pi-sessions 0.18.0 → 0.19.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 +44 -40
- package/extensions/hosts/contract.ts +10 -46
- package/extensions/hosts/registry.ts +4 -78
- package/extensions/pi-sessions.ts +27 -53
- package/extensions/session-handoff/board-loading.ts +4 -1
- package/extensions/session-handoff/board-view-model.ts +1 -0
- package/extensions/session-handoff/board.ts +4 -1
- package/extensions/session-handoff/install.ts +47 -33
- package/extensions/session-handoff/launch/backend.ts +16 -4
- package/extensions/session-handoff/launch-options.ts +2 -17
- package/extensions/session-handoff/launch-target.ts +4 -9
- package/extensions/session-handoff/receipt.ts +1 -1
- package/extensions/session-handoff/tool-presenter.ts +1 -1
- package/extensions/session-handoff/tool-schema.ts +1 -9
- package/extensions/session-handoff/tool-view-model.ts +6 -2
- package/extensions/session-handoff/tool.ts +2 -2
- package/extensions/session-messaging/directory-list.ts +115 -0
- package/extensions/session-messaging/directory.ts +114 -0
- package/extensions/session-messaging/message-router.ts +3 -8
- package/extensions/session-messaging/pi/directory-tools.ts +41 -0
- package/extensions/session-messaging/pi/install.ts +102 -0
- package/extensions/session-messaging/pi/message-contracts.ts +4 -6
- package/extensions/session-messaging/pi/reachable-contract.ts +3 -4
- package/extensions/session-messaging/pi/reachable-tool.ts +25 -175
- package/extensions/session-messaging/pi/send-message-presenter.ts +2 -1
- package/extensions/session-messaging/pi/tools.ts +49 -91
- package/extensions/session-search/extract.ts +12 -2
- package/extensions/shared/lineage.ts +8 -0
- package/extensions/shared/session-index/sync.ts +3 -0
- package/extensions/shared/settings.ts +1 -1
- package/extensions/subagents/install.ts +0 -27
- package/extensions/subagents/launch-target.ts +0 -1
- package/extensions/subagents/reconcile.ts +10 -2
- package/package.json +5 -5
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
type HandoffSessionMetadata,
|
|
24
24
|
parseHandoffSessionMetadata,
|
|
25
25
|
} from "../session-handoff/metadata.ts";
|
|
26
|
+
import { LINEAGE_CUSTOM_TYPE, LINEAGE_SCHEMA, type SessionLineage } from "../shared/lineage.ts";
|
|
26
27
|
import type { SessionOrigin } from "../shared/session-index/index.ts";
|
|
27
28
|
import { contentToText, isRecord } from "../shared/text.ts";
|
|
28
29
|
import { safeParseTypeBoxValue } from "../shared/typebox.ts";
|
|
@@ -97,6 +98,7 @@ export interface SessionEntryScan {
|
|
|
97
98
|
sessionNameEntryId?: string | undefined;
|
|
98
99
|
sessionNameTs?: string | undefined;
|
|
99
100
|
handoffMetadata?: DurableHandoffMetadataRecord | undefined;
|
|
101
|
+
lineage?: SessionLineage | undefined;
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
export interface SessionTailBaseline {
|
|
@@ -194,7 +196,10 @@ export function extractSessionRecord(sessionPath: string): ExtractedSessionRecor
|
|
|
194
196
|
chunks.push(...scan.chunks);
|
|
195
197
|
appendDurableHandoffMetadataChunks(chunks, scan.handoffMetadata);
|
|
196
198
|
|
|
197
|
-
const
|
|
199
|
+
const lineage = header.parentSession ? undefined : scan.lineage;
|
|
200
|
+
const parentSessionPath = normalizeParentSessionPath(
|
|
201
|
+
header.parentSession ?? lineage?.parentSessionFile,
|
|
202
|
+
);
|
|
198
203
|
const parentSession = parentSessionPath
|
|
199
204
|
? inspectParentSession(parentSessionPath, header.id, sessionPath)
|
|
200
205
|
: undefined;
|
|
@@ -211,7 +216,7 @@ export function extractSessionRecord(sessionPath: string): ExtractedSessionRecor
|
|
|
211
216
|
messageCount: scan.messageCount,
|
|
212
217
|
entryCount: scan.entryCount,
|
|
213
218
|
parentSessionPath,
|
|
214
|
-
parentSessionId: parentSession?.sessionId,
|
|
219
|
+
parentSessionId: lineage?.parentSessionId ?? parentSession?.sessionId,
|
|
215
220
|
sessionOrigin: deriveSessionOrigin(
|
|
216
221
|
parentSessionPath,
|
|
217
222
|
parentSession?.launchedSubagent ?? false,
|
|
@@ -306,6 +311,7 @@ export function scanSessionEntries(
|
|
|
306
311
|
let sessionNameEntryId: string | undefined;
|
|
307
312
|
let sessionNameTs: string | undefined;
|
|
308
313
|
let handoffMetadata: DurableHandoffMetadataRecord | undefined;
|
|
314
|
+
let lineage: SessionLineage | undefined;
|
|
309
315
|
|
|
310
316
|
for (const entry of entries) {
|
|
311
317
|
if (!isSessionEntry(entry)) continue;
|
|
@@ -336,6 +342,9 @@ export function scanSessionEntries(
|
|
|
336
342
|
continue;
|
|
337
343
|
}
|
|
338
344
|
case "custom": {
|
|
345
|
+
if (entry.customType === LINEAGE_CUSTOM_TYPE && !lineage) {
|
|
346
|
+
lineage = safeParseTypeBoxValue(LINEAGE_SCHEMA, entry.data);
|
|
347
|
+
}
|
|
339
348
|
const nextHandoffMetadata = extractHandoffMetadata(entry, entryTs);
|
|
340
349
|
if (nextHandoffMetadata && !handoffMetadata) {
|
|
341
350
|
handoffMetadata = nextHandoffMetadata;
|
|
@@ -393,6 +402,7 @@ export function scanSessionEntries(
|
|
|
393
402
|
sessionNameEntryId,
|
|
394
403
|
sessionNameTs,
|
|
395
404
|
handoffMetadata,
|
|
405
|
+
lineage,
|
|
396
406
|
};
|
|
397
407
|
}
|
|
398
408
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Static, Type } from "typebox";
|
|
2
|
+
|
|
3
|
+
export const LINEAGE_CUSTOM_TYPE = "pi-sessions.lineage";
|
|
4
|
+
export const LINEAGE_SCHEMA = Type.Object({
|
|
5
|
+
parentSessionId: Type.String({ minLength: 1 }),
|
|
6
|
+
parentSessionFile: Type.Optional(Type.String({ minLength: 1 })),
|
|
7
|
+
});
|
|
8
|
+
export type SessionLineage = Static<typeof LINEAGE_SCHEMA>;
|
|
@@ -231,6 +231,9 @@ function applyTailSync(
|
|
|
231
231
|
sessionOrigin?: SessionOrigin,
|
|
232
232
|
): boolean {
|
|
233
233
|
return db.transaction((): boolean => {
|
|
234
|
+
// A lineage entry can arrive after the initial header sync. Re-read the header to
|
|
235
|
+
// preserve its precedence and rebuild lineage through the normal full-sync path.
|
|
236
|
+
if (tail.scan.lineage) return false;
|
|
234
237
|
// Another process may have advanced the index between our baseline read
|
|
235
238
|
// and this transaction; the tail deltas would then double-count.
|
|
236
239
|
const current = getSessionRowByPath(db, baseline.sessionPath);
|
|
@@ -10,7 +10,7 @@ import { parseTypeBoxValue } from "./typebox.ts";
|
|
|
10
10
|
|
|
11
11
|
export const DEFAULT_AUTO_TITLE_REFRESH_TURNS = 4;
|
|
12
12
|
export const DEFAULT_AUTO_TITLE_TIMEOUT_SECONDS = 15;
|
|
13
|
-
export const DEFAULT_AUTO_TITLE_TOKEN_BUDGET =
|
|
13
|
+
export const DEFAULT_AUTO_TITLE_TOKEN_BUDGET = 512;
|
|
14
14
|
export const DEFAULT_AUTO_TITLE_PROMPT = `Name this coding session (under 80 chars). Be specific to what is being discussed. Your exact output will be displayed to the user, so make sure that it contains ONLY the title itself and nothing else.`;
|
|
15
15
|
const SESSION_FILE_SETTINGS_SCHEMA = Type.Object({
|
|
16
16
|
messaging: Type.Optional(
|
|
@@ -10,10 +10,6 @@ import type {
|
|
|
10
10
|
SendMessageResult,
|
|
11
11
|
} from "../session-messaging/install.ts";
|
|
12
12
|
import type { SessionWaker } from "../session-messaging/message-router.ts";
|
|
13
|
-
import {
|
|
14
|
-
createSessionCancelTool,
|
|
15
|
-
createSessionSendMessageTool,
|
|
16
|
-
} from "../session-messaging/pi/tools.ts";
|
|
17
13
|
import type { SessionLifecycle } from "../shared/composition.ts";
|
|
18
14
|
import type { CompactionThresholdSettings, SessionSettings } from "../shared/settings.ts";
|
|
19
15
|
import { isTmuxInstalled } from "../shared/tmux.ts";
|
|
@@ -82,7 +78,6 @@ export function installSubagents(
|
|
|
82
78
|
messaging: MessagingHandle;
|
|
83
79
|
readCompactionSettings: (cwd: string) => CompactionThresholdSettings;
|
|
84
80
|
sendMessage(request: SendMessageRequest): Promise<SendMessageResult>;
|
|
85
|
-
hasHostedSessions?: () => boolean;
|
|
86
81
|
},
|
|
87
82
|
): SubagentsHandle {
|
|
88
83
|
pi.registerMessageRenderer(SUBAGENT_REPORT_MESSAGE_CUSTOM_TYPE, renderSubagentReportMessage);
|
|
@@ -225,28 +220,9 @@ export function installSubagents(
|
|
|
225
220
|
}
|
|
226
221
|
};
|
|
227
222
|
|
|
228
|
-
const registerMessagingTools = (): void => {
|
|
229
|
-
const isSubagent = handle.getParentSessionId() !== undefined;
|
|
230
|
-
pi.registerTool(
|
|
231
|
-
createSessionSendMessageTool(handle, {
|
|
232
|
-
role: isSubagent ? { kind: "subagent" } : { kind: "wakeCapable" },
|
|
233
|
-
hostedSessions: deps.hasHostedSessions?.() ?? false,
|
|
234
|
-
getCachedRelationTo: deps.messaging.getCachedRelationTo,
|
|
235
|
-
getParentSessionId: () => handle.getParentSessionId(),
|
|
236
|
-
}),
|
|
237
|
-
);
|
|
238
|
-
pi.registerTool(
|
|
239
|
-
createSessionCancelTool(handle, {
|
|
240
|
-
role: isSubagent ? { kind: "subagent" } : { kind: "plain" },
|
|
241
|
-
getParentSessionId: () => handle.getParentSessionId(),
|
|
242
|
-
}),
|
|
243
|
-
);
|
|
244
|
-
};
|
|
245
|
-
|
|
246
223
|
pi.on("session_tree", async (_event, ctx) => {
|
|
247
224
|
settledChildLifecycle.cancel();
|
|
248
225
|
refreshChildSession();
|
|
249
|
-
registerMessagingTools();
|
|
250
226
|
try {
|
|
251
227
|
await reconciler.reconcileAndRestoreSuspended();
|
|
252
228
|
} catch (error) {
|
|
@@ -303,9 +279,6 @@ export function installSubagents(
|
|
|
303
279
|
reconciler.beginSession();
|
|
304
280
|
current = { parent };
|
|
305
281
|
refreshChildSession();
|
|
306
|
-
if (identity) {
|
|
307
|
-
registerMessagingTools();
|
|
308
|
-
}
|
|
309
282
|
try {
|
|
310
283
|
if (_event.reason === "reload") {
|
|
311
284
|
await reconciler.reconcile();
|
|
@@ -19,7 +19,6 @@ export function createSubagentLaunchTarget(
|
|
|
19
19
|
value: SUBAGENT_LAUNCH,
|
|
20
20
|
description:
|
|
21
21
|
"'subagent' delegates one task to a detached tmux worker and requests a report by default.",
|
|
22
|
-
requestResponseDefault: true,
|
|
23
22
|
bootstrapMode: "automatic",
|
|
24
23
|
approveProjectTrust: true,
|
|
25
24
|
describeSubagentChild(input) {
|
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
type TmuxExecutor,
|
|
9
9
|
tmuxSessionName,
|
|
10
10
|
} from "../shared/tmux.ts";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
classifySubagent,
|
|
13
|
+
isRunningSubagentState,
|
|
14
|
+
type SubagentEvidence,
|
|
15
|
+
type SubagentState,
|
|
16
|
+
} from "./classify.ts";
|
|
12
17
|
import {
|
|
13
18
|
type ChildSubagentLifecycle,
|
|
14
19
|
collectParentLedger,
|
|
@@ -220,8 +225,11 @@ export class SubagentReconciler {
|
|
|
220
225
|
continue;
|
|
221
226
|
}
|
|
222
227
|
|
|
228
|
+
// A running child writes its report to its own transcript before sending it live; recovering it in
|
|
229
|
+
// between would deliver it as recovered. Once the child stops, recovery is the report's only path.
|
|
230
|
+
const reports = isRunningSubagentState(child.state) ? [] : child.reports;
|
|
223
231
|
let reportLedger: ParentSubagentLedger | undefined;
|
|
224
|
-
for (const report of
|
|
232
|
+
for (const report of reports) {
|
|
225
233
|
if (this.hasSentReport(report.reportId)) {
|
|
226
234
|
continue;
|
|
227
235
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-sessions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pi session search, ask, handoff, messaging, auto-titling, and indexing tools",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@biomejs/biome": "^2.5.10",
|
|
41
|
-
"@earendil-works/pi-agent-core": "^1.
|
|
42
|
-
"@earendil-works/pi-ai": "^1.
|
|
43
|
-
"@earendil-works/pi-coding-agent": "^1.
|
|
44
|
-
"@earendil-works/pi-tui": "^1.
|
|
41
|
+
"@earendil-works/pi-agent-core": "^1.2.0",
|
|
42
|
+
"@earendil-works/pi-ai": "^1.2.0",
|
|
43
|
+
"@earendil-works/pi-coding-agent": "^1.2.0",
|
|
44
|
+
"@earendil-works/pi-tui": "^1.2.0",
|
|
45
45
|
"@types/node": "^24.13.3",
|
|
46
46
|
"strip-ansi": "^7.2.0",
|
|
47
47
|
"typebox": "1.3.27",
|