@ia-ccun/code-agent-cli 0.0.52 → 0.0.53-beta.2

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 (89) hide show
  1. package/bin/cli.js +97 -69
  2. package/config/agent/bin/README.md +102 -0
  3. package/config/agent/extensions/aicode-advisor.ts +197 -61
  4. package/config/agent/extensions/aicode-banner.ts +101 -0
  5. package/config/agent/extensions/aicode-btw.ts +677 -183
  6. package/config/agent/extensions/aicode-cd.ts +1017 -0
  7. package/config/agent/extensions/aicode-grillMe.ts +198 -0
  8. package/config/agent/extensions/aicode-pets.ts +351 -0
  9. package/config/agent/extensions/aicode-plan.ts +2 -0
  10. package/config/agent/extensions/aicode-safety-guard.ts +555 -0
  11. package/config/agent/extensions/aicode-tips.ts +69 -30
  12. package/config/agent/extensions/aicode-todo-progress.ts +547 -0
  13. package/config/agent/extensions/context-monitor.ts +30 -15
  14. package/config/agent/extensions/context.ts +132 -60
  15. package/config/agent/extensions/custom-footer.ts +0 -5
  16. package/config/agent/extensions/custom.ts +119 -109
  17. package/config/agent/extensions/error-monitor.ts +38 -51
  18. package/config/agent/extensions/goal.ts +8 -6
  19. package/config/agent/extensions/msg-debug.ts +103 -112
  20. package/config/agent/extensions/ponytail.ts +441 -0
  21. package/config/agent/extensions/review.ts +365 -164
  22. package/config/agent/extensions/tps.ts +87 -0
  23. package/config/agent/extensions/working-msg.ts +164 -51
  24. package/config/agent/help.md +0 -7
  25. package/config/agent/models.json +7 -7
  26. package/config/agent/settings.json +2 -1
  27. package/config/agent/skills/chroma/LICENSE +21 -0
  28. package/config/agent/skills/chroma/chroma-local/SKILL.md +165 -0
  29. package/config/agent/skills/chroma/chroma-local/cli.md +48 -0
  30. package/config/agent/skills/chroma/chroma-local/data-model.md +103 -0
  31. package/config/agent/skills/chroma/chroma-local/error-handling/python.md +166 -0
  32. package/config/agent/skills/chroma/chroma-local/error-handling/typescript.md +231 -0
  33. package/config/agent/skills/chroma/chroma-local/local-chroma/python.md +95 -0
  34. package/config/agent/skills/chroma/chroma-local/local-chroma/typescript.md +101 -0
  35. package/config/agent/skills/chroma/chroma-local/metadata/python.md +154 -0
  36. package/config/agent/skills/chroma/chroma-local/metadata/typescript.md +153 -0
  37. package/config/agent/skills/chroma/chroma-local/querying/python.md +147 -0
  38. package/config/agent/skills/chroma/chroma-local/querying/typescript.md +157 -0
  39. package/config/agent/skills/chroma/chroma-local/regex/python.md +69 -0
  40. package/config/agent/skills/chroma/chroma-local/regex/typescript.md +71 -0
  41. package/config/agent/skills/chroma/chroma-local/understanding-a-codebase.md +80 -0
  42. package/config/agent/skills/chroma/chroma-local/updating-deleting/python.md +159 -0
  43. package/config/agent/skills/chroma/chroma-local/updating-deleting/typescript.md +228 -0
  44. package/config/agent/skills/milvus-skill/LICENSE +202 -0
  45. package/config/agent/skills/milvus-skill/README.md +37 -0
  46. package/config/agent/skills/milvus-skill/SKILL.md +259 -0
  47. package/config/agent/skills/milvus-skill/references/collection.md +86 -0
  48. package/config/agent/skills/milvus-skill/references/database.md +24 -0
  49. package/config/agent/skills/milvus-skill/references/index.md +69 -0
  50. package/config/agent/skills/milvus-skill/references/partition.md +29 -0
  51. package/config/agent/skills/milvus-skill/references/patterns.md +150 -0
  52. package/config/agent/skills/milvus-skill/references/user-role.md +90 -0
  53. package/config/agent/skills/milvus-skill/references/vector.md +247 -0
  54. package/config/agent/skills/ponytail/SKILL.md +100 -0
  55. package/config/agent/skills/ponytail-audit/SKILL.md +40 -0
  56. package/config/agent/skills/ponytail-debt/SKILL.md +44 -0
  57. package/config/agent/skills/ponytail-help/SKILL.md +68 -0
  58. package/config/agent/skills/ponytail-review/SKILL.md +56 -0
  59. package/config/agent/skills/zread/README.md +115 -0
  60. package/config/agent/skills/zread/SKILL.md +103 -0
  61. package/config/agent/skills/zread/references/stdio-protocol.md +312 -0
  62. package/dist/banner.d.ts.map +1 -1
  63. package/dist/banner.js +8 -1
  64. package/dist/banner.js.map +1 -1
  65. package/dist/config-loader.d.ts.map +1 -1
  66. package/dist/config-loader.js +9 -2
  67. package/dist/config-loader.js.map +1 -1
  68. package/package.json +7 -7
  69. package/scripts/postinstall.js +58 -1
  70. package/scripts/regression/README.md +224 -0
  71. package/scripts/regression/cases/01-version-and-deps.sh +94 -0
  72. package/scripts/regression/cases/02-extension-loading.sh +130 -0
  73. package/scripts/regression/cases/03-rpc-protocol.sh +259 -0
  74. package/scripts/regression/cases/04-prompt-stream.sh +130 -0
  75. package/scripts/regression/cases/05-slash-commands-and-skills.sh +119 -0
  76. package/scripts/regression/cases/06-tool-execution.sh +115 -0
  77. package/scripts/regression/cases/07-pi-new-features.sh +141 -0
  78. package/scripts/regression/lib/common.sh +287 -0
  79. package/scripts/regression/results/.last_pid +1 -0
  80. package/scripts/regression/results/.last_workdir +1 -0
  81. package/scripts/regression/results/02-extension-loading.log +18 -0
  82. package/scripts/regression/results/03-prompt-stream.log +16 -0
  83. package/scripts/regression/results/03-rpc-protocol.log +27 -0
  84. package/scripts/regression/results/04-slash-commands-and-skills.log +11 -0
  85. package/scripts/regression/results/05-tool-execution.log +8 -0
  86. package/scripts/regression/results/06-pi-new-features.log +29 -0
  87. package/scripts/regression/run-all.sh +187 -0
  88. package/config/agent/dangerous-patterns.json +0 -106
  89. package/config/agent/extensions/bash-check.ts +0 -227
@@ -22,7 +22,9 @@ function formatBeijingTime(date: Date = new Date()): string {
22
22
 
23
23
  // 格式化为文件名日期字符串 (北京时间)
24
24
  function getLogFileName(date: Date = new Date()): string {
25
- const beijingDate = new Date(date.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }));
25
+ const beijingDate = new Date(
26
+ date.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
27
+ );
26
28
  const year = beijingDate.getFullYear();
27
29
  const month = String(beijingDate.getMonth() + 1).padStart(2, "0");
28
30
  const day = String(beijingDate.getDate()).padStart(2, "0");
@@ -34,7 +36,9 @@ function cleanOldLogs(): void {
34
36
  if (!fs.existsSync(ERROR_LOG_DIR)) return;
35
37
 
36
38
  const files = fs.readdirSync(ERROR_LOG_DIR);
37
- const now = new Date(new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }));
39
+ const now = new Date(
40
+ new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
41
+ );
38
42
 
39
43
  for (const file of files) {
40
44
  if (!file.startsWith("aicode-error-") || !file.endsWith(".log")) continue;
@@ -43,8 +47,14 @@ function cleanOldLogs(): void {
43
47
  const match = file.match(/aicode-error-(\d{4})-(\d{2})-(\d{2})\.log/);
44
48
  if (!match) continue;
45
49
 
46
- const fileDate = new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]));
47
- const diffDays = Math.floor((now.getTime() - fileDate.getTime()) / (1000 * 60 * 60 * 24));
50
+ const fileDate = new Date(
51
+ parseInt(match[1]),
52
+ parseInt(match[2]) - 1,
53
+ parseInt(match[3]),
54
+ );
55
+ const diffDays = Math.floor(
56
+ (now.getTime() - fileDate.getTime()) / (1000 * 60 * 60 * 24),
57
+ );
48
58
 
49
59
  if (diffDays > KEEP_DAYS) {
50
60
  fs.unlinkSync(path.join(ERROR_LOG_DIR, file));
@@ -63,7 +73,10 @@ function ensureLogDir(): void {
63
73
  * @author: xujianjiang
64
74
  * Append error message to log file
65
75
  */
66
- function appendToErrorLog(message: string, context?: Record<string, unknown>): void {
76
+ function appendToErrorLog(
77
+ message: string,
78
+ context?: Record<string, unknown>,
79
+ ): void {
67
80
  ensureLogDir();
68
81
  cleanOldLogs(); // 每次写日志时清理过期文件
69
82
  const timestamp = formatBeijingTime();
@@ -80,64 +93,38 @@ export default function (pi: ExtensionAPI) {
80
93
  // Counter for error tracking
81
94
  let toolErrorCount = 0;
82
95
  let bashErrorCount = 0;
83
- const startTime = Date.now();
84
-
85
96
  // Monitor tool execution end for errors
86
97
  pi.on("tool_execution_end", (event) => {
87
98
  const { toolCallId, toolName, result, isError } = event;
88
99
 
89
100
  if (isError) {
90
101
  toolErrorCount++;
91
- const errorMsg = typeof result === "string" ? result : JSON.stringify(result);
102
+ const errorMsg =
103
+ typeof result === "string" ? result : JSON.stringify(result);
92
104
 
93
105
  // Append to error log file
94
- appendToErrorLog(
95
- `Tool Execution Failed: ${toolName}`,
96
- {
97
- toolCallId,
98
- toolName,
99
- errorMessage: errorMsg?.substring(0, 500),
100
- }
101
- );
106
+ appendToErrorLog(`Tool Execution Failed: ${toolName}`, {
107
+ toolCallId,
108
+ toolName,
109
+ errorMessage: errorMsg?.substring(0, 500),
110
+ });
102
111
  }
103
112
  });
104
113
 
105
114
  // Monitor user bash events for command failures
115
+ // pi 1.1.0: UserBashEvent 不再含 result 字段, 只在执行前发; 实际退出码在 tool_execution_end 监听
106
116
  pi.on("user_bash", (event) => {
107
- const { command, result } = event;
108
-
109
- if (result?.exitCode !== 0) {
110
- bashErrorCount++;
111
-
112
- // Append to error log file
113
- appendToErrorLog(
114
- `Bash Command Failed: ${command} - exit code ${result?.exitCode}`,
115
- {
116
- command,
117
- exitCode: result?.exitCode,
118
- stderr: result?.stderr?.substring(0, 500),
119
- stdout: result?.stdout?.substring(0, 500),
120
- }
121
- );
122
- }
123
- });
124
-
125
- // Summary on session shutdown
126
- pi.on("session_shutdown", () => {
127
- const uptime = Math.round((Date.now() - startTime) / 1000);
128
- const totalErrors = toolErrorCount + bashErrorCount;
129
-
130
- if (totalErrors > 0) {
131
- console.log(`
132
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
133
- 📊 Error Monitor 会话统计
134
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
135
- 总错误数: ${totalErrors}
136
- 工具错误数: ${toolErrorCount}
137
- Bash 错误数: ${bashErrorCount}
138
- 运行时长: ${uptime}秒
139
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
140
- `);
141
- }
117
+ const { command, excludeFromContext, cwd } = event;
118
+ // 记录用户执行的命令(不判断成功/失败,失败已在 tool_execution_end 记录)
119
+ bashErrorCount++;
120
+ appendToErrorLog(
121
+ `Bash Command Executed: ${command}${excludeFromContext ? " (excluded from context)" : ""}`,
122
+ {
123
+ command,
124
+ cwd,
125
+ excludeFromContext,
126
+ },
127
+ );
142
128
  });
129
+ // Session shutdown - silent (errors already logged to file)
143
130
  }
@@ -1,4 +1,4 @@
1
- import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
1
+ import type { ExtensionAPI, ExtensionContext, ExtensionToolContext } from "@earendil-works/pi-coding-agent";
2
2
  import { Box, Spacer, Text } from "@earendil-works/pi-tui";
3
3
 
4
4
  const CUSTOM_TYPE = "aicode-goal";
@@ -161,8 +161,8 @@ function syncGoalTools(pi: ExtensionAPI) {
161
161
  for (const name of GOAL_TOOL_NAMES) (want ? active.add(name) : active.delete(name));
162
162
  pi.setActiveTools(Array.from(active));
163
163
  }
164
-
165
- function persist(pi: ExtensionAPI, ctx: ExtensionContext, next: GoalState | null) {
164
+ // pi 1.1.0: ExtensionToolContext (used in CustomTool.execute) extends ExtensionContext
165
+ function persist(pi: ExtensionAPI, ctx: ExtensionContext | ExtensionToolContext, next: GoalState | null) {
166
166
  goal = next;
167
167
  pi.appendEntry(CUSTOM_TYPE, { goal: next, statusBarEnabled });
168
168
  updateStatusBar(ctx);
@@ -299,11 +299,13 @@ export default function piGoal(pi: ExtensionAPI) {
299
299
  additionalProperties: false,
300
300
  } as any,
301
301
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
302
- if (params.status !== "complete") {
303
- return { content: [{ type: "text", text: "update_goal 仅接受 status=complete。" }], isError: true };
302
+ // pi 1.1.0: AgentToolResult.details is required (T = JsonValue | undefined)
303
+ // pi 1.1.0: params 在 execute 里是 unknown, 需要 narrow
304
+ if ((params as { status?: string }).status !== "complete") {
305
+ return { content: [{ type: "text", text: "update_goal 仅接受 status=complete。" }], details: undefined, isError: true };
304
306
  }
305
307
  if (!goal) {
306
- return { content: [{ type: "text", text: "未设置目标。" }], isError: true };
308
+ return { content: [{ type: "text", text: "未设置目标。" }], details: undefined, isError: true };
307
309
  }
308
310
  const now = Date.now();
309
311
  const next: GoalState = { ...goal, status: "complete", updatedAt: now };
@@ -1,19 +1,30 @@
1
1
  /**
2
2
  * @author: xujianjiang
3
3
  * Msg Debug Extension - Log LLM request/response messages to file
4
- *
4
+ *
5
5
  * This extension logs all requests and responses sent to/from the LLM provider
6
6
  * for debugging purposes. Logs are saved to ~/.aicode-cli/logs/
7
- *
7
+ *
8
8
  * Features:
9
9
  * - Logs before_provider_request: messages, parameters, raw payload
10
- * - Logs after_provider_response: usage stats, response content, stop reason
11
- * - Logs errors with context
12
- * - Console output removed (logs to file only)
10
+ * - Logs after_provider_response: HTTP status, response headers
13
11
  * - Session-based log files (one file per session)
12
+ *
13
+ * NOTE: pi 1.1.0 event payloads changed:
14
+ * - BeforeProviderRequestEvent now only has `payload: unknown` (no provider/model).
15
+ * Provider/model are read from `ctx.model` instead.
16
+ * - AfterProviderResponseEvent now only has `status: number, headers: Record<string,string>`.
17
+ * Response/usage/cached fields are gone (use `provider_stream_event` or message events).
18
+ * - The "error" event no longer exists.
14
19
  */
15
20
 
16
- import { appendFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from "node:fs";
21
+ import {
22
+ appendFileSync,
23
+ existsSync,
24
+ mkdirSync,
25
+ readdirSync,
26
+ unlinkSync,
27
+ } from "node:fs";
17
28
  import { join } from "node:path";
18
29
  import os from "node:os";
19
30
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
@@ -25,7 +36,9 @@ function formatBeijingTime(date: Date = new Date()): string {
25
36
 
26
37
  // 格式化为文件名日期字符串 (北京时间)
27
38
  function formatDateStr(date: Date = new Date()): string {
28
- const beijingDate = new Date(date.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }));
39
+ const beijingDate = new Date(
40
+ date.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
41
+ );
29
42
  const year = beijingDate.getFullYear();
30
43
  const month = String(beijingDate.getMonth() + 1).padStart(2, "0");
31
44
  const day = String(beijingDate.getDate()).padStart(2, "0");
@@ -46,19 +59,43 @@ function ensureLogDir(): void {
46
59
  // 清理过期日志文件
47
60
  function cleanOldLogs(): void {
48
61
  if (!existsSync(LOG_DIR)) return;
49
- const now = new Date(new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }));
62
+ const now = new Date(
63
+ new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }),
64
+ );
50
65
  for (const file of readdirSync(LOG_DIR)) {
51
66
  if (!file.startsWith("llm-requests-") || !file.endsWith(".log")) continue;
52
67
  const match = file.match(/llm-requests-(\d{4})-(\d{2})-(\d{2})\.log/);
53
68
  if (!match) continue;
54
- const fileDate = new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]));
55
- const diffDays = Math.floor((now.getTime() - fileDate.getTime()) / (1000 * 60 * 60 * 24));
69
+ const fileDate = new Date(
70
+ parseInt(match[1]),
71
+ parseInt(match[2]) - 1,
72
+ parseInt(match[3]),
73
+ );
74
+ const diffDays = Math.floor(
75
+ (now.getTime() - fileDate.getTime()) / (1000 * 60 * 60 * 24),
76
+ );
56
77
  if (diffDays > KEEP_DAYS) {
57
78
  unlinkSync(join(LOG_DIR, file));
58
79
  }
59
80
  }
60
81
  }
61
82
 
83
+ // 1.1.0 BeforeProviderRequestEvent.payload is `unknown`; narrow to a useful shape.
84
+ type ProviderPayload = {
85
+ messages?: Array<{ role?: string; content?: unknown }>;
86
+ max_tokens?: number;
87
+ temperature?: number;
88
+ top_p?: number;
89
+ top_k?: number;
90
+ stop_sequences?: string[];
91
+ thinking?: unknown;
92
+ budget_tokens?: number;
93
+ };
94
+
95
+ function isProviderPayload(value: unknown): value is ProviderPayload {
96
+ return typeof value === "object" && value !== null;
97
+ }
98
+
62
99
  export default function (pi: ExtensionAPI) {
63
100
  ensureLogDir();
64
101
  cleanOldLogs();
@@ -69,156 +106,110 @@ export default function (pi: ExtensionAPI) {
69
106
  // Filter to only log user and assistant messages
70
107
  const ALLOWED_ROLES = ["user", "assistant"];
71
108
 
72
- function formatMessage(message: any): string {
109
+ function formatMessage(message: { role?: string; content?: unknown }): string {
73
110
  if (!message) return "undefined";
74
-
75
111
  const role = message.role || "unknown";
76
112
  let content = "";
77
-
78
113
  if (typeof message.content === "string") {
79
114
  content = message.content;
80
115
  } else if (Array.isArray(message.content)) {
81
- content = message.content.map((item: any) => {
82
- if (item.type === "text") {
83
- return item.text || "";
84
- } else if (item.type === "image") {
85
- return `[image: ${item.source?.type || "unknown"}]`;
86
- } else if (item.type === "tool_use") {
87
- return `[tool_use: ${item.name || "unknown"}]`;
88
- } else if (item.type === "tool_result") {
89
- return `[tool_result: ${item.tool_use_id || ""}]`;
90
- }
91
- return JSON.stringify(item);
92
- }).join("\n");
116
+ content = message.content
117
+ .map((item: any) => {
118
+ if (item.type === "text") return item.text || "";
119
+ if (item.type === "image")
120
+ return `[image: ${item.source?.type || "unknown"}]`;
121
+ if (item.type === "tool_use")
122
+ return `[tool_use: ${item.name || "unknown"}]`;
123
+ if (item.type === "tool_result")
124
+ return `[tool_result: ${item.tool_use_id || ""}]`;
125
+ return JSON.stringify(item);
126
+ })
127
+ .join("\n");
93
128
  }
94
-
95
129
  return `[${role}]: ${content}`;
96
130
  }
97
131
 
98
- // Filter messages to only include user and assistant
99
- function filterMessages(messages: any[]): any[] {
132
+ function filterMessages(
133
+ messages: unknown,
134
+ ): Array<{ role?: string; content?: unknown }> {
100
135
  if (!Array.isArray(messages)) return [];
101
- return messages.filter((msg) => ALLOWED_ROLES.includes(msg.role));
136
+ return (messages as Array<{ role?: string }>).filter((msg) =>
137
+ ALLOWED_ROLES.includes(msg.role ?? ""),
138
+ );
102
139
  }
103
140
 
104
141
  // Log before request is sent
105
- pi.on("before_provider_request", (event) => {
142
+ pi.on("before_provider_request", (event, ctx) => {
106
143
  const timestamp = formatBeijingTime();
107
- const { provider, model, payload } = event;
108
-
109
- // Filter messages to only user and assistant
144
+ const payload = isProviderPayload(event.payload) ? event.payload : {};
145
+ // Provider/model are no longer on the event payload in 1.1.0; read from ctx.
146
+ const modelId = (ctx.model as { id?: string } | undefined)?.id;
147
+ const provider = modelId?.split("/")[0] ?? "unknown";
148
+ const model = modelId ?? "unknown";
149
+
110
150
  const filteredMessages = filterMessages(payload.messages);
111
- const originalCount = payload.messages?.length || 0;
112
-
151
+ const originalCount = Array.isArray(payload.messages)
152
+ ? payload.messages.length
153
+ : 0;
154
+
113
155
  const logEntry = `
114
156
  ================================================================================
115
157
  [${timestamp}] BEFORE PROVIDER REQUEST
116
158
  ================================================================================
117
- Provider: ${provider || "unknown"}
118
- Model: ${model || "unknown"}
159
+ Provider: ${provider}
160
+ Model: ${model}
119
161
  Messages: ${filteredMessages.length} (filtered from ${originalCount}, showing user/assistant only)
120
162
 
121
163
  --- Messages ---
122
- ${filteredMessages.map((msg: any, i: number) =>
123
- `[${i}] ${formatMessage(msg)}`
124
- ).join("\n\n") || "No messages"}
164
+ ${
165
+ filteredMessages
166
+ .map((msg, i: number) => `[${i}] ${formatMessage(msg)}`)
167
+ .join("\n\n") || "No messages"
168
+ }
125
169
 
126
170
  --- Request Parameters ---
127
- ${JSON.stringify({
128
- maxTokens: payload.max_tokens,
129
- temperature: payload.temperature,
130
- topP: payload.top_p,
131
- topK: payload.top_k,
132
- stopSequences: payload.stop_sequences,
133
- thinking: payload.thinking ? "enabled" : "disabled",
134
- budgetTokens: payload.budget_tokens
135
- }, null, 2)}
171
+ ${JSON.stringify(
172
+ {
173
+ maxTokens: payload.max_tokens,
174
+ temperature: payload.temperature,
175
+ topP: payload.top_p,
176
+ topK: payload.top_k,
177
+ stopSequences: payload.stop_sequences,
178
+ thinking: payload.thinking ? "enabled" : "disabled",
179
+ budgetTokens: payload.budget_tokens,
180
+ },
181
+ null,
182
+ 2,
183
+ )}
136
184
 
137
185
  --- Raw Payload (filtered) ---
138
186
  ${JSON.stringify({ ...payload, messages: filteredMessages }, null, 2)}
139
187
 
140
188
  ================================================================================
141
189
  `;
142
-
190
+
143
191
  // Save to file
144
192
  appendFileSync(logFile, logEntry, "utf8");
145
193
  });
146
194
 
147
- // Log after response is received
195
+ // Log after response is received (HTTP-level, not assistant message)
148
196
  pi.on("after_provider_response", (event) => {
149
197
  const timestamp = formatBeijingTime();
150
- const { response, usage, cached } = event;
151
-
152
- // Check if response is defined
153
- if (!response) {
154
- const logEntry = `
155
- ================================================================================
156
- [${timestamp}] AFTER PROVIDER RESPONSE
157
- ================================================================================
158
- Cached: ${cached ? "YES" : "NO"}
159
- Error: Response is undefined
198
+ const { status, headers } = event;
160
199
 
161
- ================================================================================
162
- `;
163
- appendFileSync(logFile, logEntry, "utf8");
164
- return;
165
- }
166
-
167
200
  const logEntry = `
168
201
  ================================================================================
169
202
  [${timestamp}] AFTER PROVIDER RESPONSE
170
203
  ================================================================================
171
- Cached: ${cached ? "YES" : "NO"}
172
-
173
- --- Usage Stats ---
174
- ${JSON.stringify(usage, null, 2)}
204
+ Status: ${status}
175
205
 
176
- --- Response Content ---
177
- ${response.content?.map((c: any, i: number) => {
178
- if (c.type === "text") {
179
- return `[${i}] text: ${c.text}`;
180
- } else if (c.type === "tool_use") {
181
- return `[${i}] tool_use: ${c.name}(${JSON.stringify(c.input)})`;
182
- }
183
- return `[${i}] ${c.type}: ${JSON.stringify(c)}`;
184
- }).join("\n") || "No content"}
185
-
186
- --- Stop Reason ---
187
- ${response.stopReason || "unknown"}
188
-
189
- --- Raw Response ---
190
- ${JSON.stringify(response, null, 2)}
206
+ --- Response Headers ---
207
+ ${JSON.stringify(headers, null, 2)}
191
208
 
192
209
  ================================================================================
193
-
194
210
  `;
195
-
196
- // Save to file
197
- appendFileSync(logFile, logEntry, "utf8");
198
- });
199
211
 
200
- // Log request errors
201
- pi.on("error", (event) => {
202
- const timestamp = formatBeijingTime();
203
- const { error, context } = event;
204
-
205
- const logEntry = `
206
- ################################################################################
207
- [${timestamp}] ERROR
208
- ################################################################################
209
- Error: ${error.message || String(error)}
210
- Stack: ${error.stack || "No stack trace"}
211
-
212
- Context:
213
- ${JSON.stringify(context, null, 2)}
214
-
215
- ################################################################################
216
-
217
- `;
218
-
219
212
  // Save to file
220
213
  appendFileSync(logFile, logEntry, "utf8");
221
214
  });
222
-
223
- // Extension enabled silently (logs to file only, no console output)
224
215
  }