pi-code 1.0.7 → 1.0.9

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/extensions/web.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  import type { LookupAddress } from 'node:dns'
10
10
  import { lookup } from 'node:dns/promises'
11
11
  import type { LookupFunction } from 'node:net'
12
+ import type { Usage } from '@earendil-works/pi-ai'
12
13
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent'
13
14
  import { Type } from 'typebox'
14
15
 
@@ -252,16 +253,16 @@ function rememberFetch(cache: FetchCache, url: string, body: string, now: number
252
253
  }
253
254
 
254
255
  /** Claude's WebFetch runs the prompt over the page with a fast model and returns
255
- * that answer, not the raw page. Best-effort: any failure (no model, provider error)
256
+ * that answer, not the raw page, along with the nested call's usage so the tool
257
+ * result can account for it. Best-effort: any failure (no model, provider error)
256
258
  * yields null so the caller falls back to the markdown. */
257
- async function answerFromPage(model: Parameters<typeof completeText>[0], prompt: string, url: string, body: string, signal?: AbortSignal): Promise<string | null> {
259
+ async function answerFromPage(model: Parameters<typeof completeText>[0], prompt: string, url: string, body: string, signal?: AbortSignal): Promise<{ text: string; usage: Usage } | null> {
258
260
  try {
259
- const answer = await completeText(model, `${prompt}\n\nAnswer using only the page content below, fetched from ${url}:\n\n${body}`, {
261
+ return await completeText(model, `${prompt}\n\nAnswer using only the page content below, fetched from ${url}:\n\n${body}`, {
260
262
  system: 'You extract and answer questions from a web page. Answer only from the provided content, concisely. If the content does not contain the answer, say so.',
261
263
  maxTokens: 1024,
262
264
  signal,
263
265
  })
264
- return answer || null
265
266
  } catch {
266
267
  return null
267
268
  }
@@ -318,14 +319,18 @@ export default function webExtension(pi: ExtensionAPI) {
318
319
  }
319
320
 
320
321
  // Best-effort prompt-over-page: a failure returns null, so web_fetch always
321
- // falls back to the raw markdown and returns something.
322
+ // falls back to the raw markdown and returns something. The nested call's
323
+ // usage rides on the result either way, so pi counts it in session totals.
324
+ let usage: Usage | undefined
322
325
  if (params.prompt && ctx?.model) {
323
326
  const answer = await answerFromPage(ctx.model, params.prompt, params.url, body, signal)
324
- if (answer) return { content: [{ type: 'text' as const, text: answer }], details: {} }
327
+ if (answer?.text) return { content: [{ type: 'text' as const, text: answer.text }], details: {}, usage: answer.usage }
328
+ // An empty answer still cost the completion; the fallback carries its usage.
329
+ usage = answer?.usage
325
330
  }
326
331
  // The char cap alone admits thousands of short lines; pi's tool-output budget
327
332
  // bounds lines too, which the shared guard enforces.
328
- return { content: [{ type: 'text' as const, text: capForContext(body) || '(empty response)' }], details: {} }
333
+ return { content: [{ type: 'text' as const, text: capForContext(body) || '(empty response)' }], details: {}, usage }
329
334
  },
330
335
  })
331
336
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-code",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Claude Code experience for the pi coding agent: reads your .claude config (rules, commands, skills, hooks, output styles, MCP servers, agents) and adds todo, checkpoints, memory, web, and subagents",
5
5
  "keywords": [
6
6
  "pi",