pi-spark 0.25.0 → 0.26.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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.26.0](https://github.com/zlliang/pi-spark/compare/v0.25.0...v0.26.0) (2026-09-20)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * Require Pi 0.86.0 or later.
9
+
10
+ ### Features
11
+
12
+ * adopt Pi 0.86 model and usage APIs ([5d5868d](https://github.com/zlliang/pi-spark/commit/5d5868defdad303a0b8a9761b013886a198c268d))
13
+
3
14
  ## [0.25.0](https://github.com/zlliang/pi-spark/compare/v0.24.0...v0.25.0) (2026-09-14)
4
15
 
5
16
 
@@ -73,3 +73,5 @@ The independent-session design preserves the main thread's WebSocket reuse, `pre
73
73
  Cache admission remains provider-controlled and noisy. In repeated GPT-5.6 Luna title A/B probes, isolated background calls caused no continuation errors and showed no negative main-thread cache trend. Main second-turn latency remained around the normal control range instead of paying the reconnect penalty seen when the shared main socket was closed.
74
74
 
75
75
  Use the [`measuring-model-cache`](../.agents/skills/measuring-model-cache/SKILL.md) skill to repeat the comparison after changes to Pi, providers, session handling, title, recap, or other background model features.
76
+
77
+ **Update Sep 19, 2026:** Background calls now use Pi 0.86's `ctx.modelRegistry.streamSimple(...).result()` to support extension-registered providers and resolve authentication automatically. Independent Codex UUIDv7 sessions and cleanup in `finally` remain unchanged.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-spark",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "Pi package that polishes your daily experience",
5
5
  "keywords": [
6
6
  "pi-coding-agent",
@@ -44,17 +44,17 @@
44
44
  "zod": "^4.4.3"
45
45
  },
46
46
  "peerDependencies": {
47
- "@earendil-works/pi-agent-core": ">=0.85.0",
48
- "@earendil-works/pi-ai": ">=0.85.0",
49
- "@earendil-works/pi-coding-agent": ">=0.85.0",
50
- "@earendil-works/pi-tui": ">=0.85.0",
47
+ "@earendil-works/pi-agent-core": ">=0.86.0",
48
+ "@earendil-works/pi-ai": ">=0.86.0",
49
+ "@earendil-works/pi-coding-agent": ">=0.86.0",
50
+ "@earendil-works/pi-tui": ">=0.86.0",
51
51
  "typebox": "*"
52
52
  },
53
53
  "devDependencies": {
54
- "@earendil-works/pi-agent-core": ">=0.85.0",
55
- "@earendil-works/pi-ai": ">=0.85.0",
56
- "@earendil-works/pi-coding-agent": ">=0.85.0",
57
- "@earendil-works/pi-tui": ">=0.85.0",
54
+ "@earendil-works/pi-agent-core": ">=0.86.0",
55
+ "@earendil-works/pi-ai": ">=0.86.0",
56
+ "@earendil-works/pi-coding-agent": ">=0.86.0",
57
+ "@earendil-works/pi-tui": ">=0.86.0",
58
58
  "@types/node": "*",
59
59
  "typebox": "*",
60
60
  "typescript": "^7.0.2"
@@ -4,7 +4,7 @@ import { clearRecapWidget, setRecapLoadingWidget, setRecapTextWidget } from "./w
4
4
  import { sanitizeText } from "../../utils/format";
5
5
  import { completeBackground, resolveModelSettings } from "../../utils/model";
6
6
 
7
- import type { Api, Model, ModelThinkingLevel, SimpleStreamOptions, Usage } from "@earendil-works/pi-ai";
7
+ import type { Api, Model, ModelThinkingLevel, Usage } from "@earendil-works/pi-ai";
8
8
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
9
9
  import type { RecapConfig } from "./config";
10
10
 
@@ -82,23 +82,18 @@ export class RecapManager {
82
82
  }
83
83
 
84
84
  private async generate(ctx: ExtensionContext, model: Model<Api>, thinkingLevel: ModelThinkingLevel, signal: AbortSignal): Promise<{ content: string; usage: Usage }> {
85
- const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
86
- if (!auth.ok) throw new Error(auth.error);
87
-
88
- const options: SimpleStreamOptions = { maxTokens: MAX_TOKENS, signal };
89
- if (auth.apiKey) options.apiKey = auth.apiKey;
90
- if (auth.headers) options.headers = auth.headers;
91
- if (auth.env) options.env = auth.env;
92
- if (thinkingLevel !== "off") options.reasoning = thinkingLevel;
93
-
94
- const response = await completeBackground(model, {
85
+ const response = await completeBackground(ctx, model, {
95
86
  systemPrompt: SYSTEM_PROMPT,
96
87
  messages: [{
97
88
  role: "user",
98
89
  content: [{ type: "text", text: this.buildPrompt(ctx) }],
99
90
  timestamp: Date.now(),
100
91
  }],
101
- }, options);
92
+ }, {
93
+ maxTokens: MAX_TOKENS,
94
+ signal,
95
+ reasoning: thinkingLevel === "off" ? undefined : thinkingLevel,
96
+ });
102
97
 
103
98
  if (response.stopReason === "error") {
104
99
  throw new Error(response.errorMessage ?? "Recap generation failed");
@@ -3,7 +3,7 @@ import { convertToLlm, serializeConversation } from "@earendil-works/pi-coding-a
3
3
  import { sanitizeText } from "../../utils/format";
4
4
  import { completeBackground, resolveModelSettings } from "../../utils/model";
5
5
 
6
- import type { Api, Model, ModelThinkingLevel, SimpleStreamOptions, Usage } from "@earendil-works/pi-ai";
6
+ import type { Api, Model, ModelThinkingLevel, Usage } from "@earendil-works/pi-ai";
7
7
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
8
8
  import type { TitleConfig } from "./config";
9
9
 
@@ -64,23 +64,18 @@ export class TitleManager {
64
64
  }
65
65
 
66
66
  private async generate(ctx: ExtensionContext, model: Model<Api>, thinkingLevel: ModelThinkingLevel, signal: AbortSignal): Promise<{ content: string; usage: Usage }> {
67
- const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
68
- if (!auth.ok) throw new Error(auth.error);
69
-
70
- const options: SimpleStreamOptions = { maxTokens: MAX_TOKENS, signal };
71
- if (auth.apiKey) options.apiKey = auth.apiKey;
72
- if (auth.headers) options.headers = auth.headers;
73
- if (auth.env) options.env = auth.env;
74
- if (thinkingLevel !== "off") options.reasoning = thinkingLevel;
75
-
76
- const response = await completeBackground(model, {
67
+ const response = await completeBackground(ctx, model, {
77
68
  systemPrompt: SYSTEM_PROMPT,
78
69
  messages: [{
79
70
  role: "user",
80
71
  content: [{ type: "text", text: this.buildPrompt(ctx) }],
81
72
  timestamp: Date.now(),
82
73
  }],
83
- }, options);
74
+ }, {
75
+ maxTokens: MAX_TOKENS,
76
+ signal,
77
+ reasoning: thinkingLevel === "off" ? undefined : thinkingLevel,
78
+ });
84
79
 
85
80
  if (response.stopReason === "error") {
86
81
  throw new Error(response.errorMessage ?? "Title generation failed");
@@ -1,10 +1,9 @@
1
1
  import { uuidv7 } from "@earendil-works/pi-agent-core";
2
2
  import { clampThinkingLevel, cleanupSessionResources } from "@earendil-works/pi-ai";
3
- import { completeSimple } from "@earendil-works/pi-ai/compat";
4
3
 
5
4
  import { formatModel } from "./format";
6
5
 
7
- import type { Api, Model, ModelThinkingLevel } from "@earendil-works/pi-ai";
6
+ import type { Api, AssistantMessage, Context, Model, ModelsSimpleStreamOptions, ModelThinkingLevel } from "@earendil-works/pi-ai";
8
7
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
9
8
  import type { OptionalModelConfig } from "../config/model";
10
9
 
@@ -30,17 +29,17 @@ type ThinkingLevelSelection = {
30
29
  * Completes a one-shot background request, using an isolated session for OpenAI Codex models.
31
30
  * See [the investigation](../../docs/background-model-calls-and-openai-codex-sessions.md).
32
31
  */
33
- export const completeBackground: typeof completeSimple = async (model, context, options) => {
34
- if (model.api !== "openai-codex-responses") return completeSimple(model, context, options);
32
+ export async function completeBackground(ctx: ExtensionContext, model: Model<Api>, context: Context, options?: ModelsSimpleStreamOptions): Promise<AssistantMessage> {
33
+ if (model.api !== "openai-codex-responses") return ctx.modelRegistry.streamSimple(model, context, options).result();
35
34
 
36
35
  const sessionId = uuidv7();
37
36
 
38
37
  try {
39
- return await completeSimple(model, context, { ...options, sessionId });
38
+ return await ctx.modelRegistry.streamSimple(model, context, { ...options, sessionId }).result();
40
39
  } finally {
41
40
  cleanupSessionResources(sessionId);
42
41
  }
43
- };
42
+ }
44
43
 
45
44
  /**
46
45
  * Resolves the model and thinking level for a background feature (recap, title, ...).
@@ -33,7 +33,7 @@ export function isUsage(value: unknown): value is Usage {
33
33
  * - `message` carries them on `message`.
34
34
  * - `custom` on `data`.
35
35
  * - `custom_message` on `details`.
36
- * - `compaction` and `branch_summary` directly on the entry.
36
+ * - `usage`, `compaction`, and `branch_summary` directly on the entry.
37
37
  *
38
38
  * Other entry types carry no usage. Returns `undefined` when the resolved field has no `usage`.
39
39
  */
@@ -42,8 +42,7 @@ export function getEntryUsage(entry: SessionEntry): Usage | undefined {
42
42
  if (entry.type === "message") source = entry.message;
43
43
  else if (entry.type === "custom") source = entry.data;
44
44
  else if (entry.type === "custom_message") source = entry.details;
45
- else if (entry.type === "compaction" || entry.type === "branch_summary") source = entry;
46
- else return;
45
+ else source = entry;
47
46
 
48
47
  if (typeof source !== "object" || source === null) return;
49
48
  const data = source as { usage?: unknown };