pi-sessions 0.7.2 → 0.8.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 +1 -0
- package/extensions/session-auto-title/generate.ts +2 -2
- package/extensions/session-auto-title/retitle.ts +18 -3
- package/extensions/session-auto-title/wizard.ts +5 -0
- package/extensions/session-auto-title.ts +15 -1
- package/extensions/shared/settings.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -190,6 +190,7 @@ To change auto-titling settings, edit `~/.pi/agent/settings.json`:
|
|
|
190
190
|
"sessions": {
|
|
191
191
|
"autoTitle": {
|
|
192
192
|
"refreshTurns": 4,
|
|
193
|
+
"timeoutSecs": 15,
|
|
193
194
|
"model": "anthropic/claude-haiku-4-5",
|
|
194
195
|
"prompt": "Custom prompt that overrides the default."
|
|
195
196
|
}
|
|
@@ -7,7 +7,6 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
|
7
7
|
import type { AutoTitleContext } from "./context.ts";
|
|
8
8
|
import type { AutoTitleTrigger } from "./state.ts";
|
|
9
9
|
|
|
10
|
-
const AUTO_TITLE_REQUEST_TIMEOUT_MS = 15_000;
|
|
11
10
|
const AUTO_TITLE_MAX_TOKENS = 64;
|
|
12
11
|
const AUTO_TITLE_CHAR_MAX = 80;
|
|
13
12
|
|
|
@@ -34,6 +33,7 @@ export async function generateAutoTitle(
|
|
|
34
33
|
context: AutoTitleContext,
|
|
35
34
|
trigger: AutoTitleTrigger,
|
|
36
35
|
systemPrompt: string,
|
|
36
|
+
timeoutMs: number,
|
|
37
37
|
): Promise<AutoTitleGenerationResult> {
|
|
38
38
|
if (!context.conversationText) {
|
|
39
39
|
return {
|
|
@@ -64,7 +64,7 @@ export async function generateAutoTitle(
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
const abortController = new AbortController();
|
|
67
|
-
const timeoutId = setTimeout(() => abortController.abort(),
|
|
67
|
+
const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
|
|
68
68
|
|
|
69
69
|
try {
|
|
70
70
|
const requestContext = {
|
|
@@ -58,6 +58,7 @@ export async function runBulkRetitle(
|
|
|
58
58
|
mode: RetitleMode,
|
|
59
59
|
getSessionEpoch: () => number,
|
|
60
60
|
systemPrompt: string,
|
|
61
|
+
timeoutMs: number,
|
|
61
62
|
): Promise<BulkRetitleResult> {
|
|
62
63
|
const result: BulkRetitleResult = {
|
|
63
64
|
attempted: 0,
|
|
@@ -85,6 +86,7 @@ export async function runBulkRetitle(
|
|
|
85
86
|
model,
|
|
86
87
|
isManual: true,
|
|
87
88
|
systemPrompt,
|
|
89
|
+
timeoutMs,
|
|
88
90
|
getSessionEpoch,
|
|
89
91
|
notifyOnSuccess: false,
|
|
90
92
|
});
|
|
@@ -92,7 +94,7 @@ export async function runBulkRetitle(
|
|
|
92
94
|
continue;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
const outcome = await retitleStoredSession(ctx, model, session.path, systemPrompt);
|
|
97
|
+
const outcome = await retitleStoredSession(ctx, model, session.path, systemPrompt, timeoutMs);
|
|
96
98
|
result[outcome] += 1;
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -165,6 +167,7 @@ export interface RetitlePlanOptions {
|
|
|
165
167
|
model: Model<Api> | undefined;
|
|
166
168
|
isManual: boolean;
|
|
167
169
|
systemPrompt: string;
|
|
170
|
+
timeoutMs: number;
|
|
168
171
|
existingPlan?: AutoTitleTriggerPlan;
|
|
169
172
|
getSessionEpoch?: () => number;
|
|
170
173
|
notifyOnSuccess?: boolean;
|
|
@@ -173,8 +176,17 @@ export interface RetitlePlanOptions {
|
|
|
173
176
|
export async function runRetitlePlan(
|
|
174
177
|
options: RetitlePlanOptions,
|
|
175
178
|
): Promise<AutoTitleGenerationResult> {
|
|
176
|
-
const {
|
|
177
|
-
|
|
179
|
+
const {
|
|
180
|
+
pi,
|
|
181
|
+
controller,
|
|
182
|
+
ctx,
|
|
183
|
+
model,
|
|
184
|
+
isManual,
|
|
185
|
+
systemPrompt,
|
|
186
|
+
timeoutMs,
|
|
187
|
+
existingPlan,
|
|
188
|
+
getSessionEpoch,
|
|
189
|
+
} = options;
|
|
178
190
|
const notifyOnSuccess = options.notifyOnSuccess ?? isManual;
|
|
179
191
|
|
|
180
192
|
const plan = existingPlan ?? controller.handleManualRetitle(ctx);
|
|
@@ -211,6 +223,7 @@ export async function runRetitlePlan(
|
|
|
211
223
|
titleContext,
|
|
212
224
|
plan.reason,
|
|
213
225
|
systemPrompt,
|
|
226
|
+
timeoutMs,
|
|
214
227
|
);
|
|
215
228
|
if (!generatedTitle.ok) {
|
|
216
229
|
return generatedTitle;
|
|
@@ -257,6 +270,7 @@ async function retitleStoredSession(
|
|
|
257
270
|
model: Model<Api>,
|
|
258
271
|
sessionPath: string,
|
|
259
272
|
systemPrompt: string,
|
|
273
|
+
timeoutMs: number,
|
|
260
274
|
): Promise<"retitled" | "unchanged" | "failed"> {
|
|
261
275
|
const sessionManager = SessionManager.open(sessionPath);
|
|
262
276
|
const currentTitle = sessionManager.getSessionName();
|
|
@@ -279,6 +293,7 @@ async function retitleStoredSession(
|
|
|
279
293
|
titleContext,
|
|
280
294
|
plan.reason,
|
|
281
295
|
systemPrompt,
|
|
296
|
+
timeoutMs,
|
|
282
297
|
);
|
|
283
298
|
if (!generatedTitle.ok) {
|
|
284
299
|
return "failed";
|
|
@@ -57,6 +57,7 @@ export async function showRetitleWizard(
|
|
|
57
57
|
model: Model<Api> | undefined,
|
|
58
58
|
getSessionEpoch: () => number,
|
|
59
59
|
systemPrompt: string,
|
|
60
|
+
timeoutMs: number,
|
|
60
61
|
options?: RetitleWizardOptions,
|
|
61
62
|
): Promise<RetitleCommandOutcome> {
|
|
62
63
|
return ctx.ui.custom<RetitleCommandOutcome>(
|
|
@@ -70,6 +71,7 @@ export async function showRetitleWizard(
|
|
|
70
71
|
model,
|
|
71
72
|
getSessionEpoch,
|
|
72
73
|
systemPrompt,
|
|
74
|
+
timeoutMs,
|
|
73
75
|
done,
|
|
74
76
|
options,
|
|
75
77
|
),
|
|
@@ -99,6 +101,7 @@ class RetitleWizardPanel implements Focusable {
|
|
|
99
101
|
private readonly model: Model<Api> | undefined,
|
|
100
102
|
private readonly getSessionEpoch: () => number,
|
|
101
103
|
private readonly systemPrompt: string,
|
|
104
|
+
private readonly timeoutMs: number,
|
|
102
105
|
private readonly done: (result: RetitleCommandOutcome) => void,
|
|
103
106
|
options?: RetitleWizardOptions,
|
|
104
107
|
) {
|
|
@@ -250,6 +253,7 @@ class RetitleWizardPanel implements Focusable {
|
|
|
250
253
|
model: this.model,
|
|
251
254
|
isManual: true,
|
|
252
255
|
systemPrompt: this.systemPrompt,
|
|
256
|
+
timeoutMs: this.timeoutMs,
|
|
253
257
|
getSessionEpoch: this.getSessionEpoch,
|
|
254
258
|
});
|
|
255
259
|
if (result.ok) {
|
|
@@ -310,6 +314,7 @@ class RetitleWizardPanel implements Focusable {
|
|
|
310
314
|
mode,
|
|
311
315
|
this.getSessionEpoch,
|
|
312
316
|
this.systemPrompt,
|
|
317
|
+
this.timeoutMs,
|
|
313
318
|
);
|
|
314
319
|
notifyBulkRetitleResult(this.ctx, scan, mode, result);
|
|
315
320
|
this.done(
|
|
@@ -75,6 +75,7 @@ export default function sessionAutoTitleExtension(pi: ExtensionAPI): void {
|
|
|
75
75
|
model,
|
|
76
76
|
invocation,
|
|
77
77
|
settings.autoTitle.prompt,
|
|
78
|
+
settings.autoTitle.timeoutMs,
|
|
78
79
|
);
|
|
79
80
|
},
|
|
80
81
|
),
|
|
@@ -104,6 +105,7 @@ export default function sessionAutoTitleExtension(pi: ExtensionAPI): void {
|
|
|
104
105
|
getSessionEpoch: () => sessionEpoch,
|
|
105
106
|
notifyOnSuccess: false,
|
|
106
107
|
systemPrompt: settings.autoTitle.prompt,
|
|
108
|
+
timeoutMs: settings.autoTitle.timeoutMs,
|
|
107
109
|
})
|
|
108
110
|
.then((outcome) => {
|
|
109
111
|
if (outcome.ok) {
|
|
@@ -139,6 +141,7 @@ async function handleTitleInvocation(
|
|
|
139
141
|
model: Model<Api> | undefined,
|
|
140
142
|
invocation: RetitleCommandInvocation,
|
|
141
143
|
systemPrompt: string,
|
|
144
|
+
timeoutMs: number,
|
|
142
145
|
): Promise<RetitleCommandOutcome> {
|
|
143
146
|
const retitleOpts = {
|
|
144
147
|
pi,
|
|
@@ -147,6 +150,7 @@ async function handleTitleInvocation(
|
|
|
147
150
|
model,
|
|
148
151
|
isManual: true,
|
|
149
152
|
systemPrompt,
|
|
153
|
+
timeoutMs,
|
|
150
154
|
getSessionEpoch: state.getSessionEpoch,
|
|
151
155
|
};
|
|
152
156
|
|
|
@@ -165,7 +169,15 @@ async function handleTitleInvocation(
|
|
|
165
169
|
return retitleCurrentSession();
|
|
166
170
|
}
|
|
167
171
|
|
|
168
|
-
return showRetitleWizard(
|
|
172
|
+
return showRetitleWizard(
|
|
173
|
+
pi,
|
|
174
|
+
state.controller,
|
|
175
|
+
ctx,
|
|
176
|
+
model,
|
|
177
|
+
state.getSessionEpoch,
|
|
178
|
+
systemPrompt,
|
|
179
|
+
timeoutMs,
|
|
180
|
+
);
|
|
169
181
|
}
|
|
170
182
|
|
|
171
183
|
if (invocation.scope === "this") {
|
|
@@ -180,6 +192,7 @@ async function handleTitleInvocation(
|
|
|
180
192
|
model,
|
|
181
193
|
state.getSessionEpoch,
|
|
182
194
|
systemPrompt,
|
|
195
|
+
timeoutMs,
|
|
183
196
|
{
|
|
184
197
|
initialInvocation: {
|
|
185
198
|
scope: invocation.scope,
|
|
@@ -201,6 +214,7 @@ async function handleTitleInvocation(
|
|
|
201
214
|
mode,
|
|
202
215
|
state.getSessionEpoch,
|
|
203
216
|
systemPrompt,
|
|
217
|
+
timeoutMs,
|
|
204
218
|
);
|
|
205
219
|
notifyBulkRetitleResult(ctx, scan, mode, result);
|
|
206
220
|
return "success";
|
|
@@ -7,6 +7,7 @@ import { type Static, Type } from "typebox";
|
|
|
7
7
|
import { parseTypeBoxValue } from "./typebox.ts";
|
|
8
8
|
|
|
9
9
|
export const DEFAULT_AUTO_TITLE_REFRESH_TURNS = 4;
|
|
10
|
+
export const DEFAULT_AUTO_TITLE_TIMEOUT_SECONDS = 15;
|
|
10
11
|
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.`;
|
|
11
12
|
const SESSION_FILE_SETTINGS_SCHEMA = Type.Object({
|
|
12
13
|
handoff: Type.Optional(
|
|
@@ -22,6 +23,7 @@ const SESSION_FILE_SETTINGS_SCHEMA = Type.Object({
|
|
|
22
23
|
autoTitle: Type.Optional(
|
|
23
24
|
Type.Object({
|
|
24
25
|
refreshTurns: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
26
|
+
timeoutSecs: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
25
27
|
model: Type.Optional(Type.String()),
|
|
26
28
|
thinkingLevel: Type.Optional(Type.String()),
|
|
27
29
|
prompt: Type.Optional(Type.String()),
|
|
@@ -57,6 +59,7 @@ export interface AgentModelSettings {
|
|
|
57
59
|
|
|
58
60
|
export interface AutoTitleSettings extends AgentModelSettings {
|
|
59
61
|
refreshTurns: number;
|
|
62
|
+
timeoutMs: number;
|
|
60
63
|
prompt: string;
|
|
61
64
|
}
|
|
62
65
|
|
|
@@ -193,6 +196,7 @@ function resolveSessionSettings(fileSettings: SessionFileSettings): SessionSetti
|
|
|
193
196
|
autoTitle: {
|
|
194
197
|
...resolveAgentModelSettings(fileSettings.autoTitle),
|
|
195
198
|
refreshTurns: fileSettings.autoTitle?.refreshTurns ?? DEFAULT_AUTO_TITLE_REFRESH_TURNS,
|
|
199
|
+
timeoutMs: (fileSettings.autoTitle?.timeoutSecs ?? DEFAULT_AUTO_TITLE_TIMEOUT_SECONDS) * 1000,
|
|
196
200
|
prompt: normalizeAutoTitlePrompt(fileSettings.autoTitle?.prompt),
|
|
197
201
|
},
|
|
198
202
|
ask: {
|