pi-codebuddy-sdk 0.1.14 → 0.1.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-codebuddy-sdk",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "CodeBuddy Agent SDK provider for Pi — native tool execution, 29+ models, thinking controls, session pool",
5
5
  "author": "slahser",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  * Two files: index.ts (registration + model discovery), provider.ts (streaming).
11
11
  */
12
12
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
- import type { ProviderApi, SimpleStreamOptions } from "@earendil-works/pi-ai/compat";
13
+ import type { ProviderApi } from "@earendil-works/pi-ai/compat";
14
14
  import { streamCodebuddy } from "./provider.js";
15
15
 
16
16
  // ── provider registration ──
@@ -25,7 +25,6 @@ export default function activate(api: ExtensionAPI) {
25
25
  streamSimple: streamCodebuddy,
26
26
  });
27
27
 
28
- // Lazy model discovery at startup
29
28
  queueMicrotask(async () => {
30
29
  try {
31
30
  const models = await discoverModels();
@@ -38,8 +37,7 @@ export default function activate(api: ExtensionAPI) {
38
37
  streamSimple: streamCodebuddy,
39
38
  });
40
39
  } catch {
41
- // Model discovery failed — provider registered with empty models,
42
- // user can still try manually.
40
+ // Model discovery failed — provider stays with empty models, user can try manually.
43
41
  }
44
42
  });
45
43
  }
@@ -52,13 +50,13 @@ async function getSdk() {
52
50
  return _sdk;
53
51
  }
54
52
 
55
- export async function discoverModels(): Promise<ProviderApi["models"]> {
53
+ async function discoverModels(): Promise<ProviderApi["models"]> {
56
54
  const sdk = await getSdk();
57
55
  const q = sdk.query({ prompt: " ", options: { maxTurns: 0 } });
58
56
  const supported = await q.supportedModels();
59
57
  await q.interrupt();
60
58
 
61
- const models = supported.map((m) => {
59
+ return supported.map((m) => {
62
60
  const caps = detectCapabilities(m.id);
63
61
  return {
64
62
  id: m.id,
@@ -72,11 +70,9 @@ export async function discoverModels(): Promise<ProviderApi["models"]> {
72
70
  maxTokens: caps.maxTokens,
73
71
  };
74
72
  });
75
-
76
- return models;
77
73
  }
78
74
 
79
- // ── capability detection (pattern-based, like pi-claude-bridge) ──
75
+ // ── capability detection ──
80
76
 
81
77
  const THINKING_PATTERNS = [/claude/i, /gemini/i, /gpt-5/i];
82
78
  const IMAGE_PATTERNS = [/claude/i, /gemini/i, /gpt/i];
package/src/provider.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Provider entry point — pi-claude-bridge pattern.
3
3
  *
4
4
  * For each Pi turn:
5
- * 1. Extract the latest user message (strip context-mode injection)
5
+ * 1. Extract the latest user message
6
6
  * 2. Call sdk.query() — stateless, CodeBuddy uses its own system prompt
7
7
  * 3. Stream events back to Pi (text, thinking, toolcalls)
8
8
  */
@@ -54,39 +54,16 @@ function makeErrorPartial(model: Model<any>, error: unknown): AssistantMessage {
54
54
 
55
55
  // ── extract user message ──
56
56
 
57
- /**
58
- * Extract the latest user message from Pi Context.
59
- * Strips context-mode session state injection.
60
- *
61
- * Like pi-claude-bridge's extractUserPrompt() but with context-mode defense.
62
- */
63
57
  function extractUserMessage(context: Context): string {
64
58
  const msgs = context.messages;
65
-
66
- // Pi injects context-mode state as the LAST user message (after the real one).
67
- // Search from the end, skip obvious context-mode injections.
68
59
  for (let i = msgs.length - 1; i >= 0; i--) {
69
60
  if (msgs[i].role !== "user") continue;
70
61
  const raw = contentToText((msgs[i] as any).content);
71
- if (!raw) continue;
72
-
73
- // Skip messages that are entirely context-mode injection
74
- if (isContextModeInjection(raw)) continue;
75
-
76
- return stripPiInjection(raw);
62
+ if (raw) return raw.trim();
77
63
  }
78
64
  return "Continue.";
79
65
  }
80
66
 
81
- /** Detect if a user message is a Pi context-mode state injection (not a real message). */
82
- function isContextModeInjection(text: string): boolean {
83
- const t = text.trim();
84
- if (t.startsWith("context-mode active.")) return true;
85
- if (t.includes("<session_state") || t.includes("<session_mode")) return true;
86
- if (t.startsWith("<summary>") && t.includes("</summary>")) return true;
87
- return false;
88
- }
89
-
90
67
  function contentToText(content: unknown): string {
91
68
  if (typeof content === "string") return content;
92
69
  if (!Array.isArray(content)) return "";
@@ -96,55 +73,6 @@ function contentToText(content: unknown): string {
96
73
  .join("\n");
97
74
  }
98
75
 
99
- /**
100
- * Strip context-mode session state that Pi prepends to user messages.
101
- *
102
- * Pi injects several formats:
103
- * context-mode active. Hierarchy: ...
104
- * <session_state source="compaction"> ... </session_state>
105
- * <summary>Compacted: ...</summary>
106
- *
107
- * Strip all of them, keep only the actual user message.
108
- */
109
- function stripPiInjection(text: string): string {
110
- // Fast path
111
- if (!text.includes("context-mode") && !text.includes("<session_state") && !text.includes("<summary>")) {
112
- return text;
113
- }
114
-
115
- // 1. Remove <summary> blocks (compaction summaries contain context-mode state)
116
- text = text.replace(/<summary>[\s\S]*?<\/summary>\s*/g, "");
117
-
118
- // 2. Remove <session_state> block and everything between "context-mode" and </session_state>
119
- const sessionEnd = text.lastIndexOf("</session_state>");
120
- if (sessionEnd >= 0) {
121
- const after = text.slice(sessionEnd + "</session_state>".length).trimStart();
122
- if (after) {
123
- text = after;
124
- } else {
125
- // All content was in the session_state block
126
- const before = text.slice(0, text.indexOf("<session_state")).trim();
127
- if (before) text = before;
128
- }
129
- }
130
-
131
- // 3. Remove "context-mode active." line and everything after until a blank line
132
- // Pattern: "context-mode active. ... Hierarchy: ..." followed by blank line, then real message
133
- const cmRe = /context-mode active\.[\s\S]*?\n\s*\n([\s\S]*)$/;
134
- const cmMatch = text.match(cmRe);
135
- if (cmMatch?.[1]?.trim()) {
136
- text = cmMatch[1].trim();
137
- }
138
-
139
- // 4. If "context-mode" is still the only content, return empty
140
- if (text.trim() === "context-mode active." || text.trim().startsWith("context-mode active.")) {
141
- const afterCm = text.replace(/context-mode active\.[\s\S]*/, "").trim();
142
- if (afterCm) text = afterCm;
143
- }
144
-
145
- return text.trim() || text;
146
- }
147
-
148
76
  // ── thinking config ──
149
77
 
150
78
  const THINKING_BUDGETS: Record<string, number> = {
@@ -228,7 +156,7 @@ export function streamCodebuddy(
228
156
  if (options.signal.aborted) { await onAbort(); return; }
229
157
  }
230
158
 
231
- // ── per-block tracking (for stream_event delta accumulation) ──
159
+ // ── per-block tracking ──
232
160
  type BlockKind = "text" | "thinking" | "tool_use" | "redacted_thinking";
233
161
  const blockKind = new Map<number, BlockKind>();
234
162
  const blockAccum = new Map<number, string>();
@@ -304,7 +232,6 @@ export function streamCodebuddy(
304
232
  continue;
305
233
  }
306
234
 
307
- // Token-level streaming via stream_event (preferred)
308
235
  if (msg.type === "stream_event") {
309
236
  hasStreamEvents = true;
310
237
  const evt = msg.event;
@@ -326,7 +253,7 @@ export function streamCodebuddy(
326
253
  continue;
327
254
  }
328
255
 
329
- // Fallback: full assistant messages
256
+ // Fallback: full assistant messages (when model doesn't support streaming)
330
257
  if (msg.type === "assistant" && !hasStreamEvents) {
331
258
  let ci = 0;
332
259
  for (const block of msg.message.content) {
@@ -1 +0,0 @@
1
- # Temporary: add debug logging to extractUserMessage