pi-sessions 0.5.1 → 0.6.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 (44) hide show
  1. package/README.md +19 -9
  2. package/extensions/session-ask.ts +4 -4
  3. package/extensions/session-auto-title/command.ts +1 -1
  4. package/extensions/session-auto-title/controller.ts +3 -3
  5. package/extensions/session-auto-title/generate.ts +2 -2
  6. package/extensions/session-auto-title/model.ts +1 -1
  7. package/extensions/session-auto-title/retitle.ts +5 -5
  8. package/extensions/session-auto-title/state.ts +1 -1
  9. package/extensions/session-auto-title/wizard.ts +4 -4
  10. package/extensions/session-auto-title.ts +8 -8
  11. package/extensions/session-handoff/extract.ts +18 -5
  12. package/extensions/session-handoff/metadata.ts +4 -1
  13. package/extensions/session-handoff/picker.ts +2 -2
  14. package/extensions/session-handoff/query.ts +6 -8
  15. package/extensions/session-handoff/refs.ts +2 -2
  16. package/extensions/session-handoff/spawn.ts +1 -1
  17. package/extensions/session-handoff.ts +18 -8
  18. package/extensions/session-hooks.ts +3 -3
  19. package/extensions/session-index.ts +4 -4
  20. package/extensions/session-messaging/broker/process.ts +305 -0
  21. package/extensions/session-messaging/broker/spawn.ts +159 -0
  22. package/extensions/session-messaging/pi/client.ts +256 -0
  23. package/extensions/session-messaging/pi/incoming-runtime.ts +130 -0
  24. package/extensions/session-messaging/pi/message-contracts.ts +37 -0
  25. package/extensions/session-messaging/pi/message-view.ts +120 -0
  26. package/extensions/session-messaging/pi/renderer.ts +16 -0
  27. package/extensions/session-messaging/pi/service.ts +315 -0
  28. package/extensions/session-messaging/pi/tools.ts +144 -0
  29. package/extensions/session-messaging/shared/framing.ts +72 -0
  30. package/extensions/session-messaging/shared/protocol.ts +102 -0
  31. package/extensions/session-messaging/shared/socket-path.ts +30 -0
  32. package/extensions/session-messaging.ts +30 -0
  33. package/extensions/session-search/extract.ts +4 -4
  34. package/extensions/session-search/hooks.ts +3 -3
  35. package/extensions/session-search/reindex.ts +2 -2
  36. package/extensions/session-search.ts +4 -4
  37. package/extensions/shared/session-index/common.ts +3 -3
  38. package/extensions/shared/session-index/index.ts +5 -5
  39. package/extensions/shared/session-index/lineage.ts +9 -2
  40. package/extensions/shared/session-index/schema.ts +3 -3
  41. package/extensions/shared/session-index/search.ts +4 -4
  42. package/extensions/shared/session-index/store.ts +2 -2
  43. package/extensions/shared/settings.ts +1 -1
  44. package/package.json +4 -3
package/README.md CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  ## Install
22
22
 
23
- Requires Pi `0.80.2` or newer.
23
+ Requires Pi `0.80.2` or newer and Node `>=24 <26`.
24
24
 
25
25
  **From npm** (recommended):
26
26
 
@@ -51,14 +51,15 @@ What session did I implement the db layer?
51
51
 
52
52
  ## Features
53
53
 
54
- | Extension | Surface | What it does |
55
- | ------------------ | ------------------------------------- | ------------------------------------------------------- |
56
- | Session Search | `session_search` pi tool | Search through old sessions |
57
- | Session Ask | `session_ask` pi tool | Ask questions about old sessions |
58
- | Session Handoff | `/handoff`, `session_handoff` pi tool | Start a focused new session; alternative to compaction |
59
- | Session Picker | `Alt+O` | Reference old sessions in your prompt |
60
- | Session Index | `/session-index` slash command | Shows index status and rebuilds the local session index |
61
- | Session Auto Title | in background, `/title` slash command | Give sessions titles |
54
+ | Extension | Surface | What it does |
55
+ | ------------------ | ---------------------------------------------------- | ------------------------------------------------------- |
56
+ | Session Search | `session_search` pi tool | Search through old sessions |
57
+ | Session Ask | `session_ask` pi tool | Ask questions about old sessions |
58
+ | Session Handoff | `/handoff`, `session_handoff` pi tool | Start a focused new session; alternative to compaction |
59
+ | Session Messaging | `session_list_live`, `session_send_message` pi tools | Send messages between running Pi sessions |
60
+ | Session Picker | `Alt+O` | Reference old sessions in your prompt |
61
+ | Session Index | `/session-index` slash command | Shows index status and rebuilds the local session index |
62
+ | Session Auto Title | in background, `/title` slash command | Give sessions titles |
62
63
 
63
64
  ## Session Handoff
64
65
 
@@ -86,6 +87,15 @@ When running in Ghostty on macOS, pi-sessions also exposes a `session_handoff` t
86
87
 
87
88
  If background handoffs ever target the wrong pane, run `/handoff --identify` from the intended source pane to refresh the in-memory Ghostty terminal binding.
88
89
 
90
+ ## Session Messaging
91
+
92
+ Agents can coordinate with other currently running Pi sessions:
93
+
94
+ - `session_list_live` lists other live sessions
95
+ - `session_send_message` sends a message to a live session
96
+
97
+ Incoming messages start the recipient agent when idle and steer it when already running. Inactive sessions cannot receive messages, but you can still use `session_search` and `session_ask` with them.
98
+
89
99
  ## Session picker
90
100
 
91
101
  Directly reference prior sessions by looking them up by contents.
@@ -3,16 +3,16 @@ import { complete } from "@earendil-works/pi-ai/compat";
3
3
  import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
4
4
  import { Text } from "@earendil-works/pi-tui";
5
5
  import { Type } from "typebox";
6
- import { type RenderedSessionTree, renderSessionTreeMarkdown } from "./session-search/extract.js";
6
+ import { type RenderedSessionTree, renderSessionTreeMarkdown } from "./session-search/extract.ts";
7
7
  import {
8
8
  getIndexStatus,
9
9
  getSessionById,
10
10
  INDEX_SCHEMA_VERSION,
11
11
  openIndexDatabase,
12
12
  type SessionLineageRow,
13
- } from "./shared/session-index/index.js";
14
- import { formatSessionTitleOrShortId, isExactSessionId } from "./shared/session-ui.js";
15
- import { loadSettings } from "./shared/settings.js";
13
+ } from "./shared/session-index/index.ts";
14
+ import { formatSessionTitleOrShortId, isExactSessionId } from "./shared/session-ui.ts";
15
+ import { loadSettings } from "./shared/settings.ts";
16
16
 
17
17
  const SESSION_ASK_SYSTEM_PROMPT = `You are analyzing a Pi coding session transcript. The transcript includes the entire session tree, including abandoned branches and summaries.
18
18
 
@@ -1,6 +1,6 @@
1
1
  import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
2
  import type { AutocompleteItem } from "@earendil-works/pi-tui";
3
- import { isTuiMode } from "../shared/pi-mode.js";
3
+ import { isTuiMode } from "../shared/pi-mode.ts";
4
4
 
5
5
  export const TITLE_USAGE = "Usage: /title [this|folder|pi] [-f]";
6
6
 
@@ -1,13 +1,13 @@
1
1
  import { buildSessionContext, type ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import type { AutoTitleSettings } from "../shared/settings.js";
3
- import type { AutoTitleFailure } from "./generate.js";
2
+ import type { AutoTitleSettings } from "../shared/settings.ts";
3
+ import type { AutoTitleFailure } from "./generate.ts";
4
4
  import {
5
5
  type AutoTitleMode,
6
6
  type AutoTitlePersistedState,
7
7
  type AutoTitleTrigger,
8
8
  createAutoTitleState,
9
9
  getLatestAutoTitleState,
10
- } from "./state.js";
10
+ } from "./state.ts";
11
11
 
12
12
  export interface SessionAutoTitleStateSnapshot {
13
13
  currentSessionFile: string | undefined;
@@ -4,8 +4,8 @@ import { join } from "node:path";
4
4
  import type { Api, Model, TextContent, UserMessage } from "@earendil-works/pi-ai";
5
5
  import { completeSimple } from "@earendil-works/pi-ai/compat";
6
6
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
7
- import type { AutoTitleContext } from "./context.js";
8
- import type { AutoTitleTrigger } from "./state.js";
7
+ import type { AutoTitleContext } from "./context.ts";
8
+ import type { AutoTitleTrigger } from "./state.ts";
9
9
 
10
10
  const AUTO_TITLE_REQUEST_TIMEOUT_MS = 15_000;
11
11
  const AUTO_TITLE_MAX_TOKENS = 64;
@@ -1,6 +1,6 @@
1
1
  import type { Api, Model } from "@earendil-works/pi-ai";
2
2
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
- import { ModelReference } from "../shared/settings.js";
3
+ import { ModelReference } from "../shared/settings.ts";
4
4
 
5
5
  const DEFAULT_AUTO_TITLE_FALLBACK_MODELS: readonly ModelReference[] = [
6
6
  new ModelReference("google", "gemini-flash-lite-latest"),
@@ -6,19 +6,19 @@ import type {
6
6
  SessionInfo,
7
7
  } from "@earendil-works/pi-coding-agent";
8
8
  import { SessionManager } from "@earendil-works/pi-coding-agent";
9
- import type { RetitleMode, RetitleScope } from "./command.js";
10
- import { buildAutoTitleContext } from "./context.js";
11
- import type { AutoTitleTriggerPlan, SessionAutoTitleController } from "./controller.js";
9
+ import type { RetitleMode, RetitleScope } from "./command.ts";
10
+ import { buildAutoTitleContext } from "./context.ts";
11
+ import type { AutoTitleTriggerPlan, SessionAutoTitleController } from "./controller.ts";
12
12
  import {
13
13
  type AutoTitleGenerationResult,
14
14
  createAutoTitleFailure,
15
15
  generateAutoTitle,
16
- } from "./generate.js";
16
+ } from "./generate.ts";
17
17
  import {
18
18
  AUTO_TITLE_STATE_CUSTOM_TYPE,
19
19
  type AutoTitlePersistedState,
20
20
  createAutoTitleState,
21
- } from "./state.js";
21
+ } from "./state.ts";
22
22
 
23
23
  export interface RetitleScopeScan {
24
24
  scope: Exclude<RetitleScope, "this">;
@@ -1,6 +1,6 @@
1
1
  import type { SessionEntry } from "@earendil-works/pi-coding-agent";
2
2
  import { type Static, Type } from "typebox";
3
- import { safeParseTypeBoxValue } from "../shared/typebox.js";
3
+ import { safeParseTypeBoxValue } from "../shared/typebox.ts";
4
4
 
5
5
  export const AUTO_TITLE_STATE_CUSTOM_TYPE = "pi-sessions.auto-title";
6
6
  export const AUTO_TITLE_STATE_VERSION = 1;
@@ -7,9 +7,9 @@ import {
7
7
  } from "@earendil-works/pi-coding-agent";
8
8
  import type { Focusable, TUI } from "@earendil-works/pi-tui";
9
9
  import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
10
- import type { RetitleCommandOutcome, RetitleMode, RetitleScope } from "./command.js";
11
- import type { AutoRetitleStatus, SessionAutoTitleController } from "./controller.js";
12
- import type { AutoTitleFailure } from "./generate.js";
10
+ import type { RetitleCommandOutcome, RetitleMode, RetitleScope } from "./command.ts";
11
+ import type { AutoRetitleStatus, SessionAutoTitleController } from "./controller.ts";
12
+ import type { AutoTitleFailure } from "./generate.ts";
13
13
  import {
14
14
  buildBulkRetitleMessage,
15
15
  buildRetitleScopeScan,
@@ -20,7 +20,7 @@ import {
20
20
  type RetitleScopeScan,
21
21
  runBulkRetitle,
22
22
  runRetitlePlan,
23
- } from "./retitle.js";
23
+ } from "./retitle.ts";
24
24
 
25
25
  interface RetitleWizardOptions {
26
26
  initialInvocation?: {
@@ -11,29 +11,29 @@ import {
11
11
  getRetitleArgumentCompletions,
12
12
  type RetitleCommandInvocation,
13
13
  type RetitleCommandOutcome,
14
- } from "./session-auto-title/command.js";
14
+ } from "./session-auto-title/command.ts";
15
15
  import {
16
16
  createSessionAutoTitleController,
17
17
  type SessionAutoTitleController,
18
- } from "./session-auto-title/controller.js";
19
- import { resolveAutoTitleModel } from "./session-auto-title/model.js";
18
+ } from "./session-auto-title/controller.ts";
19
+ import { resolveAutoTitleModel } from "./session-auto-title/model.ts";
20
20
  import {
21
21
  buildRetitleScopeScan,
22
22
  notifyBulkRetitleResult,
23
23
  persistAutoTitleState,
24
24
  runBulkRetitle,
25
25
  runRetitlePlan,
26
- } from "./session-auto-title/retitle.js";
27
- import { showRetitleWizard } from "./session-auto-title/wizard.js";
28
- import { isTuiMode } from "./shared/pi-mode.js";
29
- import { loadSettings } from "./shared/settings.js";
26
+ } from "./session-auto-title/retitle.ts";
27
+ import { showRetitleWizard } from "./session-auto-title/wizard.ts";
28
+ import { isTuiMode } from "./shared/pi-mode.ts";
29
+ import { loadSettings } from "./shared/settings.ts";
30
30
 
31
31
  export {
32
32
  createSessionAutoTitleCommandHandler,
33
33
  getRetitleArgumentCompletions,
34
34
  parseRetitleCommand,
35
35
  TITLE_USAGE,
36
- } from "./session-auto-title/command.js";
36
+ } from "./session-auto-title/command.ts";
37
37
 
38
38
  interface TitleRunState {
39
39
  controller: SessionAutoTitleController;
@@ -19,7 +19,7 @@ import {
19
19
  serializeConversation,
20
20
  } from "@earendil-works/pi-coding-agent";
21
21
  import { type Static, Type } from "typebox";
22
- import { parseTypeBoxValue } from "../shared/typebox.js";
22
+ import { parseTypeBoxValue } from "../shared/typebox.ts";
23
23
 
24
24
  const MAX_RELEVANT_FILES = 12;
25
25
  const MAX_OPEN_QUESTIONS = 8;
@@ -88,6 +88,7 @@ export async function generateHandoffDraft(
88
88
  goal: string,
89
89
  thinkingLevel: ThinkingLevel | undefined,
90
90
  signal?: AbortSignal,
91
+ requestResponse = false,
91
92
  ): Promise<HandoffDraftResult | undefined> {
92
93
  if (!ctx.model) {
93
94
  throw new Error("No model is available for handoff.");
@@ -99,6 +100,7 @@ export async function generateHandoffDraft(
99
100
  goal,
100
101
  thinkingLevel,
101
102
  signal,
103
+ requestResponse,
102
104
  );
103
105
  }
104
106
 
@@ -108,6 +110,7 @@ export async function generateHandoffDraftFromSessionManager(
108
110
  goal: string,
109
111
  thinkingLevel: ThinkingLevel | undefined,
110
112
  signal?: AbortSignal,
113
+ requestResponse = false,
111
114
  ): Promise<HandoffDraftResult | undefined> {
112
115
  if (!ctx.model) {
113
116
  throw new Error("No model is available for handoff.");
@@ -139,7 +142,7 @@ export async function generateHandoffDraftFromSessionManager(
139
142
  const sessionPath = sourceSessionManager.getSessionFile();
140
143
 
141
144
  return {
142
- draft: assembleHandoffDraft(sessionId, sessionPath, handoffContext, goal),
145
+ draft: assembleHandoffDraft(sessionId, sessionPath, handoffContext, goal, requestResponse),
143
146
  context: handoffContext,
144
147
  sessionId,
145
148
  sessionPath,
@@ -241,8 +244,9 @@ export function assembleHandoffDraft(
241
244
  sessionPath: string | undefined,
242
245
  handoffContext: HandoffContext,
243
246
  goal: string,
247
+ requestResponse = false,
244
248
  ): string {
245
- const sections = [buildContinuityLine(sessionId, sessionPath)];
249
+ const sections = [buildContinuityLine(sessionId, sessionPath, requestResponse)];
246
250
  const nextTask = handoffContext.nextTask.trim() || goal.trim();
247
251
 
248
252
  if (nextTask) {
@@ -326,8 +330,17 @@ function extractHandoffContextFromArguments(
326
330
  };
327
331
  }
328
332
 
329
- function buildContinuityLine(sessionId: string, _sessionPath: string | undefined): string {
330
- return `Continuing work from session ${sessionId}. When you lack specific information you can use session_ask.`;
333
+ function buildContinuityLine(
334
+ sessionId: string,
335
+ _sessionPath: string | undefined,
336
+ requestResponse: boolean,
337
+ ): string {
338
+ const base = `Continuing work from session ${sessionId}. When you lack specific information you can use session_ask.`;
339
+ if (!requestResponse) {
340
+ return base;
341
+ }
342
+
343
+ return `${base} When this work is complete, send that session a completion report with session_send_message.`;
331
344
  }
332
345
 
333
346
  function normalizeStringArray(value: unknown, limit: number): string[] {
@@ -1,7 +1,7 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import type { CustomEntry, SessionEntry } from "@earendil-works/pi-coding-agent";
3
3
  import { type Static, Type } from "typebox";
4
- import { safeParseTypeBoxValue } from "../shared/typebox.js";
4
+ import { safeParseTypeBoxValue } from "../shared/typebox.ts";
5
5
 
6
6
  export const HANDOFF_METADATA_CUSTOM_TYPE = "pi-sessions.handoff";
7
7
  export const HANDOFF_BOOTSTRAP_ENV = "PI_SESSIONS_HANDOFF_BOOTSTRAP";
@@ -30,6 +30,7 @@ export const CHILD_GENERATED_HANDOFF_BOOTSTRAP_SCHEMA = Type.Object({
30
30
  goal: Type.String(),
31
31
  title: Type.String(),
32
32
  parentSessionFile: Type.String(),
33
+ requestResponse: Type.Optional(Type.Boolean()),
33
34
  });
34
35
 
35
36
  export const HANDOFF_BOOTSTRAP_SCHEMA = Type.Union([
@@ -80,6 +81,7 @@ export function createChildGeneratedHandoffBootstrap(options: {
80
81
  goal: string;
81
82
  title: string;
82
83
  parentSessionFile: string;
84
+ requestResponse?: boolean | undefined;
83
85
  }): ChildGeneratedHandoffBootstrap {
84
86
  return {
85
87
  mode: "generate",
@@ -87,6 +89,7 @@ export function createChildGeneratedHandoffBootstrap(options: {
87
89
  goal: options.goal.trim(),
88
90
  title: options.title.trim(),
89
91
  parentSessionFile: options.parentSessionFile,
92
+ ...(options.requestResponse === undefined ? {} : { requestResponse: options.requestResponse }),
90
93
  };
91
94
  }
92
95
 
@@ -11,8 +11,8 @@ import {
11
11
  import {
12
12
  stripSearchSnippetMarkers,
13
13
  transformSearchSnippetMatches,
14
- } from "../shared/search-snippet.js";
15
- import { listSessionPickerItems, normalizeDisplayText, type SessionPickerItem } from "./query.js";
14
+ } from "../shared/search-snippet.ts";
15
+ import { listSessionPickerItems, normalizeDisplayText, type SessionPickerItem } from "./query.ts";
16
16
 
17
17
  const MAX_VISIBLE_BROWSE_ROWS = 10;
18
18
  const MAX_VISIBLE_SEARCH_ROWS = 4;
@@ -1,7 +1,7 @@
1
- import { stripSearchSnippetMarkers } from "../shared/search-snippet.js";
1
+ import { stripSearchSnippetMarkers } from "../shared/search-snippet.ts";
2
2
  import {
3
3
  getIndexStatus,
4
- getLineageSessions,
4
+ getLineageRelationMap,
5
5
  getSessionByPath,
6
6
  INDEX_SCHEMA_VERSION,
7
7
  openIndexDatabase,
@@ -9,9 +9,9 @@ import {
9
9
  type SessionIndexDatabase,
10
10
  type SessionLineageRelation,
11
11
  searchSessions,
12
- } from "../shared/session-index/index.js";
13
- import { shortenSessionId } from "../shared/session-ui.js";
14
- import { formatCompactRelativeTime } from "../shared/time.js";
12
+ } from "../shared/session-index/index.ts";
13
+ import { shortenSessionId } from "../shared/session-ui.ts";
14
+ import { formatCompactRelativeTime } from "../shared/time.ts";
15
15
 
16
16
  export const SESSION_TOKEN_PREFIX = "@session:";
17
17
 
@@ -127,9 +127,7 @@ function buildPresentationContext(
127
127
  return {
128
128
  currentSessionId,
129
129
  relationBySessionId: currentSessionId
130
- ? new Map(
131
- getLineageSessions(db, currentSessionId).map((row) => [row.sessionId, row.relation]),
132
- )
130
+ ? getLineageRelationMap(db, currentSessionId)
133
131
  : new Map<string, SessionLineageRelation>(),
134
132
  };
135
133
  }
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { parseSessionFile } from "../session-search/extract.js";
2
+ import { parseSessionFile } from "../session-search/extract.ts";
3
3
  import {
4
4
  getIndexStatus,
5
5
  getSessionById,
@@ -7,7 +7,7 @@ import {
7
7
  openIndexDatabase,
8
8
  type SessionLineageRow,
9
9
  type SessionOrigin,
10
- } from "../shared/session-index/index.js";
10
+ } from "../shared/session-index/index.ts";
11
11
 
12
12
  const HANDOFF_REF_PREFIX = "@handoff/";
13
13
  const SESSION_REFERENCE_HELP =
@@ -8,7 +8,7 @@ import {
8
8
  type SessionHeader,
9
9
  type SessionInfoEntry,
10
10
  } from "@earendil-works/pi-coding-agent";
11
- import { HANDOFF_BOOTSTRAP_ENV } from "./metadata.js";
11
+ import { HANDOFF_BOOTSTRAP_ENV } from "./metadata.ts";
12
12
 
13
13
  const GHOSTTY_MACOS_ONLY_MESSAGE = "Split handoff currently supports Ghostty on macOS only.";
14
14
  const GHOSTTY_REQUIRED_MESSAGE = "Split handoff requires running inside Ghostty.";
@@ -15,7 +15,7 @@ import {
15
15
  generateHandoffDraft,
16
16
  generateHandoffDraftFromSessionManager,
17
17
  type HandoffDraftResult,
18
- } from "./session-handoff/extract.js";
18
+ } from "./session-handoff/extract.ts";
19
19
  import {
20
20
  type ChildGeneratedHandoffBootstrap,
21
21
  createChildGeneratedHandoffBootstrap,
@@ -29,14 +29,14 @@ import {
29
29
  hasUserMessages,
30
30
  isChildGeneratedHandoffBootstrap,
31
31
  parseHandoffBootstrap,
32
- } from "./session-handoff/metadata.js";
33
- import { openSessionReferencePicker } from "./session-handoff/picker.js";
34
- import { SESSION_TOKEN_PREFIX } from "./session-handoff/query.js";
32
+ } from "./session-handoff/metadata.ts";
33
+ import { openSessionReferencePicker } from "./session-handoff/picker.ts";
34
+ import { SESSION_TOKEN_PREFIX } from "./session-handoff/query.ts";
35
35
  import {
36
36
  renderStrongModal,
37
37
  reviewHandoffDraft,
38
38
  reviewHandoffDraftForSend,
39
- } from "./session-handoff/review.js";
39
+ } from "./session-handoff/review.ts";
40
40
  import {
41
41
  buildPiResumeCommand,
42
42
  createHandoffSession,
@@ -45,9 +45,9 @@ import {
45
45
  isGhosttyHandoffAvailable,
46
46
  launchSplitHandoffSession,
47
47
  validateSplitHandoffPrerequisites,
48
- } from "./session-handoff/spawn.js";
49
- import { isTuiMode } from "./shared/pi-mode.js";
50
- import { loadSettings } from "./shared/settings.js";
48
+ } from "./session-handoff/spawn.ts";
49
+ import { isTuiMode } from "./shared/pi-mode.ts";
50
+ import { loadSettings } from "./shared/settings.ts";
51
51
 
52
52
  const HANDOFF_USAGE = "Usage: /handoff [--left|--right|--up|--down] <goal for new thread>";
53
53
  const TOOL_HANDOFF_PROVISIONAL_TITLE = "Session handoff";
@@ -58,6 +58,7 @@ interface HandoffToolParams {
58
58
  goal: string;
59
59
  splitDirection: HandoffSplitDirection;
60
60
  cwd?: string | undefined;
61
+ requestResponse?: boolean | undefined;
61
62
  }
62
63
 
63
64
  interface HandoffToolDetails {
@@ -110,6 +111,12 @@ export default function sessionHandoffExtension(pi: ExtensionAPI): void {
110
111
  "Optional target working directory. Relative paths resolve from the current session cwd.",
111
112
  }),
112
113
  ),
114
+ requestResponse: Type.Optional(
115
+ Type.Boolean({
116
+ description:
117
+ "Whether the child session should report completion/results of its task back to this session.",
118
+ }),
119
+ ),
113
120
  }),
114
121
  async execute(_toolCallId, params: HandoffToolParams, _signal, _onUpdate, ctx) {
115
122
  return executeSessionHandoffTool(pi, params, ctx, identifiedGhosttyTerminalId);
@@ -388,6 +395,7 @@ async function executeSessionHandoffTool(
388
395
  return createHandoffToolError("No conversation to hand off.");
389
396
  }
390
397
 
398
+ const requestResponse = params.requestResponse ?? false;
391
399
  const createdSession = createHandoffSession({
392
400
  cwd: targetCwd.path,
393
401
  sessionDir: ctx.sessionManager.getSessionDir(),
@@ -400,6 +408,7 @@ async function executeSessionHandoffTool(
400
408
  goal,
401
409
  title: TOOL_HANDOFF_PROVISIONAL_TITLE,
402
410
  parentSessionFile,
411
+ requestResponse,
403
412
  }),
404
413
  );
405
414
  const model = formatModelArgument(ctx.model, pi.getThinkingLevel());
@@ -482,6 +491,7 @@ async function startChildGeneratedHandoff(
482
491
  bootstrap.goal,
483
492
  thinkingLevel,
484
493
  signal,
494
+ bootstrap.requestResponse ?? false,
485
495
  ),
486
496
  );
487
497
  if (!generatedDraft) {
@@ -1,7 +1,7 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { HANDOFF_BOOTSTRAP_ENV, parseHandoffBootstrap } from "./session-handoff/metadata.js";
3
- import { createSessionHookController } from "./session-search/hooks.js";
4
- import { loadSettings } from "./shared/settings.js";
2
+ import { HANDOFF_BOOTSTRAP_ENV, parseHandoffBootstrap } from "./session-handoff/metadata.ts";
3
+ import { createSessionHookController } from "./session-search/hooks.ts";
4
+ import { loadSettings } from "./shared/settings.ts";
5
5
 
6
6
  interface SessionStartLifecycleEvent {
7
7
  reason?: "startup" | "reload" | "new" | "resume" | "fork";
@@ -5,10 +5,10 @@ import {
5
5
  type Theme,
6
6
  } from "@earendil-works/pi-coding-agent";
7
7
  import { type Focusable, matchesKey, visibleWidth } from "@earendil-works/pi-tui";
8
- import { type ReindexResult, rebuildSessionIndex } from "./session-search/reindex.js";
9
- import { isTuiMode } from "./shared/pi-mode.js";
10
- import { getIndexStatus, type SessionIndexStatus } from "./shared/session-index/index.js";
11
- import { loadSettings } from "./shared/settings.js";
8
+ import { type ReindexResult, rebuildSessionIndex } from "./session-search/reindex.ts";
9
+ import { isTuiMode } from "./shared/pi-mode.ts";
10
+ import { getIndexStatus, type SessionIndexStatus } from "./shared/session-index/index.ts";
11
+ import { loadSettings } from "./shared/settings.ts";
12
12
 
13
13
  type SessionIndexAction = "reindex" | undefined;
14
14