@maxiaochao/pi-toolkit 0.13.7 → 0.13.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +5 -1
  3. package/extensions/btw/index.ts +7 -16
  4. package/extensions/btw/tests/context.mjs +78 -0
  5. package/package.json +4 -3
  6. package/packages/cache-export/CHANGELOG.md +11 -0
  7. package/packages/cache-export/README.md +14 -0
  8. package/packages/cache-export/TESTING.md +8 -2
  9. package/packages/cache-export/docs/demo.gif +0 -0
  10. package/packages/cache-export/docs/screenshot.png +0 -0
  11. package/packages/cache-export/extensions/cache-export.ts +44 -3
  12. package/packages/cache-export/package.json +24 -6
  13. package/packages/cache-export/src/render.ts +120 -59
  14. package/packages/cache-export/tests/e2e-browser.mjs +45 -5
  15. package/packages/cache-export/tests/evidence.mjs +113 -0
  16. package/packages/cache-export/tests/extension.mjs +52 -0
  17. package/packages/cache-export/tests/run-tests.mjs +12 -11
  18. package/packages/codex-edit/CHANGELOG.md +7 -0
  19. package/packages/codex-edit/README.md +4 -0
  20. package/packages/codex-edit/TESTING.md +4 -0
  21. package/packages/codex-edit/docs/test-report.html +19 -11
  22. package/packages/codex-edit/docs/test-report.md +22 -13
  23. package/packages/codex-edit/docs/test-summary.md +1 -1
  24. package/packages/codex-edit/extensions/codex-edit.ts +12 -4
  25. package/packages/codex-edit/tests/comparison-cases.mjs +35 -0
  26. package/packages/codex-edit/tests/comparison-harness.mjs +5 -6
  27. package/packages/codex-edit/tests/generate-test-report.mjs +12 -7
  28. package/packages/codex-edit/tests/native-vs-codex.ts +14 -14
  29. package/packages/codex-edit/tests/released-vs-local.test.mjs +4 -0
  30. package/packages/herdr-subagents/ARCHITECTURE.md +4 -1
  31. package/packages/herdr-subagents/CHANGELOG.md +12 -0
  32. package/packages/herdr-subagents/README.md +11 -2
  33. package/packages/herdr-subagents/TESTING.md +12 -0
  34. package/packages/herdr-subagents/docs/demo.webm +0 -0
  35. package/packages/herdr-subagents/docs/screenshot.png +0 -0
  36. package/packages/herdr-subagents/extensions/subagent-report.ts +84 -30
  37. package/packages/herdr-subagents/package.json +2 -2
  38. package/packages/herdr-subagents/tests/report-boundary.mjs +83 -0
  39. package/packages/herdr-subagents/tests/report-lifecycle.mjs +69 -0
  40. package/packages/herdr-subagents/tests/runtime.mjs +26 -0
  41. package/packages/herdr-subagents/tests/standalone-package.mjs +23 -0
  42. package/packages/worktree/CHANGELOG.md +4 -0
  43. package/packages/worktree/README.md +4 -0
  44. package/packages/worktree/TESTING.md +4 -0
  45. package/packages/worktree/bin/pi-worktree +1 -1
  46. package/packages/worktree/tests/run-tests.sh +10 -0
  47. package/skills/web-browser/SKILL.md +3 -1
  48. package/skills/web-browser/scripts/start.js +42 -8
package/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## 2026-10-23 - v0.13.9
2
+
3
+ - cache-export v0.1.1: correct request timing, usage scope and historical cache evidence
4
+
5
+ ## 2026-10-23 - v0.13.8
6
+
7
+ - herdr-subagents v0.7.5: bound missing-report recovery and close unconfirmed child outcomes
8
+ - Cache Export: use request timing and historical, non-secret cache-policy observations; separate auxiliary usage, mark unknowns, and resolve exports against the session cwd.
9
+ - BTW: seed main and restored side history through SessionManager instead of transient agent state.
10
+ - Herdr Subagents: one durable child-side missing-report reminder; terminal diagnostics for unconfirmed results, cancellation, and final errors without parent-side wake-up logic.
11
+ - Codex Edit: preflight file/parent conflicts before writing and preserve ordinary file permissions on moves.
12
+ - OpenAI Account Pool: classify forced-refresh failures consistently and prohibit authentication repair after output delivery.
13
+ - Worktree: return the worktree root from already-active nested directories.
14
+ - web-browser: refuse profile mutation while Chrome is active, including another port or missing launcher state.
15
+
1
16
  ## 2026-10-23 - v0.13.7
2
17
 
3
18
  - openai-account-pool v0.1.8: fix stranded account-pool references after removal and recover email labels
package/README.md CHANGED
@@ -58,7 +58,7 @@ Root and child versions remain independent, but repository release tooling publi
58
58
 
59
59
  ### `/btw`
60
60
 
61
- `/btw` opens a separate side-chat session that can inspect the main conversation and repository without entering the main context. Closing it can optionally inject a summary. Use `Up` / `Down` for line scrolling and `PageUp` / `PageDown` for page scrolling.
61
+ `/btw` opens a separate side-chat session that can inspect the main conversation and repository without entering the main context. Main and restored side history are seeded through SessionManager; subsequent questions reuse the side session. Closing it can optionally inject a summary. Use `Up` / `Down` for line scrolling and `PageUp` / `PageDown` for page scrolling.
62
62
 
63
63
  ### Global instructions and theme
64
64
 
@@ -105,3 +105,7 @@ npm run release:check -- v0.13.1 --timeout 900 --interval 15
105
105
  Omit the tag to use the current root `package.json` version. Requires Node.js, Git, npm, tar, authenticated GitHub CLI (`gh auth login`), and local release tags (`git fetch origin --tags` if needed).
106
106
 
107
107
  The read-only check verifies remote tag commits, waits for the matching `publish.yml` run to succeed, and checks exact public npm versions and tarball integrity. It compares every packaged file and mode against an isolated snapshot of the release tag, including an optional coordinated child release. It uses the tag rather than uncommitted checkout changes, never installs or publishes, and removes temporary downloads. Failures exit nonzero; only a pending workflow or npm `E404` is retried within the shared timeout (600 seconds by default). Local packing uses `--ignore-scripts`, matching this repository's source-only packages; packages that add generated prepack output will need this check updated.
108
+
109
+ ## Safety and evidence boundaries
110
+
111
+ Cache Export reports provider/Pi usage, not measured server cache state. Main requests and auxiliary calls are separate; missing reasoning or retention remains unknown. Relative paths use the session cwd. Herdr children get at most one missing-report reminder and report unconfirmed outcomes explicitly. Codex Edit preflights path hierarchy conflicts and preserves executable bits on moves. Account-pool transient refresh errors never justify rotation, and delivered output forbids replay. Worktree reuse always returns the worktree root. Browser profile reset requires Chrome to be stopped.
@@ -12,7 +12,7 @@ import {
12
12
  type ModelRuntime,
13
13
  type ResourceLoader,
14
14
  } from "@earendil-works/pi-coding-agent";
15
- import { type AssistantMessage, type Message, type ThinkingLevel as AiThinkingLevel } from "@earendil-works/pi-ai";
15
+ import { type AssistantMessage, type ThinkingLevel as AiThinkingLevel } from "@earendil-works/pi-ai";
16
16
  import {
17
17
  Container,
18
18
  Input,
@@ -140,15 +140,8 @@ function getLastAssistantMessage(session: AgentSession): AssistantMessage | null
140
140
  return null;
141
141
  }
142
142
 
143
- function buildSeedMessages(ctx: ExtensionContext, thread: BtwDetails[]): Message[] {
144
- const seed: Message[] = [];
145
-
146
- try {
147
- const contextMessages = buildSessionContext(ctx.sessionManager.getEntries(), ctx.sessionManager.getLeafId()).messages;
148
- seed.push(...(contextMessages.filter((message) => "role" in message) as Message[]));
149
- } catch {
150
- // Ignore context seed failures and continue with an empty side thread.
151
- }
143
+ function buildSeedMessages(ctx: ExtensionContext, thread: BtwDetails[]): ReturnType<typeof buildSessionContext>["messages"] {
144
+ const seed = buildSessionContext(ctx.sessionManager.getEntries(), ctx.sessionManager.getLeafId()).messages;
152
145
 
153
146
  for (const item of thread) {
154
147
  seed.push(
@@ -631,8 +624,11 @@ export default function (pi: ExtensionAPI) {
631
624
  return null;
632
625
  }
633
626
 
627
+ const sessionManager = SessionManager.inMemory(ctx.cwd);
628
+ for (const message of buildSeedMessages(ctx, thread)) sessionManager.appendMessage(message);
634
629
  const { session } = await createAgentSession({
635
- sessionManager: SessionManager.inMemory(),
630
+ cwd: ctx.cwd,
631
+ sessionManager,
636
632
  model: ctx.model,
637
633
  modelRuntime: getParentModelRuntime(ctx),
638
634
  thinkingLevel: pi.getThinkingLevel() as SessionThinkingLevel,
@@ -640,11 +636,6 @@ export default function (pi: ExtensionAPI) {
640
636
  resourceLoader: createBtwResourceLoader(ctx),
641
637
  });
642
638
 
643
- const seedMessages = buildSeedMessages(ctx, thread);
644
- if (seedMessages.length > 0) {
645
- session.agent.state.messages = seedMessages as typeof session.agent.state.messages;
646
- }
647
-
648
639
  const unsubscribe = session.subscribe((event: AgentSessionEvent) => {
649
640
  if (!sideBusy || !pendingQuestion) {
650
641
  return;
@@ -0,0 +1,78 @@
1
+ // Exercise the real SessionManager projection and actual provider input.
2
+ import "../../../packages/background-tasks/tests/runtime.mjs";
3
+ import assert from "node:assert/strict";
4
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { join } from "node:path";
7
+ const { createAgentSession, DefaultResourceLoader, ModelRuntime, SessionManager, SettingsManager } = await import("@earendil-works/pi-coding-agent");
8
+ const { createAssistantMessageEventStream } = await import("@earendil-works/pi-ai");
9
+ const { default: btw } = await import("../index.ts");
10
+
11
+ const root = mkdtempSync(join(tmpdir(), "btw-context-"));
12
+ const originalAgentDir = process.env.PI_CODING_AGENT_DIR;
13
+ const originalCwd = process.cwd();
14
+ process.chdir(root);
15
+ process.env.PI_CODING_AGENT_DIR = root;
16
+ const settings = { retry: { enabled: false }, compaction: { enabled: false }, cacheWarming: "off" };
17
+ writeFileSync(join(root, "settings.json"), JSON.stringify(settings));
18
+ const runtime = await ModelRuntime.create({ authPath: join(root, "auth.json"), modelsPath: join(root, "models.json"), refreshOnCreate: false });
19
+ const requests = [];
20
+ const sessions = new Set();
21
+ runtime.registerProvider("btw-fixture", {
22
+ name: "Offline fixture", api: "btw-fixture", apiKey: "not-secret", baseUrl: "http://127.0.0.1:1",
23
+ models: [{ id: "offline", name: "Offline", reasoning: false, input: ["text"], contextWindow: 128000, maxTokens: 4096, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } }],
24
+ streamSimple(model, context) {
25
+ requests.push(structuredClone(context));
26
+ const answer = `ANSWER_${requests.length}`;
27
+ const stream = createAssistantMessageEventStream();
28
+ queueMicrotask(() => {
29
+ const message = { role: "assistant", api: model.api, provider: model.provider, model: model.id, timestamp: Date.now(), content: [{ type: "text", text: answer }], stopReason: "stop", usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0, totalTokens: 2, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } } };
30
+ stream.push({ type: "start", partial: message });
31
+ stream.push({ type: "done", reason: "stop", message });
32
+ });
33
+ return stream;
34
+ },
35
+ });
36
+ async function create(manager) {
37
+ const settingsManager = SettingsManager.inMemory(settings);
38
+ const loader = new DefaultResourceLoader({ cwd: root, agentDir: root, settingsManager, noExtensions: true, noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true, extensionFactories: [btw] });
39
+ await loader.reload();
40
+ const { session, extensionsResult } = await createAgentSession({ cwd: root, agentDir: root, modelRuntime: runtime, model: runtime.getModel("btw-fixture", "offline"), thinkingLevel: "off", resourceLoader: loader, sessionManager: manager, settingsManager, tools: [] });
41
+ assert.deepEqual(extensionsResult.errors, []);
42
+ sessions.add(session);
43
+ await session.bindExtensions({});
44
+ return session;
45
+ }
46
+ async function dispose(session) {
47
+ await session.abort();
48
+ await session.extensionRunner.emit({ type: "session_shutdown", reason: "quit" });
49
+ session.dispose();
50
+ sessions.delete(session);
51
+ }
52
+ try {
53
+ const manager = SessionManager.inMemory(root);
54
+ const parent = await create(manager);
55
+ await parent.prompt("MAIN_CONTEXT");
56
+ const mainMessages = structuredClone(parent.messages);
57
+ await parent.prompt("/btw SIDE_FIRST");
58
+ assert.equal(requests.length, 2);
59
+ for (const text of ["MAIN_CONTEXT", "ANSWER_1", "SIDE_FIRST"]) assert.ok(JSON.stringify(requests[1].messages).includes(text), `first BTW request lost ${text}`);
60
+ await parent.prompt("/btw SIDE_SECOND");
61
+ assert.equal(requests.length, 3);
62
+ for (const text of ["MAIN_CONTEXT", "SIDE_FIRST", "ANSWER_2", "SIDE_SECOND"]) assert.ok(JSON.stringify(requests[2].messages).includes(text), `continuous side session lost ${text}`);
63
+ assert.deepEqual(parent.messages, mainMessages, "side messages must stay outside the main context");
64
+ await dispose(parent);
65
+ const restored = await create(manager);
66
+ await restored.prompt("/btw SIDE_RESTORED");
67
+ assert.equal(requests.length, 4);
68
+ for (const text of ["MAIN_CONTEXT", "ANSWER_1", "SIDE_FIRST", "ANSWER_2", "SIDE_SECOND", "ANSWER_3", "SIDE_RESTORED"]) {
69
+ assert.ok(JSON.stringify(requests[3].messages).includes(text), `restored BTW request lost ${text}`);
70
+ }
71
+ console.log("PASS BTW provider input: main history, continuous side session, restored side history, main-context isolation");
72
+ } finally {
73
+ for (const session of sessions) await dispose(session);
74
+ if (originalAgentDir === undefined) delete process.env.PI_CODING_AGENT_DIR;
75
+ else process.env.PI_CODING_AGENT_DIR = originalAgentDir;
76
+ process.chdir(originalCwd);
77
+ rmSync(root, { recursive: true, force: true });
78
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-toolkit",
3
- "version": "0.13.7",
3
+ "version": "0.13.9",
4
4
  "description": "Personal pi coding agent toolkit: background tasks, scheduler, /btw, /cache_export, apply_patch, browser automation, and bundled theme/skills.",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -61,8 +61,9 @@
61
61
  "ws": "^8.19.0"
62
62
  },
63
63
  "scripts": {
64
- "test": "node tests/package-layout.mjs && node tests/release-check.mjs && node tests/post-release-check.mjs && npm --prefix packages/todo test && npm --prefix packages/cache-export test && npm --prefix packages/codex-edit test && npm --prefix packages/herdr-subagents test && npm --prefix packages/openai-account-pool test && npm --prefix packages/scheduler test && npm --prefix packages/background-tasks test && npm --prefix packages/worktree test && node --experimental-strip-types extensions/btw/tests/history.mjs",
65
- "test:root": "node tests/package-layout.mjs && node tests/release-check.mjs && node tests/post-release-check.mjs && node --experimental-strip-types extensions/btw/tests/history.mjs",
64
+ "test": "node tests/package-layout.mjs && node tests/release-check.mjs && node tests/post-release-check.mjs && npm --prefix packages/todo test && npm --prefix packages/cache-export test && npm --prefix packages/codex-edit test && npm --prefix packages/herdr-subagents test && npm --prefix packages/openai-account-pool test && npm --prefix packages/scheduler test && npm --prefix packages/background-tasks test && npm --prefix packages/worktree test && node --experimental-strip-types extensions/btw/tests/history.mjs && node --experimental-strip-types extensions/btw/tests/context.mjs",
65
+ "test:web-browser": "node tests/web-browser.mjs",
66
+ "test:root": "node tests/package-layout.mjs && node tests/release-check.mjs && node tests/post-release-check.mjs && node --experimental-strip-types extensions/btw/tests/history.mjs && node --experimental-strip-types extensions/btw/tests/context.mjs",
66
67
  "load-test": "pi -ne -e ./packages/cache-export/extensions/cache-export.ts && pi -ne -e ./extensions/btw/index.ts && pi -ne -e ./packages/codex-edit/extensions/codex-edit.ts && pi -ne -e ./packages/herdr-subagents/extensions/herdr-subagents.ts && pi -ne -e ./packages/scheduler/extensions/scheduler.ts && pi -ne -e ./packages/background-tasks/extensions/background-tasks.ts && pi -ne -e ./packages/worktree/extensions/worktree.ts && npm --prefix packages/background-tasks run load-test -- --toolkit",
67
68
  "load-test:root": "pi -ne -e ./extensions/btw/index.ts",
68
69
  "release:check": "node scripts/post-release-check.mjs",
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-10-23 - v0.1.1
4
+
5
+ - correct request timing, usage scope and historical cache evidence
6
+ - Keep mixed historical TTLs and partial one-hour writes unknown at request scope; a one-hour subset must not override mixed-policy evidence.
7
+
8
+ - Show main-request usage separately from completed cache-warm, compaction, and other auxiliary usage.
9
+ - Use message timestamps and response durations; remove universal TTL and inferred expiry propagation. Retention references are historical observations, never server expiry measurements.
10
+ - Track context edits, label segment cumulative rates, and show unreported reasoning as Unknown without plotting zero.
11
+ - Persist allowlisted cache parameters and model-policy references without prompts, cache keys, headers, URLs, or credentials.
12
+ - Resolve relative export paths against the session cwd; add deterministic, native Pi, and real Chrome regressions.
13
+
3
14
  ## 2026-09-30 - v0.1.0
4
15
 
5
16
  - 从 `@maxiaochao/pi-toolkit` 提取 cache export 权威源码,支持主包捆绑和独立安装。
@@ -68,3 +68,17 @@ npm --prefix packages/cache-export test
68
68
  npm --prefix packages/cache-export run load-test
69
69
  npm pack ./packages/cache-export --dry-run
70
70
  ```
71
+
72
+ ## 数据与证据口径
73
+
74
+ 主曲线只包含当前分支的主对话 assistant usage。保温、压缩等辅助 usage 单独列出,不计入主曲线或主对话合计。保温只认已完成的 `usage/cache_warm` 记录,不把准备事件当成功。
75
+
76
+ 命中率是服务商/Pi 报告的 `cacheRead / (input + cacheRead + cacheWrite)`,不是服务端缓存状态探针。横轴是请求序号;模型切换、压缩和 `context_edit` 等上下文变更会分段,累计曲线标为 segment cumulative。分段首条不等于会话冷启动。
77
+
78
+ 请求开始取 `message.timestamp`,响应耗时取 `durationMs`;落盘时间另列,不充当请求开始或空闲时间。未报告的时间、reasoning、保留期显示 Unknown。
79
+
80
+ 扩展从启用后的 `before_provider_request` 观察已知 retention/TTL 枚举和模型 `promptCache` 参考值,在对应 assistant 消息结束时写入 `cache-export-request` 自定义记录。只存非秘密白名单字段;不存提示词、cache key、headers、URL 或凭据。不用当前配置补历史;hook 之后其他扩展仍可能修改请求。这些值只表示观察到的请求/策略参考,不证明服务端采用、续期或到期。混合 TTL、缺字段或未记录历史保持未知,不套用通用 5 分钟,也不把 OpenAI 公共 API 的期限直接套给 Codex 订阅接口。
81
+
82
+ 已观察 usage/上下文事件、策略参考和可能原因分开表述。Pi 自带的缓存推断不是服务端真值,也不是本报告的校准标准。
83
+
84
+ 相对输出路径以当前 Pi session 的 cwd 为基准(包括切换后的 worktree)。
@@ -9,9 +9,15 @@
9
9
 
10
10
  ## 用例数据
11
11
 
12
- - 47 个确定性断言:命中率阈值、TTL、cache rebuild、模型切换、compaction、segment 和退化输入。
13
- - 18 个浏览器断言:三张图的 tooltip、断点两侧、全历史视图、segment 全局请求号和浏览器异常。
12
+ - 47 个基础确定性断言:命中率阈值、保留期未知、可能原因、模型切换、compaction、segment 和退化输入。
13
+ - `tests/evidence.mjs`:慢响应不冒充空闲、历史策略缺失/混合 TTL、不传播推算到期、真实保温 usage、context_edit、reasoning 未知、白名单无秘密、相对 cwd。
14
+ - `tests/extension.mjs`:真实 Pi SDK、离线 provider 的 payload hook、assistant 关联和 session-relative 导出。用 `PI_TEST_BINARY` 选 Pi 1.4.0。
15
+ - 21 个浏览器断言:原有 tooltip/断点/segment 验证,加真实 CDP 点击和键盘切换、主/辅助范围标签、Unknown reasoning 不画零线或零点。`CACHE_EXPORT_SCREENSHOT_DIR` 可保留真实截图。
14
16
 
15
17
  ## 最近验证结果
16
18
 
17
19
  迁移前基线为 `47 passed, 0 failed` 和 `18 passed, 0 failed, 0 skipped`。找不到 Chrome 时 E2E 会明确报告 skip;发布验收要求可用 Chrome 的环境中 0 skip。
20
+
21
+ 验证依据是输入 usage 与渲染结果、真实 SDK 事件和 Chrome 行为;不是 Pi 自带缓存推断,更不是对服务端实际 TTL 的校准。
22
+
23
+ 混合 TTL 回归同时包含 `cacheWrite1h` 为零、部分写入及全部写入的情况;所有已知混合策略均保持请求级 Unknown。只有写入计数揭示混合、没有 payload 历史时也不得推断为单一 1h。
Binary file
@@ -60,6 +60,47 @@ function defaultOutputPath(ctx) {
60
60
  }
61
61
 
62
62
  export default function (pi) {
63
+ let observed;
64
+ pi.on("turn_start", () => { observed = undefined; });
65
+ pi.on("agent_end", () => { observed = undefined; });
66
+ pi.on("session_start", () => { observed = undefined; });
67
+ pi.on("before_provider_request", (event, ctx) => {
68
+ if (!ctx.model) return;
69
+ const payload = event.payload ?? {};
70
+ const explicitTtlsSeconds = new Set();
71
+ const controls = [
72
+ payload.cache_control,
73
+ ...(Array.isArray(payload.system) ? payload.system.map((block) => block?.cache_control) : []),
74
+ ...(Array.isArray(payload.tools) ? payload.tools.map((tool) => tool?.cache_control) : []),
75
+ ...(Array.isArray(payload.messages) ? payload.messages.flatMap((message) =>
76
+ Array.isArray(message?.content) ? message.content.map((block) => block?.cache_control) : []) : []),
77
+ ];
78
+ for (const control of controls) {
79
+ if (control?.type === "ephemeral" && control.ttl === "1h") explicitTtlsSeconds.add(3600);
80
+ if (control?.type === "ephemeral" && control.ttl === "5m") explicitTtlsSeconds.add(300);
81
+ }
82
+ const retention = ["in_memory", "24h"].includes(payload.prompt_cache_retention) ? payload.prompt_cache_retention : null;
83
+ if (retention === "24h") explicitTtlsSeconds.add(86400);
84
+ const modelReference = {};
85
+ for (const tier of ["short", "long"]) {
86
+ const seconds = ctx.model.promptCache?.[tier];
87
+ if (Number.isFinite(seconds) && seconds > 0) modelReference[tier] = seconds;
88
+ }
89
+ // Allowlist only. Never persist prompts, headers, URLs, cache keys, or tokens.
90
+ observed = {
91
+ provider: ctx.model.provider, model: ctx.model.id, api: ctx.model.api,
92
+ observedAt: Date.now(),
93
+ policy: { retention, explicitTtlsSeconds: [...explicitTtlsSeconds], modelReference },
94
+ };
95
+ });
96
+ pi.on("message_end", (event) => {
97
+ const message = event.message;
98
+ if (message?.role !== "assistant") return;
99
+ if (observed && observed.provider === message.provider && observed.model === message.model && observed.api === message.api) {
100
+ pi.appendEntry("cache-export-request", { ...observed, messageTimestamp: message.timestamp });
101
+ }
102
+ observed = undefined;
103
+ });
63
104
  pi.registerCommand("cache_export", {
64
105
  description: "Export an interactive cache dashboard (latest Context segment by default, all-history switch, per-request charts)",
65
106
  handler: async (args, ctx) => {
@@ -77,13 +118,13 @@ export default function (pi) {
77
118
  const pathArg = tokens.find((t) => !t.startsWith("-"));
78
119
 
79
120
  const data = parseEntries(entries);
80
- if (data.requests.length === 0) {
81
- ctx.ui.notify("No assistant requests with usage data yet.", "error");
121
+ if (data.requests.length === 0 && data.auxiliary.length === 0) {
122
+ ctx.ui.notify("No main or auxiliary usage data yet.", "error");
82
123
  return;
83
124
  }
84
125
  const info = ctx.sessionManager;
85
126
  const sessionId = info.getSessionId ? info.getSessionId() : undefined;
86
- const outPath = resolve(pathArg || defaultOutputPath(ctx));
127
+ const outPath = pathArg ? resolve(ctx.cwd, pathArg) : defaultOutputPath(ctx);
87
128
 
88
129
  mkdirSync(dirname(outPath), { recursive: true });
89
130
  writeFileSync(outPath, buildCacheExportHtml(data, { title: "cache export", sessionId }), "utf8");
@@ -1,22 +1,40 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-cache-export",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Interactive cache usage dashboard export for Pi sessions.",
5
5
  "type": "module",
6
- "keywords": ["pi-package", "pi-extension", "cache", "dashboard"],
7
- "files": ["extensions", "src", "docs", "README.md", "TESTING.md", "CHANGELOG.md"],
6
+ "keywords": [
7
+ "pi-package",
8
+ "pi-extension",
9
+ "cache",
10
+ "dashboard"
11
+ ],
12
+ "files": [
13
+ "extensions",
14
+ "src",
15
+ "docs",
16
+ "README.md",
17
+ "TESTING.md",
18
+ "CHANGELOG.md"
19
+ ],
8
20
  "repository": {
9
21
  "type": "git",
10
22
  "url": "git+https://github.com/chaochaoxiaochao/pi-toolkit.git",
11
23
  "directory": "packages/cache-export"
12
24
  },
13
- "pi": { "extensions": ["./extensions/cache-export.ts"] },
25
+ "pi": {
26
+ "extensions": [
27
+ "./extensions/cache-export.ts"
28
+ ]
29
+ },
14
30
  "peerDependencies": {
15
31
  "@earendil-works/pi-coding-agent": "*"
16
32
  },
17
33
  "scripts": {
18
- "test": "node --experimental-strip-types tests/run-tests.mjs && node tests/e2e-browser.mjs",
34
+ "test": "node --experimental-strip-types tests/run-tests.mjs && node --experimental-strip-types tests/evidence.mjs && node --experimental-strip-types tests/extension.mjs && node tests/e2e-browser.mjs",
19
35
  "load-test": "pi -ne -e ./extensions/cache-export.ts"
20
36
  },
21
- "publishConfig": { "access": "public" }
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
22
40
  }