pi-sessions 0.9.0 → 0.11.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 (42) hide show
  1. package/README.md +29 -20
  2. package/dist/session-messaging/broker/client-frame.js +62 -0
  3. package/dist/session-messaging/broker/process.js +234 -0
  4. package/dist/shared/session-broker/framing.js +59 -0
  5. package/dist/shared/session-broker/socket-path.js +19 -0
  6. package/extensions/pi-sessions.ts +14 -1
  7. package/extensions/session-auto-title/generate.ts +56 -66
  8. package/extensions/session-auto-title/install.ts +2 -0
  9. package/extensions/session-auto-title/runs.ts +50 -0
  10. package/extensions/session-handoff/board-view-model.ts +2 -4
  11. package/extensions/session-handoff/bootstrap.ts +14 -16
  12. package/extensions/session-handoff/extract.ts +34 -16
  13. package/extensions/session-handoff/install.ts +1 -1
  14. package/extensions/session-handoff/kickoff.ts +4 -3
  15. package/extensions/session-handoff/launch-target.ts +1 -1
  16. package/extensions/session-handoff/metadata.ts +72 -23
  17. package/extensions/session-messaging/broker/client-frame.ts +113 -0
  18. package/extensions/session-messaging/broker/process.ts +12 -21
  19. package/extensions/session-messaging/broker/spawn.ts +12 -7
  20. package/extensions/session-messaging/pi/reachable-contract.ts +59 -0
  21. package/extensions/session-messaging/pi/reachable-presenter.ts +49 -0
  22. package/extensions/session-messaging/pi/reachable-tool.ts +190 -0
  23. package/extensions/session-messaging/pi/reachable-view-model.ts +59 -0
  24. package/extensions/session-messaging/pi/renderer.ts +1 -1
  25. package/extensions/session-messaging/pi/tools.ts +8 -8
  26. package/extensions/session-search/hooks.ts +45 -52
  27. package/extensions/session-search/install.ts +3 -158
  28. package/extensions/session-search/reindex.ts +3 -3
  29. package/extensions/session-search/renderer.ts +1 -32
  30. package/extensions/session-search/tool-contract.ts +1 -21
  31. package/extensions/shared/model-runtime.ts +8 -1
  32. package/extensions/shared/session-broker/client.ts +3 -4
  33. package/extensions/shared/session-broker/framing.ts +6 -7
  34. package/extensions/shared/session-index/lineage.ts +6 -2
  35. package/extensions/shared/session-index/sqlite.ts +44 -17
  36. package/extensions/shared/settings.ts +25 -9
  37. package/extensions/shared/tmux.ts +22 -0
  38. package/extensions/subagents/classify.ts +6 -0
  39. package/extensions/subagents/install.ts +22 -137
  40. package/extensions/subagents/report-message-renderer.ts +2 -2
  41. package/extensions/subagents/settle.ts +182 -0
  42. package/package.json +18 -26
@@ -0,0 +1,182 @@
1
+ import type { ExtensionAPI, SessionEntry } from "@earendil-works/pi-coding-agent";
2
+ import { SUBAGENT_LAUNCH } from "../session-handoff/launch-target.ts";
3
+ import { findCurrentHandoffBootstrap, type HandoffSubagent } from "../session-handoff/metadata.ts";
4
+ import { hasAttachedTmuxClients, tmuxSessionName } from "../shared/tmux.ts";
5
+ import { isRunningSubagentState } from "./classify.ts";
6
+ import {
7
+ getChildSubagentLifecycle,
8
+ SUBAGENT_CLOSED_CUSTOM_TYPE,
9
+ SUBAGENT_REPORT_REMINDER_MESSAGE_CUSTOM_TYPE,
10
+ } from "./ledger.ts";
11
+ import type { ReconcileResult, SubagentReconciler } from "./reconcile.ts";
12
+
13
+ const LINGER_POLL_MS = 1_000;
14
+ const OWNED_SUBAGENT_POLL_MS = 10_000;
15
+
16
+ export interface SubagentChildSessionState {
17
+ identity: HandoffSubagent;
18
+ requestResponse: boolean;
19
+ reportsAtTurnStart: number;
20
+ }
21
+
22
+ export interface SettledChildParentSession {
23
+ epoch: number;
24
+ getBranch(): readonly SessionEntry[];
25
+ hasPendingMessages(): boolean;
26
+ isIdle(): boolean;
27
+ shutdown(): void;
28
+ }
29
+
30
+ export interface SettledChildLifecycle {
31
+ cancel(): void;
32
+ settle(
33
+ parent: SettledChildParentSession,
34
+ child: SubagentChildSessionState,
35
+ reconciliation: ReconcileResult | undefined,
36
+ ): Promise<void>;
37
+ }
38
+
39
+ export function createSettledChildLifecycle(
40
+ pi: ExtensionAPI,
41
+ reconciler: SubagentReconciler,
42
+ isCurrentSession: (epoch: number) => boolean,
43
+ ): SettledChildLifecycle {
44
+ let generation = 0;
45
+ let timer: NodeJS.Timeout | undefined;
46
+
47
+ const cancel = (): void => {
48
+ generation += 1;
49
+ if (timer) {
50
+ clearTimeout(timer);
51
+ timer = undefined;
52
+ }
53
+ };
54
+
55
+ return {
56
+ cancel,
57
+ async settle(parent, child, reconciliation) {
58
+ cancel();
59
+ const currentGeneration = generation;
60
+ let phase: "owned-subagents" | "settle" | "observer" =
61
+ reconciliation && hasRunningOwnedSubagents(reconciliation) ? "owned-subagents" : "settle";
62
+ const canAdvance = (): boolean =>
63
+ generation === currentGeneration &&
64
+ isCurrentSession(parent.epoch) &&
65
+ !parent.hasPendingMessages() &&
66
+ parent.isIdle();
67
+ const schedule = (delayMs: number): void => {
68
+ timer = setTimeout(() => {
69
+ timer = undefined;
70
+ void advance();
71
+ }, delayMs);
72
+ };
73
+ const advance = async (): Promise<void> => {
74
+ if (!canAdvance()) {
75
+ return;
76
+ }
77
+
78
+ if (phase === "owned-subagents") {
79
+ const result = await reconciler.reconcile();
80
+ if (!canAdvance()) {
81
+ return;
82
+ }
83
+ if (hasRunningOwnedSubagents(result)) {
84
+ schedule(OWNED_SUBAGENT_POLL_MS);
85
+ return;
86
+ }
87
+ phase = "settle";
88
+ }
89
+
90
+ if (phase === "settle") {
91
+ if (!settleChild(pi, child, parent.getBranch())) {
92
+ return;
93
+ }
94
+ phase = "observer";
95
+ }
96
+
97
+ let attached = false;
98
+ try {
99
+ attached = await hasAttachedTmuxClients(
100
+ pi,
101
+ tmuxSessionName(child.identity.ownerSessionId),
102
+ );
103
+ } catch {
104
+ attached = false;
105
+ }
106
+ if (!canAdvance()) {
107
+ return;
108
+ }
109
+ if (!attached) {
110
+ parent.shutdown();
111
+ return;
112
+ }
113
+ schedule(LINGER_POLL_MS);
114
+ };
115
+
116
+ if (phase === "owned-subagents") {
117
+ schedule(OWNED_SUBAGENT_POLL_MS);
118
+ return;
119
+ }
120
+ await advance();
121
+ },
122
+ };
123
+ }
124
+
125
+ export function countSubagentReports(branch: readonly SessionEntry[]): number {
126
+ return getChildSubagentLifecycle(branch).reports.length;
127
+ }
128
+
129
+ export function findSelfSubagentIdentity(
130
+ sessionId: string,
131
+ branch: readonly SessionEntry[],
132
+ ): HandoffSubagent | undefined {
133
+ const bootstrap = findCurrentHandoffBootstrap(branch);
134
+ if (
135
+ bootstrap?.launch !== SUBAGENT_LAUNCH ||
136
+ bootstrap.sessionId !== sessionId ||
137
+ bootstrap.subagent.childSessionId !== sessionId
138
+ ) {
139
+ return undefined;
140
+ }
141
+ return bootstrap.subagent;
142
+ }
143
+
144
+ function hasRunningOwnedSubagents(result: ReconcileResult): boolean {
145
+ return [...result.states.values()].some(isRunningSubagentState);
146
+ }
147
+
148
+ function settleChild(
149
+ pi: ExtensionAPI,
150
+ child: SubagentChildSessionState,
151
+ branch: readonly SessionEntry[],
152
+ ): boolean {
153
+ const lifecycle = getChildSubagentLifecycle(branch);
154
+ const reports = lifecycle.reports.length;
155
+ if (reports > child.reportsAtTurnStart) {
156
+ return true;
157
+ }
158
+ if (!child.requestResponse) {
159
+ pi.appendEntry(SUBAGENT_CLOSED_CUSTOM_TYPE, {
160
+ reason: "no_response_expected",
161
+ });
162
+ return true;
163
+ }
164
+
165
+ if (lifecycle.hasReminder) {
166
+ pi.appendEntry(SUBAGENT_CLOSED_CUSTOM_TYPE, {
167
+ reason: "no_report_after_reminder",
168
+ });
169
+ return true;
170
+ }
171
+
172
+ pi.sendMessage(
173
+ {
174
+ customType: SUBAGENT_REPORT_REMINDER_MESSAGE_CUSTOM_TYPE,
175
+ content:
176
+ "[system] Your delegated turn settled without a task report. Call submit_task_report now with done, blocked, or incomplete status.",
177
+ display: true,
178
+ },
179
+ { triggerTurn: true },
180
+ );
181
+ return false;
182
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sessions",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "description": "Pi session search, ask, handoff, messaging, auto-titling, and indexing tools",
6
6
  "license": "MIT",
@@ -19,6 +19,7 @@
19
19
  "node": ">=24 <26"
20
20
  },
21
21
  "files": [
22
+ "dist",
22
23
  "extensions",
23
24
  "images"
24
25
  ],
@@ -28,38 +29,29 @@
28
29
  ]
29
30
  },
30
31
  "scripts": {
31
- "lint": "biome check .",
32
- "format": "biome format --write .",
33
- "typecheck": "tsc --noEmit",
34
- "test": "vitest run",
35
- "check": "biome check . && tsc --noEmit && vitest run",
36
- "prepare": "husky || true"
37
- },
38
- "dependencies": {
39
- "typebox": "^1.1.38"
32
+ "build:broker": "mise run build:broker",
33
+ "lint": "mise run lint",
34
+ "format": "mise run format",
35
+ "typecheck": "mise run typecheck",
36
+ "test": "mise run test",
37
+ "check": "mise run check"
40
38
  },
41
39
  "devDependencies": {
42
40
  "@biomejs/biome": "^2.4.14",
43
- "@earendil-works/pi-agent-core": "^0.80.10",
44
- "@earendil-works/pi-ai": "^0.80.10",
45
- "@earendil-works/pi-coding-agent": "^0.80.10",
46
- "@earendil-works/pi-tui": "^0.80.10",
41
+ "@earendil-works/pi-agent-core": "^0.82.1",
42
+ "@earendil-works/pi-ai": "^0.82.1",
43
+ "@earendil-works/pi-coding-agent": "^0.82.1",
44
+ "@earendil-works/pi-tui": "^0.82.1",
47
45
  "@types/node": "^25.6.2",
48
- "husky": "^9.1.7",
49
- "lint-staged": "^17.0.3",
46
+ "strip-ansi": "^7.2.0",
47
+ "typebox": "^1.1.38",
50
48
  "typescript": "^6.0.3",
51
49
  "vitest": "^4.1.5"
52
50
  },
53
51
  "peerDependencies": {
54
- "@earendil-works/pi-agent-core": ">=0.80.10",
55
- "@earendil-works/pi-ai": ">=0.80.10",
56
- "@earendil-works/pi-coding-agent": ">=0.80.10",
57
- "@earendil-works/pi-tui": ">=0.80.10"
58
- },
59
- "lint-staged": {
60
- "*.{js,cjs,mjs,jsx,ts,tsx,json,jsonc}": "biome check --write --no-errors-on-unmatched"
61
- },
62
- "optionalDependencies": {
63
- "better-sqlite3": "^12.9.0"
52
+ "@earendil-works/pi-agent-core": ">=0.82.1",
53
+ "@earendil-works/pi-ai": ">=0.82.1",
54
+ "@earendil-works/pi-coding-agent": ">=0.82.1",
55
+ "@earendil-works/pi-tui": ">=0.82.1"
64
56
  }
65
57
  }