@zhushanwen/pi-cache-probe 0.3.0 → 0.3.1

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": "@zhushanwen/pi-cache-probe",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Zero-impact Pi probe extension — records system prompt / tools fingerprint hashes into session custom entries (only on change) to attribute LLM prompt-cache miss to prefix drift vs server-side eviction.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -25,6 +25,9 @@
25
25
  "index.ts",
26
26
  "analyze.py"
27
27
  ],
28
+ "dependencies": {
29
+ "@zhushanwen/pi-ext-guards": "0.4.0"
30
+ },
28
31
  "devDependencies": {
29
32
  "@vitest/coverage-v8": "^4.1.9",
30
33
  "vitest": "^4.1.8"
@@ -74,7 +74,7 @@ describe("extractSystem(payload 四口径)", () => {
74
74
  ]);
75
75
  });
76
76
 
77
- it("Google:顶层 systemInstruction 对象", () => {
77
+ it("伪造对象形态:顶层 systemInstruction(实装恒传 string,仅测试构造可达)", () => {
78
78
  expect(extractSystem({ systemInstruction: { role: "system", parts: [] } })).toEqual({
79
79
  role: "system",
80
80
  parts: [],
@@ -10,6 +10,8 @@
10
10
 
11
11
  import { createHash } from "node:crypto";
12
12
 
13
+ import { isRecord } from "@zhushanwen/pi-ext-guards";
14
+
13
15
  /** 指纹 hash 长度(hex 字符数)。 */
14
16
  const HASH_LEN = 16;
15
17
 
@@ -41,10 +43,6 @@ export interface ProbeEntryData {
41
43
  error?: string;
42
44
  }
43
45
 
44
- function isRecord(value: unknown): value is Record<string, unknown> {
45
- return value !== null && typeof value === "object";
46
- }
47
-
48
46
  /** 递归 sort keys 的稳定序列化(undefined 归一为 null,防 key 顺序抖动产生假变化)。 */
49
47
  function stableStringify(value: unknown): string {
50
48
  if (value === undefined) return "null";
@@ -63,7 +61,12 @@ export function hashOf(value: unknown): string {
63
61
  * - OpenAI 兼容:messages 内 role 为 system 或 developer
64
62
  * (pi 源码 ai/src/api/openai-completions.ts 的 useDeveloperRole 分支)
65
63
  * - Anthropic:顶层 system(数组)
66
- * - Google:顶层 systemInstruction(对象)
64
+ * - Google:顶层字段 systemInstruction 为对象形态——仅手工伪造的输入可达:
65
+ * pi-ai 0.84.4 实装(google-generative-ai.js:294 / google-vertex.js:368)恒传
66
+ * string(sanitizeSurrogates(context.systemPrompt)),isRecord 分支对 string
67
+ * 恒不命中,故实装流量下 Google 侧返回 null(spFull 观测恒 no-system)。
68
+ * 归因是「提取只认对象形态」而非「Google 请求没有 system」,勿把下方
69
+ * isRecord(systemInstruction) 分支误读为 Google 口径主通路。
67
70
  */
68
71
  export function extractSystem(payload: unknown): unknown | null {
69
72
  if (!isRecord(payload)) return null;
package/src/index.ts CHANGED
@@ -9,7 +9,9 @@
9
9
  * - before_agent_start(每 turn 1 次):算 7 个输入侧 hash 暂存 pending
10
10
  * (systemPromptOptions 各字段 + getAllTools 注册表)
11
11
  * - before_provider_request(每笔 LLM 请求,仅消费 turn 首笔):从最终 payload
12
- * 提取 spFull(system 消息,兼容 system/developer/anthropic/google 口径)与
12
+ * 提取 spFull(system 消息,兼容 system/developer/anthropic/google 口径;
13
+ * google 口径实装流量恒 no-system——pi-ai 恒传 string,该分支仅手工伪造
14
+ * 对象输入可达,详见 fingerprint.ts extractSystem 注释)与
13
15
  * toolsSent(tools 数组),与 pending 合并对比,变化/基线时 appendEntry
14
16
  * - agent_end:turn 内无 provider 请求则丢弃 pending(无请求即无归因价值)
15
17
  *
@@ -21,6 +23,7 @@
21
23
  */
22
24
 
23
25
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
26
+ import { toErrorMessage } from "@zhushanwen/pi-ext-guards";
24
27
 
25
28
  import {
26
29
  buildProbeEntry,
@@ -60,12 +63,12 @@ export default function (pi: ExtensionAPI): void {
60
63
  // 输入侧 7 hash 计算在 fingerprint.ts 纯函数层(handler 只做编排与 fail-safe)
61
64
  pending = computePendingFingerprint(event?.systemPromptOptions, pi.getAllTools());
62
65
  } catch (err) {
63
- stderr(`before_agent_start failed: ${err instanceof Error ? err.message : String(err)}`);
66
+ stderr(`before_agent_start failed: ${toErrorMessage(err)}`);
64
67
  pending = null;
65
68
  try {
66
- pi.appendEntry(CUSTOM_TYPE, { v: SCHEMA_VERSION, seq, error: String(err instanceof Error ? err.message : err) });
69
+ pi.appendEntry(CUSTOM_TYPE, { v: SCHEMA_VERSION, seq, error: toErrorMessage(err) });
67
70
  } catch (appendErr) {
68
- stderr(`appendEntry failed: ${appendErr instanceof Error ? appendErr.message : String(appendErr)}`);
71
+ stderr(`appendEntry failed: ${toErrorMessage(appendErr)}`);
69
72
  }
70
73
  needsBaseline = true;
71
74
  }
@@ -88,12 +91,12 @@ export default function (pi: ExtensionAPI): void {
88
91
  last = cur;
89
92
  needsBaseline = false;
90
93
  } catch (err) {
91
- const msg = err instanceof Error ? err.message : String(err);
94
+ const msg = toErrorMessage(err);
92
95
  stderr(`before_provider_request failed: ${msg}`);
93
96
  try {
94
97
  pi.appendEntry(CUSTOM_TYPE, { v: SCHEMA_VERSION, seq, error: msg });
95
98
  } catch (appendErr) {
96
- stderr(`appendEntry failed: ${appendErr instanceof Error ? appendErr.message : String(appendErr)}`);
99
+ stderr(`appendEntry failed: ${toErrorMessage(appendErr)}`);
97
100
  }
98
101
  needsBaseline = true;
99
102
  }