pi-sessions 0.8.0 → 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.
- package/README.md +70 -37
- package/extensions/pi-sessions.ts +125 -0
- package/extensions/session-ask/agent.ts +34 -14
- package/extensions/{session-ask.ts → session-ask/install.ts} +73 -90
- package/extensions/session-ask/navigate.ts +3 -1
- package/extensions/session-ask/presenter.ts +23 -0
- package/extensions/session-ask/renderer.ts +63 -0
- package/extensions/session-ask/tool-contract.ts +27 -0
- package/extensions/session-ask/view-model.ts +18 -0
- package/extensions/session-auto-title/generate.ts +18 -18
- package/extensions/{session-auto-title.ts → session-auto-title/install.ts} +60 -53
- package/extensions/session-auto-title/model.ts +25 -17
- package/extensions/session-auto-title/retitle.ts +17 -18
- package/extensions/session-auto-title/wizard.ts +12 -11
- package/extensions/session-handoff/board-view-model.ts +181 -0
- package/extensions/session-handoff/board.ts +663 -0
- package/extensions/session-handoff/bootstrap.ts +174 -0
- package/extensions/session-handoff/extract.ts +101 -122
- package/extensions/session-handoff/install.ts +225 -0
- package/extensions/session-handoff/kickoff.ts +92 -0
- package/extensions/session-handoff/launch/backend.ts +16 -0
- package/extensions/session-handoff/launch/deferred.ts +20 -0
- package/extensions/session-handoff/launch/ghostty.ts +99 -0
- package/extensions/session-handoff/launch/resolution.ts +57 -0
- package/extensions/session-handoff/launch/shell.ts +7 -0
- package/extensions/session-handoff/launch/tmux.ts +31 -0
- package/extensions/session-handoff/launch-options.ts +61 -0
- package/extensions/session-handoff/launch-target.ts +91 -0
- package/extensions/session-handoff/metadata.ts +152 -63
- package/extensions/session-handoff/model.ts +58 -0
- package/extensions/session-handoff/query.ts +0 -1
- package/extensions/session-handoff/receipt.ts +96 -0
- package/extensions/session-handoff/review.ts +2 -28
- package/extensions/session-handoff/spawn.ts +76 -181
- package/extensions/session-handoff/strong-modal.ts +13 -0
- package/extensions/session-handoff/tool-contract.ts +12 -0
- package/extensions/session-handoff/tool-presenter.ts +35 -0
- package/extensions/session-handoff/tool-renderer.ts +66 -0
- package/extensions/session-handoff/tool-schema.ts +54 -0
- package/extensions/session-handoff/tool-view-model.ts +49 -0
- package/extensions/session-handoff/tool.ts +206 -0
- package/extensions/session-handoff/ui.ts +66 -0
- package/extensions/session-hooks/install.ts +75 -0
- package/extensions/{session-index.ts → session-index/install.ts} +9 -7
- package/extensions/session-messaging/broker/process.ts +21 -28
- package/extensions/session-messaging/install.ts +78 -0
- package/extensions/session-messaging/pi/cancel-session-presenter.ts +64 -0
- package/extensions/session-messaging/pi/incoming-message-presenter.ts +32 -0
- package/extensions/session-messaging/pi/incoming-message-view-model.ts +20 -0
- package/extensions/session-messaging/pi/incoming-runtime.ts +27 -23
- package/extensions/session-messaging/pi/message-contracts.ts +32 -0
- package/extensions/session-messaging/pi/renderer.ts +19 -12
- package/extensions/session-messaging/pi/send-message-presenter.ts +39 -0
- package/extensions/session-messaging/pi/send-message-view-model.ts +54 -0
- package/extensions/session-messaging/pi/service.ts +262 -81
- package/extensions/session-messaging/pi/tools.ts +184 -40
- package/extensions/session-search/extract.ts +60 -14
- package/extensions/session-search/hooks.ts +10 -13
- package/extensions/session-search/install.ts +373 -0
- package/extensions/session-search/renderer.ts +195 -0
- package/extensions/session-search/tool-contract.ts +51 -0
- package/extensions/shared/composition.ts +20 -0
- package/extensions/shared/errors.ts +3 -0
- package/extensions/shared/model-resolution.ts +73 -0
- package/extensions/shared/model-runtime.ts +33 -0
- package/extensions/shared/model.ts +7 -10
- package/extensions/shared/rendering/collapsible-text.ts +89 -0
- package/extensions/shared/rendering/expandable-content-layout.ts +84 -0
- package/extensions/shared/rendering/theme.ts +5 -0
- package/extensions/shared/session-broker/client.ts +30 -29
- package/extensions/shared/session-broker/protocol.ts +128 -10
- package/extensions/shared/session-index/common.ts +5 -5
- package/extensions/shared/session-index/lineage.ts +0 -3
- package/extensions/shared/session-index/schema.ts +0 -1
- package/extensions/shared/session-index/scoring.ts +1 -2
- package/extensions/shared/session-index/search.ts +11 -4
- package/extensions/shared/session-index/store.ts +4 -9
- package/extensions/shared/settings.ts +76 -38
- package/extensions/shared/text.ts +5 -0
- package/extensions/shared/thinking-levels.ts +22 -0
- package/extensions/shared/tmux.ts +205 -0
- package/extensions/subagents/cancel.ts +111 -0
- package/extensions/subagents/classify.ts +52 -0
- package/extensions/subagents/install.ts +344 -0
- package/extensions/subagents/launch-target.ts +75 -0
- package/extensions/subagents/ledger.ts +217 -0
- package/extensions/subagents/reconcile.ts +386 -0
- package/extensions/subagents/report-message-presenter.ts +36 -0
- package/extensions/subagents/report-message-renderer.ts +21 -0
- package/extensions/subagents/report-message-view-model.ts +29 -0
- package/extensions/subagents/report.ts +159 -0
- package/extensions/subagents/roster.ts +262 -0
- package/extensions/subagents/wake.ts +173 -0
- package/images/handoff-board-subagents.png +0 -0
- package/images/handoff-board-user-sessions.png +0 -0
- package/images/handoff-subagent-report.png +0 -0
- package/images/session-handoff-tool.png +0 -0
- package/package.json +10 -16
- package/extensions/session-handoff.ts +0 -741
- package/extensions/session-hooks.ts +0 -75
- package/extensions/session-messaging/pi/message-view.ts +0 -131
- package/extensions/session-messaging.ts +0 -30
- package/extensions/session-search.ts +0 -419
- package/extensions/shared/session-broker/active.ts +0 -26
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ExpandableContentPresentation } from "../../shared/rendering/expandable-content-layout.ts";
|
|
2
|
+
import type { RenderTheme } from "../../shared/rendering/theme.ts";
|
|
3
|
+
import type { IncomingMessageViewModel } from "./incoming-message-view-model.ts";
|
|
4
|
+
|
|
5
|
+
export function buildIncomingMessagePresentation(
|
|
6
|
+
model: IncomingMessageViewModel,
|
|
7
|
+
theme: RenderTheme,
|
|
8
|
+
): ExpandableContentPresentation {
|
|
9
|
+
const source = model.sourceSessionName
|
|
10
|
+
? `${model.sourceSessionName} (${model.sourceSessionId})`
|
|
11
|
+
: model.sourceSessionId;
|
|
12
|
+
const metadata = [
|
|
13
|
+
model.relation,
|
|
14
|
+
model.requestResponse ? "response requested" : undefined,
|
|
15
|
+
].filter((item): item is string => Boolean(item));
|
|
16
|
+
const metadataHint = metadata.length > 0 ? ` (${metadata.join(", ")})` : "";
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
header: `${theme.fg("toolTitle", theme.bold("incoming_message"))} ${theme.fg(
|
|
20
|
+
"muted",
|
|
21
|
+
`from ${source}${metadataHint}`,
|
|
22
|
+
)}`,
|
|
23
|
+
...(model.body
|
|
24
|
+
? {
|
|
25
|
+
body: {
|
|
26
|
+
text: theme.fg("toolOutput", model.body.replaceAll("\t", " ")),
|
|
27
|
+
collapsedRows: 3,
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
: {}),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { normalizeOptionalText } from "../../shared/text.ts";
|
|
2
|
+
import type { ReceivedMessageEntry } from "./message-contracts.ts";
|
|
3
|
+
|
|
4
|
+
export interface IncomingMessageViewModel {
|
|
5
|
+
sourceSessionId: string;
|
|
6
|
+
sourceSessionName?: string | undefined;
|
|
7
|
+
relation?: string | undefined;
|
|
8
|
+
requestResponse: boolean;
|
|
9
|
+
body: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function buildIncomingMessageView(received: ReceivedMessageEntry): IncomingMessageViewModel {
|
|
13
|
+
return {
|
|
14
|
+
sourceSessionId: received.source.sessionId,
|
|
15
|
+
sourceSessionName: normalizeOptionalText(received.source.sessionName),
|
|
16
|
+
relation: received.relation,
|
|
17
|
+
requestResponse: received.requestResponse === true,
|
|
18
|
+
body: received.body,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { isSessionStarting, SESSION_STARTING_MESSAGE } from "../../session-handoff/metadata.ts";
|
|
2
3
|
import { safeParseTypeBoxValue } from "../../shared/typebox.ts";
|
|
3
4
|
import { RECEIVED_MESSAGE_ENTRY_SCHEMA, type ReceivedMessageEntry } from "./message-contracts.ts";
|
|
4
5
|
|
|
5
6
|
export const MESSAGE_RECEIVED_CUSTOM_TYPE = "pi-sessions.message_received";
|
|
6
7
|
export const SESSION_MESSAGE_CUSTOM_TYPE = "pi-sessions.session_message";
|
|
7
8
|
|
|
8
|
-
type DeliveryResult = { delivered: true } | { delivered: false; error: string };
|
|
9
|
-
|
|
10
9
|
interface IncomingMessageActions {
|
|
11
10
|
appendEntry(customType: string, data: unknown): void;
|
|
12
11
|
sendMessage(
|
|
@@ -29,28 +28,30 @@ export class IncomingSessionMessageRuntime {
|
|
|
29
28
|
appendEntry: pi.appendEntry.bind(pi),
|
|
30
29
|
sendMessage: pi.sendMessage.bind(pi),
|
|
31
30
|
};
|
|
31
|
+
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
pi.on("session_shutdown", () => {
|
|
37
|
-
this.context = undefined;
|
|
38
|
-
});
|
|
33
|
+
bindContext(ctx: ExtensionContext): void {
|
|
34
|
+
this.context = ctx;
|
|
39
35
|
}
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return { delivered: false, error: "Target session is not ready." };
|
|
45
|
-
}
|
|
37
|
+
clearContext(): void {
|
|
38
|
+
this.context = undefined;
|
|
39
|
+
}
|
|
46
40
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
deliver(received: ReceivedMessageEntry): void {
|
|
42
|
+
const ctx = this.requireContext();
|
|
43
|
+
// A starting session is broker-reachable but has not consumed its handoff
|
|
44
|
+
// kickoff. Injecting here would land the message before the kickoff and
|
|
45
|
+
// break the first-message invariant, so refuse delivery until it kicks off.
|
|
46
|
+
if (isSessionStarting(ctx.sessionManager.getBranch())) {
|
|
47
|
+
throw new Error(SESSION_STARTING_MESSAGE);
|
|
53
48
|
}
|
|
49
|
+
this.actions.appendEntry(MESSAGE_RECEIVED_CUSTOM_TYPE, received);
|
|
50
|
+
this.inject(ctx, received);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
cancel(): void {
|
|
54
|
+
this.requireContext().abort();
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
replayPending(ctx: ExtensionContext): void {
|
|
@@ -84,6 +85,13 @@ export class IncomingSessionMessageRuntime {
|
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
private requireContext(): ExtensionContext {
|
|
89
|
+
if (!this.context) {
|
|
90
|
+
throw new Error("Target session is not ready.");
|
|
91
|
+
}
|
|
92
|
+
return this.context;
|
|
93
|
+
}
|
|
94
|
+
|
|
87
95
|
private inject(ctx: ExtensionContext, received: ReceivedMessageEntry): void {
|
|
88
96
|
const deliveryOptions = ctx.isIdle()
|
|
89
97
|
? { triggerTurn: true as const }
|
|
@@ -108,7 +116,3 @@ function formatIncomingMessageForModel(received: ReceivedMessageEntry): string {
|
|
|
108
116
|
const responseLine = received.requestResponse ? "\n\nResponse requested." : "";
|
|
109
117
|
return `Incoming message from session${titlePart} (session: ${received.source.sessionId}${relationPart}):\n\n${received.body}${responseLine}`;
|
|
110
118
|
}
|
|
111
|
-
|
|
112
|
-
function formatError(error: unknown): string {
|
|
113
|
-
return error instanceof Error && error.message.trim() ? error.message : String(error);
|
|
114
|
-
}
|
|
@@ -19,6 +19,35 @@ export const SEND_MESSAGE_PARAMS = Type.Object({
|
|
|
19
19
|
),
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
export const SEND_MESSAGE_TOOL_DETAILS_SCHEMA = Type.Object({
|
|
23
|
+
delivered: Type.Literal(true),
|
|
24
|
+
messageId: Type.String(),
|
|
25
|
+
target: RECEIVED_MESSAGE_ENDPOINT_SCHEMA,
|
|
26
|
+
relation: Type.Optional(Type.String()),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const CANCEL_SESSION_PARAMS = Type.Object({
|
|
30
|
+
session: Type.String({ description: "Bare UUID for the live session to cancel." }),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const CANCEL_SESSION_TOOL_DETAILS_SCHEMA = Type.Union([
|
|
34
|
+
Type.Object({
|
|
35
|
+
kind: Type.Literal("transport"),
|
|
36
|
+
delivered: Type.Literal(true),
|
|
37
|
+
cancelId: Type.String(),
|
|
38
|
+
target: RECEIVED_MESSAGE_ENDPOINT_SCHEMA,
|
|
39
|
+
relation: Type.Optional(Type.String()),
|
|
40
|
+
}),
|
|
41
|
+
Type.Object({
|
|
42
|
+
kind: Type.Literal("managed"),
|
|
43
|
+
accepted: Type.Literal(true),
|
|
44
|
+
target: RECEIVED_MESSAGE_ENDPOINT_SCHEMA,
|
|
45
|
+
state: Type.String(),
|
|
46
|
+
cancelId: Type.Optional(Type.String()),
|
|
47
|
+
relation: Type.Optional(Type.String()),
|
|
48
|
+
}),
|
|
49
|
+
]);
|
|
50
|
+
|
|
22
51
|
export const RECEIVED_MESSAGE_ENTRY_SCHEMA = Type.Object({
|
|
23
52
|
messageId: Type.String(),
|
|
24
53
|
source: RECEIVED_MESSAGE_ENDPOINT_SCHEMA,
|
|
@@ -32,5 +61,8 @@ export const RECEIVED_MESSAGE_ENTRY_SCHEMA = Type.Object({
|
|
|
32
61
|
});
|
|
33
62
|
|
|
34
63
|
export type SendMessageParams = Static<typeof SEND_MESSAGE_PARAMS>;
|
|
64
|
+
export type SendMessageToolDetails = Static<typeof SEND_MESSAGE_TOOL_DETAILS_SCHEMA>;
|
|
65
|
+
export type CancelSessionParams = Static<typeof CANCEL_SESSION_PARAMS>;
|
|
66
|
+
export type CancelSessionToolDetails = Static<typeof CANCEL_SESSION_TOOL_DETAILS_SCHEMA>;
|
|
35
67
|
export type ReceivedMessageEndpoint = Static<typeof RECEIVED_MESSAGE_ENDPOINT_SCHEMA>;
|
|
36
68
|
export type ReceivedMessageEntry = Static<typeof RECEIVED_MESSAGE_ENTRY_SCHEMA>;
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MessageRenderer } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Box } from "@earendil-works/pi-tui";
|
|
3
|
+
import { ExpandableContentLayout } from "../../shared/rendering/expandable-content-layout.ts";
|
|
2
4
|
import { safeParseTypeBoxValue } from "../../shared/typebox.ts";
|
|
3
|
-
import {
|
|
5
|
+
import { buildIncomingMessagePresentation } from "./incoming-message-presenter.ts";
|
|
6
|
+
import { buildIncomingMessageView } from "./incoming-message-view-model.ts";
|
|
4
7
|
import { RECEIVED_MESSAGE_ENTRY_SCHEMA } from "./message-contracts.ts";
|
|
5
|
-
import { createReceivedSessionMessageComponent } from "./message-view.ts";
|
|
6
8
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
9
|
+
export const renderIncomingSessionMessage: MessageRenderer = (message, options, theme) => {
|
|
10
|
+
const details = safeParseTypeBoxValue(RECEIVED_MESSAGE_ENTRY_SCHEMA, message.details);
|
|
11
|
+
if (!details) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const layout = new ExpandableContentLayout(theme);
|
|
16
|
+
layout.update(
|
|
17
|
+
buildIncomingMessagePresentation(buildIncomingMessageView(details), theme),
|
|
18
|
+
options.expanded,
|
|
19
|
+
);
|
|
20
|
+
const box = new Box(1, 1, (text) => theme.bg("toolSuccessBg", text));
|
|
21
|
+
box.addChild(layout);
|
|
22
|
+
return box;
|
|
23
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ExpandableContentPresentation } from "../../shared/rendering/expandable-content-layout.ts";
|
|
2
|
+
import type { RenderTheme } from "../../shared/rendering/theme.ts";
|
|
3
|
+
import type { SendMessageViewModel } from "./send-message-view-model.ts";
|
|
4
|
+
|
|
5
|
+
export function buildSendMessagePresentation(
|
|
6
|
+
model: SendMessageViewModel,
|
|
7
|
+
theme: RenderTheme,
|
|
8
|
+
): ExpandableContentPresentation {
|
|
9
|
+
const target =
|
|
10
|
+
model.status === "delivered"
|
|
11
|
+
? (model.targetSessionName ?? model.targetSessionId)
|
|
12
|
+
: (model.targetSessionId ?? "[target pending]");
|
|
13
|
+
const metadata = [
|
|
14
|
+
model.relation,
|
|
15
|
+
model.requestResponse ? "response requested" : undefined,
|
|
16
|
+
].filter((item): item is string => Boolean(item));
|
|
17
|
+
const metadataHint = metadata.length > 0 ? ` (${metadata.join(", ")})` : "";
|
|
18
|
+
const action = model.status === "delivered" ? "delivered to" : "to";
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
header: `${theme.fg("toolTitle", theme.bold("session_send_message"))} ${theme.fg(
|
|
22
|
+
"muted",
|
|
23
|
+
`${action} ${target}${metadataHint}`,
|
|
24
|
+
)}`,
|
|
25
|
+
...(model.status === "delivered"
|
|
26
|
+
? {
|
|
27
|
+
expandedMetadata: [`${theme.bold("session")} ${model.targetSessionId}`],
|
|
28
|
+
}
|
|
29
|
+
: {}),
|
|
30
|
+
...(model.body
|
|
31
|
+
? {
|
|
32
|
+
body: {
|
|
33
|
+
text: theme.fg("toolOutput", model.body.replaceAll("\t", " ")),
|
|
34
|
+
collapsedRows: 3,
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
: {}),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { normalizeOptionalText } from "../../shared/text.ts";
|
|
2
|
+
import type { SendMessageToolDetails } from "./message-contracts.ts";
|
|
3
|
+
|
|
4
|
+
export interface SendMessageRenderArgs {
|
|
5
|
+
session?: string | undefined;
|
|
6
|
+
message?: string | undefined;
|
|
7
|
+
requestResponse?: boolean | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SendingMessageViewModel {
|
|
11
|
+
status: "sending";
|
|
12
|
+
targetSessionId?: string | undefined;
|
|
13
|
+
relation?: string | undefined;
|
|
14
|
+
requestResponse: boolean;
|
|
15
|
+
body: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DeliveredMessageViewModel {
|
|
19
|
+
status: "delivered";
|
|
20
|
+
targetSessionId: string;
|
|
21
|
+
targetSessionName?: string | undefined;
|
|
22
|
+
relation?: string | undefined;
|
|
23
|
+
requestResponse: boolean;
|
|
24
|
+
body: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type SendMessageViewModel = SendingMessageViewModel | DeliveredMessageViewModel;
|
|
28
|
+
|
|
29
|
+
export function buildSendingMessageView(
|
|
30
|
+
args: SendMessageRenderArgs | undefined,
|
|
31
|
+
relation: string | undefined,
|
|
32
|
+
): SendingMessageViewModel {
|
|
33
|
+
return {
|
|
34
|
+
status: "sending",
|
|
35
|
+
targetSessionId: normalizeOptionalText(args?.session),
|
|
36
|
+
relation,
|
|
37
|
+
requestResponse: args?.requestResponse === true,
|
|
38
|
+
body: args?.message ?? "",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function buildDeliveredMessageView(
|
|
43
|
+
args: SendMessageRenderArgs | undefined,
|
|
44
|
+
details: SendMessageToolDetails,
|
|
45
|
+
): DeliveredMessageViewModel {
|
|
46
|
+
return {
|
|
47
|
+
status: "delivered",
|
|
48
|
+
targetSessionId: details.target.sessionId,
|
|
49
|
+
targetSessionName: normalizeOptionalText(details.target.sessionName),
|
|
50
|
+
relation: details.relation,
|
|
51
|
+
requestResponse: args?.requestResponse === true,
|
|
52
|
+
body: args?.message?.trim() ?? "",
|
|
53
|
+
};
|
|
54
|
+
}
|