pi-sessions 0.5.0 → 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.
- package/README.md +19 -9
- package/extensions/session-ask.ts +6 -5
- package/extensions/session-auto-title/command.ts +1 -1
- package/extensions/session-auto-title/controller.ts +3 -3
- package/extensions/session-auto-title/generate.ts +4 -9
- package/extensions/session-auto-title/model.ts +1 -1
- package/extensions/session-auto-title/retitle.ts +5 -5
- package/extensions/session-auto-title/state.ts +1 -1
- package/extensions/session-auto-title/wizard.ts +4 -4
- package/extensions/session-auto-title.ts +8 -8
- package/extensions/session-handoff/extract.ts +18 -5
- package/extensions/session-handoff/metadata.ts +4 -1
- package/extensions/session-handoff/picker.ts +2 -2
- package/extensions/session-handoff/query.ts +6 -8
- package/extensions/session-handoff/refs.ts +2 -2
- package/extensions/session-handoff/spawn.ts +1 -1
- package/extensions/session-handoff.ts +18 -8
- package/extensions/session-hooks.ts +3 -3
- package/extensions/session-index.ts +4 -4
- package/extensions/session-messaging/broker/process.ts +305 -0
- package/extensions/session-messaging/broker/spawn.ts +159 -0
- package/extensions/session-messaging/pi/client.ts +256 -0
- package/extensions/session-messaging/pi/incoming-runtime.ts +130 -0
- package/extensions/session-messaging/pi/message-contracts.ts +37 -0
- package/extensions/session-messaging/pi/message-view.ts +120 -0
- package/extensions/session-messaging/pi/renderer.ts +16 -0
- package/extensions/session-messaging/pi/service.ts +315 -0
- package/extensions/session-messaging/pi/tools.ts +144 -0
- package/extensions/session-messaging/shared/framing.ts +72 -0
- package/extensions/session-messaging/shared/protocol.ts +102 -0
- package/extensions/session-messaging/shared/socket-path.ts +30 -0
- package/extensions/session-messaging.ts +30 -0
- package/extensions/session-search/extract.ts +4 -4
- package/extensions/session-search/hooks.ts +3 -3
- package/extensions/session-search/reindex.ts +2 -2
- package/extensions/session-search.ts +4 -4
- package/extensions/shared/session-index/common.ts +3 -3
- package/extensions/shared/session-index/index.ts +5 -5
- package/extensions/shared/session-index/lineage.ts +9 -2
- package/extensions/shared/session-index/schema.ts +3 -3
- package/extensions/shared/session-index/search.ts +4 -4
- package/extensions/shared/session-index/store.ts +2 -2
- package/extensions/shared/settings.ts +1 -1
- package/package.json +12 -11
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
## Install
|
|
22
22
|
|
|
23
|
-
Requires Pi `0.
|
|
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
|
|
55
|
-
| ------------------ |
|
|
56
|
-
| Session Search | `session_search` pi tool
|
|
57
|
-
| Session Ask | `session_ask` pi tool
|
|
58
|
-
| Session Handoff | `/handoff`, `session_handoff` pi tool
|
|
59
|
-
| Session
|
|
60
|
-
| Session
|
|
61
|
-
| Session
|
|
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.
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Message } from "@earendil-works/pi-ai";
|
|
2
|
+
import { complete } from "@earendil-works/pi-ai/compat";
|
|
2
3
|
import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
|
|
3
4
|
import { Text } from "@earendil-works/pi-tui";
|
|
4
5
|
import { Type } from "typebox";
|
|
5
|
-
import { type RenderedSessionTree, renderSessionTreeMarkdown } from "./session-search/extract.
|
|
6
|
+
import { type RenderedSessionTree, renderSessionTreeMarkdown } from "./session-search/extract.ts";
|
|
6
7
|
import {
|
|
7
8
|
getIndexStatus,
|
|
8
9
|
getSessionById,
|
|
9
10
|
INDEX_SCHEMA_VERSION,
|
|
10
11
|
openIndexDatabase,
|
|
11
12
|
type SessionLineageRow,
|
|
12
|
-
} from "./shared/session-index/index.
|
|
13
|
-
import { formatSessionTitleOrShortId, isExactSessionId } from "./shared/session-ui.
|
|
14
|
-
import { loadSettings } from "./shared/settings.
|
|
13
|
+
} from "./shared/session-index/index.ts";
|
|
14
|
+
import { formatSessionTitleOrShortId, isExactSessionId } from "./shared/session-ui.ts";
|
|
15
|
+
import { loadSettings } from "./shared/settings.ts";
|
|
15
16
|
|
|
16
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.
|
|
17
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.
|
|
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.
|
|
3
|
-
import type { AutoTitleFailure } from "./generate.
|
|
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.
|
|
10
|
+
} from "./state.ts";
|
|
11
11
|
|
|
12
12
|
export interface SessionAutoTitleStateSnapshot {
|
|
13
13
|
currentSessionFile: string | undefined;
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
completeSimple,
|
|
7
|
-
type Model,
|
|
8
|
-
type TextContent,
|
|
9
|
-
type UserMessage,
|
|
10
|
-
} from "@earendil-works/pi-ai";
|
|
4
|
+
import type { Api, Model, TextContent, UserMessage } from "@earendil-works/pi-ai";
|
|
5
|
+
import { completeSimple } from "@earendil-works/pi-ai/compat";
|
|
11
6
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
12
|
-
import type { AutoTitleContext } from "./context.
|
|
13
|
-
import type { AutoTitleTrigger } from "./state.
|
|
7
|
+
import type { AutoTitleContext } from "./context.ts";
|
|
8
|
+
import type { AutoTitleTrigger } from "./state.ts";
|
|
14
9
|
|
|
15
10
|
const AUTO_TITLE_REQUEST_TIMEOUT_MS = 15_000;
|
|
16
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.
|
|
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.
|
|
10
|
-
import { buildAutoTitleContext } from "./context.
|
|
11
|
-
import type { AutoTitleTriggerPlan, SessionAutoTitleController } from "./controller.
|
|
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.
|
|
16
|
+
} from "./generate.ts";
|
|
17
17
|
import {
|
|
18
18
|
AUTO_TITLE_STATE_CUSTOM_TYPE,
|
|
19
19
|
type AutoTitlePersistedState,
|
|
20
20
|
createAutoTitleState,
|
|
21
|
-
} from "./state.
|
|
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.
|
|
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.
|
|
11
|
-
import type { AutoRetitleStatus, SessionAutoTitleController } from "./controller.
|
|
12
|
-
import type { AutoTitleFailure } from "./generate.
|
|
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.
|
|
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.
|
|
14
|
+
} from "./session-auto-title/command.ts";
|
|
15
15
|
import {
|
|
16
16
|
createSessionAutoTitleController,
|
|
17
17
|
type SessionAutoTitleController,
|
|
18
|
-
} from "./session-auto-title/controller.
|
|
19
|
-
import { resolveAutoTitleModel } from "./session-auto-title/model.
|
|
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.
|
|
27
|
-
import { showRetitleWizard } from "./session-auto-title/wizard.
|
|
28
|
-
import { isTuiMode } from "./shared/pi-mode.
|
|
29
|
-
import { loadSettings } from "./shared/settings.
|
|
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.
|
|
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.
|
|
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(
|
|
330
|
-
|
|
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.
|
|
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.
|
|
15
|
-
import { listSessionPickerItems, normalizeDisplayText, type SessionPickerItem } from "./query.
|
|
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.
|
|
1
|
+
import { stripSearchSnippetMarkers } from "../shared/search-snippet.ts";
|
|
2
2
|
import {
|
|
3
3
|
getIndexStatus,
|
|
4
|
-
|
|
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.
|
|
13
|
-
import { shortenSessionId } from "../shared/session-ui.
|
|
14
|
-
import { formatCompactRelativeTime } from "../shared/time.
|
|
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
|
-
?
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
33
|
-
import { openSessionReferencePicker } from "./session-handoff/picker.
|
|
34
|
-
import { SESSION_TOKEN_PREFIX } from "./session-handoff/query.
|
|
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.
|
|
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.
|
|
49
|
-
import { isTuiMode } from "./shared/pi-mode.
|
|
50
|
-
import { loadSettings } from "./shared/settings.
|
|
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.
|
|
3
|
-
import { createSessionHookController } from "./session-search/hooks.
|
|
4
|
-
import { loadSettings } from "./shared/settings.
|
|
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.
|
|
9
|
-
import { isTuiMode } from "./shared/pi-mode.
|
|
10
|
-
import { getIndexStatus, type SessionIndexStatus } from "./shared/session-index/index.
|
|
11
|
-
import { loadSettings } from "./shared/settings.
|
|
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
|
|