pi-sessions 0.7.2 → 0.9.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 (104) hide show
  1. package/README.md +71 -37
  2. package/extensions/pi-sessions.ts +125 -0
  3. package/extensions/session-ask/agent.ts +34 -14
  4. package/extensions/{session-ask.ts → session-ask/install.ts} +73 -90
  5. package/extensions/session-ask/navigate.ts +3 -1
  6. package/extensions/session-ask/presenter.ts +23 -0
  7. package/extensions/session-ask/renderer.ts +63 -0
  8. package/extensions/session-ask/tool-contract.ts +27 -0
  9. package/extensions/session-ask/view-model.ts +18 -0
  10. package/extensions/session-auto-title/generate.ts +18 -18
  11. package/extensions/{session-auto-title.ts → session-auto-title/install.ts} +67 -46
  12. package/extensions/session-auto-title/model.ts +25 -17
  13. package/extensions/session-auto-title/retitle.ts +26 -12
  14. package/extensions/session-auto-title/wizard.ts +12 -6
  15. package/extensions/session-handoff/board-view-model.ts +181 -0
  16. package/extensions/session-handoff/board.ts +663 -0
  17. package/extensions/session-handoff/bootstrap.ts +174 -0
  18. package/extensions/session-handoff/extract.ts +101 -122
  19. package/extensions/session-handoff/install.ts +225 -0
  20. package/extensions/session-handoff/kickoff.ts +92 -0
  21. package/extensions/session-handoff/launch/backend.ts +16 -0
  22. package/extensions/session-handoff/launch/deferred.ts +20 -0
  23. package/extensions/session-handoff/launch/ghostty.ts +99 -0
  24. package/extensions/session-handoff/launch/resolution.ts +57 -0
  25. package/extensions/session-handoff/launch/shell.ts +7 -0
  26. package/extensions/session-handoff/launch/tmux.ts +31 -0
  27. package/extensions/session-handoff/launch-options.ts +61 -0
  28. package/extensions/session-handoff/launch-target.ts +91 -0
  29. package/extensions/session-handoff/metadata.ts +152 -63
  30. package/extensions/session-handoff/model.ts +58 -0
  31. package/extensions/session-handoff/query.ts +0 -1
  32. package/extensions/session-handoff/receipt.ts +96 -0
  33. package/extensions/session-handoff/review.ts +2 -28
  34. package/extensions/session-handoff/spawn.ts +76 -181
  35. package/extensions/session-handoff/strong-modal.ts +13 -0
  36. package/extensions/session-handoff/tool-contract.ts +12 -0
  37. package/extensions/session-handoff/tool-presenter.ts +35 -0
  38. package/extensions/session-handoff/tool-renderer.ts +66 -0
  39. package/extensions/session-handoff/tool-schema.ts +54 -0
  40. package/extensions/session-handoff/tool-view-model.ts +49 -0
  41. package/extensions/session-handoff/tool.ts +206 -0
  42. package/extensions/session-handoff/ui.ts +66 -0
  43. package/extensions/session-hooks/install.ts +75 -0
  44. package/extensions/{session-index.ts → session-index/install.ts} +9 -7
  45. package/extensions/session-messaging/broker/process.ts +21 -28
  46. package/extensions/session-messaging/install.ts +78 -0
  47. package/extensions/session-messaging/pi/cancel-session-presenter.ts +64 -0
  48. package/extensions/session-messaging/pi/incoming-message-presenter.ts +32 -0
  49. package/extensions/session-messaging/pi/incoming-message-view-model.ts +20 -0
  50. package/extensions/session-messaging/pi/incoming-runtime.ts +27 -23
  51. package/extensions/session-messaging/pi/message-contracts.ts +32 -0
  52. package/extensions/session-messaging/pi/renderer.ts +19 -12
  53. package/extensions/session-messaging/pi/send-message-presenter.ts +39 -0
  54. package/extensions/session-messaging/pi/send-message-view-model.ts +54 -0
  55. package/extensions/session-messaging/pi/service.ts +262 -81
  56. package/extensions/session-messaging/pi/tools.ts +184 -40
  57. package/extensions/session-search/extract.ts +60 -14
  58. package/extensions/session-search/hooks.ts +10 -13
  59. package/extensions/session-search/install.ts +373 -0
  60. package/extensions/session-search/renderer.ts +195 -0
  61. package/extensions/session-search/tool-contract.ts +51 -0
  62. package/extensions/shared/composition.ts +20 -0
  63. package/extensions/shared/errors.ts +3 -0
  64. package/extensions/shared/model-resolution.ts +73 -0
  65. package/extensions/shared/model-runtime.ts +33 -0
  66. package/extensions/shared/model.ts +7 -10
  67. package/extensions/shared/rendering/collapsible-text.ts +89 -0
  68. package/extensions/shared/rendering/expandable-content-layout.ts +84 -0
  69. package/extensions/shared/rendering/theme.ts +5 -0
  70. package/extensions/shared/session-broker/client.ts +30 -29
  71. package/extensions/shared/session-broker/protocol.ts +128 -10
  72. package/extensions/shared/session-index/common.ts +5 -5
  73. package/extensions/shared/session-index/lineage.ts +0 -3
  74. package/extensions/shared/session-index/schema.ts +0 -1
  75. package/extensions/shared/session-index/scoring.ts +1 -2
  76. package/extensions/shared/session-index/search.ts +11 -4
  77. package/extensions/shared/session-index/store.ts +4 -9
  78. package/extensions/shared/settings.ts +80 -38
  79. package/extensions/shared/text.ts +5 -0
  80. package/extensions/shared/thinking-levels.ts +22 -0
  81. package/extensions/shared/tmux.ts +205 -0
  82. package/extensions/subagents/cancel.ts +111 -0
  83. package/extensions/subagents/classify.ts +52 -0
  84. package/extensions/subagents/install.ts +344 -0
  85. package/extensions/subagents/launch-target.ts +75 -0
  86. package/extensions/subagents/ledger.ts +217 -0
  87. package/extensions/subagents/reconcile.ts +386 -0
  88. package/extensions/subagents/report-message-presenter.ts +36 -0
  89. package/extensions/subagents/report-message-renderer.ts +21 -0
  90. package/extensions/subagents/report-message-view-model.ts +29 -0
  91. package/extensions/subagents/report.ts +159 -0
  92. package/extensions/subagents/roster.ts +262 -0
  93. package/extensions/subagents/wake.ts +173 -0
  94. package/images/handoff-board-subagents.png +0 -0
  95. package/images/handoff-board-user-sessions.png +0 -0
  96. package/images/handoff-subagent-report.png +0 -0
  97. package/images/session-handoff-tool.png +0 -0
  98. package/package.json +10 -16
  99. package/extensions/session-handoff.ts +0 -741
  100. package/extensions/session-hooks.ts +0 -75
  101. package/extensions/session-messaging/pi/message-view.ts +0 -131
  102. package/extensions/session-messaging.ts +0 -30
  103. package/extensions/session-search.ts +0 -419
  104. package/extensions/shared/session-broker/active.ts +0 -26
@@ -3,13 +3,14 @@ import {
3
3
  copyToClipboard,
4
4
  type ExtensionAPI,
5
5
  type ExtensionCommandContext,
6
+ type ModelRuntime,
6
7
  type Theme,
7
8
  } from "@earendil-works/pi-coding-agent";
8
9
  import type { Focusable, TUI } from "@earendil-works/pi-tui";
9
10
  import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
10
11
  import type { RetitleCommandOutcome, RetitleMode, RetitleScope } from "./command.ts";
11
12
  import type { AutoRetitleStatus, SessionAutoTitleController } from "./controller.ts";
12
- import type { AutoTitleFailure } from "./generate.ts";
13
+ import type { AutoTitleFailure, AutoTitleGeneration } from "./generate.ts";
13
14
  import {
14
15
  buildBulkRetitleMessage,
15
16
  buildRetitleScopeScan,
@@ -54,9 +55,10 @@ export async function showRetitleWizard(
54
55
  pi: ExtensionAPI,
55
56
  controller: SessionAutoTitleController,
56
57
  ctx: ExtensionCommandContext,
58
+ modelRuntime: ModelRuntime,
57
59
  model: Model<Api> | undefined,
58
60
  getSessionEpoch: () => number,
59
- systemPrompt: string,
61
+ generation: AutoTitleGeneration,
60
62
  options?: RetitleWizardOptions,
61
63
  ): Promise<RetitleCommandOutcome> {
62
64
  return ctx.ui.custom<RetitleCommandOutcome>(
@@ -67,9 +69,10 @@ export async function showRetitleWizard(
67
69
  pi,
68
70
  controller,
69
71
  ctx,
72
+ modelRuntime,
70
73
  model,
71
74
  getSessionEpoch,
72
- systemPrompt,
75
+ generation,
73
76
  done,
74
77
  options,
75
78
  ),
@@ -96,9 +99,10 @@ class RetitleWizardPanel implements Focusable {
96
99
  private readonly pi: ExtensionAPI,
97
100
  private readonly controller: SessionAutoTitleController,
98
101
  private readonly ctx: ExtensionCommandContext,
102
+ private readonly modelRuntime: ModelRuntime,
99
103
  private readonly model: Model<Api> | undefined,
100
104
  private readonly getSessionEpoch: () => number,
101
- private readonly systemPrompt: string,
105
+ private readonly generation: AutoTitleGeneration,
102
106
  private readonly done: (result: RetitleCommandOutcome) => void,
103
107
  options?: RetitleWizardOptions,
104
108
  ) {
@@ -247,9 +251,10 @@ class RetitleWizardPanel implements Focusable {
247
251
  pi: this.pi,
248
252
  controller: this.controller,
249
253
  ctx: this.ctx,
254
+ modelRuntime: this.modelRuntime,
250
255
  model: this.model,
251
256
  isManual: true,
252
- systemPrompt: this.systemPrompt,
257
+ generation: this.generation,
253
258
  getSessionEpoch: this.getSessionEpoch,
254
259
  });
255
260
  if (result.ok) {
@@ -305,11 +310,12 @@ class RetitleWizardPanel implements Focusable {
305
310
  this.pi,
306
311
  this.controller,
307
312
  this.ctx,
313
+ this.modelRuntime,
308
314
  this.model,
309
315
  scan,
310
316
  mode,
311
317
  this.getSessionEpoch,
312
- this.systemPrompt,
318
+ this.generation,
313
319
  );
314
320
  notifyBulkRetitleResult(this.ctx, scan, mode, result);
315
321
  this.done(
@@ -0,0 +1,181 @@
1
+ import { tmuxSessionName } from "../shared/tmux.ts";
2
+ import type { SubagentState } from "../subagents/classify.ts";
3
+ import type { SubagentRosterEntry } from "../subagents/roster.ts";
4
+ import { shellQuote } from "./launch/shell.ts";
5
+ import type { HandoffLaunchReceipt } from "./receipt.ts";
6
+
7
+ const RUNNING_SUBAGENT_STATES: ReadonlySet<SubagentState> = new Set(["starting", "busy", "active"]);
8
+
9
+ export type HandoffBoardTab = "subagents" | "user-sessions";
10
+ export type UserSessionStatus = "live" | "ready" | "starting" | "closed" | "unknown";
11
+
12
+ export interface UserSessionRunEvidence {
13
+ transcriptAvailable: boolean;
14
+ hasStarted: boolean;
15
+ }
16
+
17
+ export interface UserSessionEntry {
18
+ sessionId: string;
19
+ timestamp: string;
20
+ receipt: HandoffLaunchReceipt;
21
+ runEvidence?: UserSessionRunEvidence | undefined;
22
+ }
23
+
24
+ export interface HandoffBoardSnapshot {
25
+ subagents: readonly SubagentRosterEntry[];
26
+ userSessions: readonly UserSessionEntry[];
27
+ liveSessionIds: ReadonlySet<string>;
28
+ hasLiveSessionEvidence: boolean;
29
+ }
30
+
31
+ export interface HandoffBoardDetail {
32
+ label: string;
33
+ value: string;
34
+ }
35
+
36
+ export interface HandoffBoardAction {
37
+ subagent?: SubagentRosterEntry | undefined;
38
+ canStop: boolean;
39
+ observeCommand?: string | undefined;
40
+ resumeCommand?: string | undefined;
41
+ }
42
+
43
+ export interface HandoffSubagentRow {
44
+ kind: "subagent";
45
+ title: string;
46
+ depth: number;
47
+ status: SubagentState;
48
+ timestamp: string;
49
+ }
50
+
51
+ export interface UserSessionRow {
52
+ kind: "user-session";
53
+ title: string;
54
+ status: UserSessionStatus;
55
+ timestamp: string;
56
+ }
57
+
58
+ export interface HandoffSubagentsView {
59
+ tab: "subagents";
60
+ rows: readonly HandoffSubagentRow[];
61
+ details: readonly HandoffBoardDetail[];
62
+ action?: HandoffBoardAction | undefined;
63
+ }
64
+
65
+ export interface UserSessionsView {
66
+ tab: "user-sessions";
67
+ rows: readonly UserSessionRow[];
68
+ details: readonly HandoffBoardDetail[];
69
+ action?: HandoffBoardAction | undefined;
70
+ }
71
+
72
+ export type HandoffBoardView = HandoffSubagentsView | UserSessionsView;
73
+
74
+ export function buildHandoffBoardView(
75
+ snapshot: HandoffBoardSnapshot,
76
+ tab: HandoffBoardTab,
77
+ selectedIndex: number,
78
+ options: { insideTmux: boolean },
79
+ ): HandoffBoardView {
80
+ if (tab === "subagents") {
81
+ const selected = snapshot.subagents[selectedIndex];
82
+ return {
83
+ tab,
84
+ rows: snapshot.subagents.map((subagent) => ({
85
+ kind: "subagent",
86
+ title: subagent.title,
87
+ depth: subagent.depth,
88
+ status: subagent.state,
89
+ timestamp: subagent.launchedAt,
90
+ })),
91
+ details: selected
92
+ ? [
93
+ { label: "Session", value: selected.sessionId },
94
+ { label: "Model", value: selected.model ?? "Default" },
95
+ {
96
+ label: "Owner",
97
+ value: selected.ownerIsCurrentSession ? "this session" : selected.ownerTitle,
98
+ },
99
+ { label: "Goal", value: selected.goal },
100
+ ]
101
+ : [],
102
+ ...(selected ? { action: buildSubagentAction(selected, options.insideTmux) } : {}),
103
+ };
104
+ }
105
+
106
+ const rows = snapshot.userSessions.map(
107
+ (entry): UserSessionRow => ({
108
+ kind: "user-session",
109
+ title: entry.receipt.title,
110
+ status: getUserSessionStatus(snapshot, entry),
111
+ timestamp: entry.timestamp,
112
+ }),
113
+ );
114
+ const selected = snapshot.userSessions[selectedIndex];
115
+ if (!selected) {
116
+ return { tab, rows, details: [] };
117
+ }
118
+
119
+ const details: HandoffBoardDetail[] = [
120
+ { label: "Session", value: selected.sessionId },
121
+ { label: "Model", value: selected.receipt.model },
122
+ { label: "Launch", value: selected.receipt.launch },
123
+ ];
124
+ if (selected.receipt.backend) {
125
+ details.push({ label: "Backend", value: selected.receipt.backend });
126
+ }
127
+ if (selected.receipt.cwd) {
128
+ details.push({ label: "Directory", value: selected.receipt.cwd });
129
+ }
130
+ const action = buildUserSessionAction(snapshot, selected);
131
+ return {
132
+ tab,
133
+ rows,
134
+ details,
135
+ ...(action ? { action } : {}),
136
+ };
137
+ }
138
+
139
+ function getUserSessionStatus(
140
+ snapshot: HandoffBoardSnapshot,
141
+ entry: UserSessionEntry,
142
+ ): UserSessionStatus {
143
+ if (snapshot.liveSessionIds.has(entry.sessionId)) {
144
+ return "live";
145
+ }
146
+ const evidence = entry.runEvidence;
147
+ if (!evidence?.transcriptAvailable) {
148
+ return "unknown";
149
+ }
150
+ if (evidence.hasStarted) {
151
+ return snapshot.hasLiveSessionEvidence ? "closed" : "unknown";
152
+ }
153
+ return entry.receipt.launch === "deferred" ? "ready" : "starting";
154
+ }
155
+
156
+ function buildUserSessionAction(
157
+ snapshot: HandoffBoardSnapshot,
158
+ entry: UserSessionEntry,
159
+ ): HandoffBoardAction | undefined {
160
+ if (!snapshot.hasLiveSessionEvidence || snapshot.liveSessionIds.has(entry.sessionId)) {
161
+ return undefined;
162
+ }
163
+ return {
164
+ canStop: false,
165
+ resumeCommand: entry.receipt.resumeCommand,
166
+ };
167
+ }
168
+
169
+ function buildSubagentAction(entry: SubagentRosterEntry, insideTmux: boolean): HandoffBoardAction {
170
+ return {
171
+ subagent: entry,
172
+ canStop: RUNNING_SUBAGENT_STATES.has(entry.state),
173
+ ...(entry.tmuxWindowId ? { observeCommand: buildObserveCommand(entry, insideTmux) } : {}),
174
+ ...(!entry.managedLive ? { resumeCommand: entry.resumeCommand } : {}),
175
+ };
176
+ }
177
+
178
+ function buildObserveCommand(entry: SubagentRosterEntry, insideTmux: boolean): string {
179
+ const target = shellQuote(`${tmuxSessionName(entry.ownerSessionId)}:${entry.tmuxWindowId}`);
180
+ return insideTmux ? `tmux switch-client -t ${target}` : `tmux attach-session -t ${target}`;
181
+ }