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 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(), AUTO_TITLE_REQUEST_TIMEOUT_MS);
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 { pi, controller, ctx, model, isManual, systemPrompt, existingPlan, getSessionEpoch } =
177
- options;
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(pi, state.controller, ctx, model, state.getSessionEpoch, systemPrompt);
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: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sessions",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "description": "Pi session search, ask, handoff, messaging, auto-titling, and indexing tools",
6
6
  "license": "MIT",