@harmonyos-arkts/opencode-acp 0.0.7 → 0.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/CHANGELOG.md CHANGED
@@ -6,6 +6,11 @@ All notable changes to this project will be documented in this file.
6
6
 
7
7
  ### Added
8
8
 
9
+ - 日志老化机制:基于 harmony-code 的日志管理策略
10
+ - 文件大小轮转:单个日志文件超过 5MB 时自动重命名(加时间戳后缀)
11
+ - 存留期清理:启动时删除超过 7 天的日志文件
12
+ - 数量上限:最多保留 30 个日志文件
13
+ - 缓冲写入:日志条目缓冲后每 500ms 或每 50 条批量刷盘,减少 I/O 开销
9
14
  - MCP extMethod API:通过 ACP `extMethod("mcp/*")` 向客户端暴露 MCP 服务器运行时管理能力
10
15
  - `mcp/status` — 获取所有 MCP 服务器连接状态
11
16
  - `mcp/add` — 动态添加 MCP 服务器(local/remote)
package/README.md CHANGED
@@ -128,7 +128,12 @@ Options:
128
128
 
129
129
  ## Traffic Logging
130
130
 
131
- All communication is logged to `~/.harmony-acp/logs/` as JSONL files (auto-rotated, max 30 files).
131
+ All communication is logged to `~/.harmony-acp/logs/` as JSONL files with automatic rotation and aging:
132
+
133
+ - **Size rotation**: log files are renamed with a timestamp when they exceed 5MB
134
+ - **Age cleanup**: files older than 7 days are deleted on startup
135
+ - **Count cap**: at most 30 log files are retained
136
+ - **Buffered writes**: log entries are buffered and flushed every 500ms or every 50 entries for performance
132
137
 
133
138
  ### Log Categories
134
139
 
package/dist/index.cjs CHANGED
@@ -374,15 +374,15 @@ var require_readShebang = __commonJS({
374
374
  var shebangCommand = require_shebang_command();
375
375
  function readShebang(command) {
376
376
  const size = 150;
377
- const buffer = Buffer.alloc(size);
377
+ const buffer2 = Buffer.alloc(size);
378
378
  let fd;
379
379
  try {
380
380
  fd = fs.openSync(command, "r");
381
- fs.readSync(fd, buffer, 0, size, 0);
381
+ fs.readSync(fd, buffer2, 0, size, 0);
382
382
  fs.closeSync(fd);
383
383
  } catch (e) {
384
384
  }
385
- return shebangCommand(buffer.toString());
385
+ return shebangCommand(buffer2.toString());
386
386
  }
387
387
  module2.exports = readShebang;
388
388
  }
@@ -15846,7 +15846,7 @@ var createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer,
15846
15846
  if (!response.body)
15847
15847
  throw new Error("No body in SSE response");
15848
15848
  const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
15849
- let buffer = "";
15849
+ let buffer2 = "";
15850
15850
  const abortHandler = () => {
15851
15851
  try {
15852
15852
  reader.cancel();
@@ -15859,10 +15859,10 @@ var createSseClient = ({ onRequest, onSseError, onSseEvent, responseTransformer,
15859
15859
  const { done, value } = await reader.read();
15860
15860
  if (done)
15861
15861
  break;
15862
- buffer += value;
15863
- buffer = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
15864
- const chunks = buffer.split("\n\n");
15865
- buffer = chunks.pop() ?? "";
15862
+ buffer2 += value;
15863
+ buffer2 = buffer2.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
15864
+ const chunks = buffer2.split("\n\n");
15865
+ buffer2 = chunks.pop() ?? "";
15866
15866
  for (const chunk of chunks) {
15867
15867
  const lines = chunk.split("\n");
15868
15868
  const dataLines = [];
@@ -20298,9 +20298,15 @@ var import_fs = require("fs");
20298
20298
  var import_path = require("path");
20299
20299
  var import_os = require("os");
20300
20300
  var LOG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".harmony-acp", "logs");
20301
+ var MAX_LOG_SIZE_BYTES = 5 * 1024 * 1024;
20302
+ var MAX_LOG_AGE_DAYS = 7;
20301
20303
  var MAX_LOG_FILES = 30;
20304
+ var FLUSH_INTERVAL_MS = 500;
20305
+ var BUFFER_SIZE_LIMIT = 50;
20302
20306
  var logFile = "";
20303
20307
  var enabled = false;
20308
+ var buffer = [];
20309
+ var flushTimer = null;
20304
20310
  function initLogger() {
20305
20311
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
20306
20312
  logFile = (0, import_path.join)(LOG_DIR, `${ts}.log`);
@@ -20310,24 +20316,72 @@ function initLogger() {
20310
20316
  }
20311
20317
  function setLogEnabled(v) {
20312
20318
  enabled = v;
20319
+ if (!v) flush();
20313
20320
  }
20314
20321
  function cleanOldLogs() {
20315
20322
  try {
20316
- const files = (0, import_fs.readdirSync)(LOG_DIR).filter((f) => f.endsWith(".log")).map((f) => ({
20317
- name: f,
20318
- path: (0, import_path.join)(LOG_DIR, f),
20319
- mtime: (0, import_fs.statSync)((0, import_path.join)(LOG_DIR, f)).mtime.getTime()
20320
- })).sort((a, b) => b.mtime - a.mtime);
20321
- for (let i = MAX_LOG_FILES; i < files.length; i++) {
20323
+ const now = Date.now();
20324
+ const maxAgeMs = MAX_LOG_AGE_DAYS * 24 * 60 * 60 * 1e3;
20325
+ const files = (0, import_fs.readdirSync)(LOG_DIR).filter((f) => f.endsWith(".log")).map((f) => {
20326
+ const full = (0, import_path.join)(LOG_DIR, f);
20327
+ try {
20328
+ return { name: f, path: full, mtime: (0, import_fs.statSync)(full).mtime.getTime() };
20329
+ } catch {
20330
+ return null;
20331
+ }
20332
+ }).filter((e) => e !== null);
20333
+ for (const f of files) {
20334
+ if (now - f.mtime > maxAgeMs) {
20335
+ try {
20336
+ (0, import_fs.unlinkSync)(f.path);
20337
+ } catch {
20338
+ }
20339
+ }
20340
+ }
20341
+ const surviving = files.filter((f) => now - f.mtime <= maxAgeMs).sort((a, b) => b.mtime - a.mtime);
20342
+ for (let i = MAX_LOG_FILES; i < surviving.length; i++) {
20322
20343
  try {
20323
- const { unlinkSync } = require("fs");
20324
- unlinkSync(files[i].path);
20344
+ (0, import_fs.unlinkSync)(surviving[i].path);
20325
20345
  } catch {
20326
20346
  }
20327
20347
  }
20328
20348
  } catch {
20329
20349
  }
20330
20350
  }
20351
+ function rotateIfNeeded() {
20352
+ try {
20353
+ const stat = (0, import_fs.statSync)(logFile);
20354
+ if (stat.size >= MAX_LOG_SIZE_BYTES) {
20355
+ const now = /* @__PURE__ */ new Date();
20356
+ const pad = (n) => String(n).padStart(2, "0");
20357
+ const ts = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
20358
+ const baseName = logFile.replace(/\.log$/, "");
20359
+ (0, import_fs.renameSync)(logFile, `${baseName}.${ts}.log`);
20360
+ }
20361
+ } catch {
20362
+ }
20363
+ }
20364
+ function flush() {
20365
+ if (flushTimer) {
20366
+ clearTimeout(flushTimer);
20367
+ flushTimer = null;
20368
+ }
20369
+ if (buffer.length === 0) return;
20370
+ const data = buffer.join("");
20371
+ buffer = [];
20372
+ try {
20373
+ (0, import_fs.appendFileSync)(logFile, data);
20374
+ rotateIfNeeded();
20375
+ } catch {
20376
+ }
20377
+ }
20378
+ function scheduleFlush() {
20379
+ if (flushTimer) return;
20380
+ flushTimer = setTimeout(() => {
20381
+ flushTimer = null;
20382
+ flush();
20383
+ }, FLUSH_INTERVAL_MS);
20384
+ }
20331
20385
  function write(category, action, data) {
20332
20386
  if (!enabled) return;
20333
20387
  const entry = {
@@ -20336,9 +20390,11 @@ function write(category, action, data) {
20336
20390
  action,
20337
20391
  ...data && { data }
20338
20392
  };
20339
- try {
20340
- (0, import_fs.appendFileSync)(logFile, JSON.stringify(entry) + "\n");
20341
- } catch {
20393
+ buffer.push(JSON.stringify(entry) + "\n");
20394
+ if (buffer.length >= BUFFER_SIZE_LIMIT) {
20395
+ flush();
20396
+ } else {
20397
+ scheduleFlush();
20342
20398
  }
20343
20399
  }
20344
20400
  function acpIn(method, params) {
@@ -20366,9 +20422,15 @@ function sanitize(obj, depth = 0) {
20366
20422
  } else if (val && typeof val === "object" && !Array.isArray(val)) {
20367
20423
  result[key] = sanitize(val, depth + 1);
20368
20424
  } else if (val && Array.isArray(val)) {
20369
- result[key] = val.map(
20370
- (item) => typeof item === "string" && item.length > 2e3 ? item.slice(0, 2e3) + `... [${item.length} chars total]` : item && typeof item === "object" ? sanitize(item, depth + 1) : item
20371
- );
20425
+ result[key] = val.map((item) => {
20426
+ if (typeof item === "string" && item.length > 2e3) {
20427
+ return item.slice(0, 2e3) + `... [${item.length} chars total]`;
20428
+ }
20429
+ if (item && typeof item === "object") {
20430
+ return sanitize(item, depth + 1);
20431
+ }
20432
+ return item;
20433
+ });
20372
20434
  } else {
20373
20435
  result[key] = val;
20374
20436
  }
@@ -20406,12 +20468,12 @@ function toLocations(toolName, input) {
20406
20468
  case "read":
20407
20469
  case "edit":
20408
20470
  case "write":
20409
- return input["filePath"] ? [{ path: input["filePath"] }] : [];
20471
+ return input.filePath ? [{ path: input.filePath }] : [];
20410
20472
  case "glob":
20411
20473
  case "grep":
20412
- return input["path"] ? [{ path: input["path"] }] : [];
20474
+ return input.path ? [{ path: input.path }] : [];
20413
20475
  case "list":
20414
- return input["path"] ? [{ path: input["path"] }] : [];
20476
+ return input.path ? [{ path: input.path }] : [];
20415
20477
  default:
20416
20478
  return [];
20417
20479
  }
@@ -20436,18 +20498,30 @@ function parseUri(uri) {
20436
20498
  return { type: "text", text: uri };
20437
20499
  }
20438
20500
  }
20501
+ function buildUpdateLogPayload(updateType, update) {
20502
+ if (updateType === "agent_message_chunk" || updateType === "agent_thought_chunk") {
20503
+ return { messageId: update.messageId?.slice(0, 12), delta: update.content?.text?.slice(0, 200) };
20504
+ }
20505
+ if (updateType === "tool_call" || updateType === "tool_call_update") {
20506
+ return { toolCallId: update.toolCallId, tool: update.title, kind: update.kind, status: update.status };
20507
+ }
20508
+ if (updateType === "session_info_update") {
20509
+ return { title: update.title, _meta: update._meta };
20510
+ }
20511
+ if (updateType === "usage_update") {
20512
+ return { used: update.used, size: update.size, cost: update.cost, _meta: update._meta, error: update._meta?.error?.message };
20513
+ }
20514
+ if (update._meta?.error) {
20515
+ return { error: update._meta.error?.message ?? update._meta.error };
20516
+ }
20517
+ return {};
20518
+ }
20439
20519
  async function sendToClient(connection, params) {
20440
20520
  const updateType = params.update.sessionUpdate;
20441
20521
  const update = params.update;
20442
20522
  acpOut(`sessionUpdate.${updateType}`, {
20443
20523
  sessionId: params.sessionId.slice(0, 12),
20444
- ...updateType === "agent_message_chunk" || updateType === "agent_thought_chunk" ? { messageId: update.messageId?.slice(0, 12), delta: update.content?.text?.slice(0, 200) } : updateType === "tool_call" || updateType === "tool_call_update" ? { toolCallId: update.toolCallId, tool: update.title, kind: update.kind, status: update.status } : updateType === "session_info_update" ? { title: update.title, _meta: update._meta } : updateType === "usage_update" ? {
20445
- used: update.used,
20446
- size: update.size,
20447
- cost: update.cost,
20448
- _meta: update._meta,
20449
- error: update._meta?.error?.message
20450
- } : update._meta?.error ? { error: update._meta.error?.message ?? update._meta.error } : {}
20524
+ ...buildUpdateLogPayload(updateType, update)
20451
20525
  });
20452
20526
  return connection.sessionUpdate(params);
20453
20527
  }
@@ -20478,9 +20552,19 @@ function normalizeError(err) {
20478
20552
  }
20479
20553
  if (typeof err === "object") {
20480
20554
  const o = err;
20481
- const message = typeof o.message === "string" && o.message.trim() ? o.message.trim() : typeof o.error === "string" && o.error.trim() ? o.error.trim() : void 0;
20555
+ let message;
20556
+ if (typeof o.message === "string" && o.message.trim()) {
20557
+ message = o.message.trim();
20558
+ } else if (typeof o.error === "string" && o.error.trim()) {
20559
+ message = o.error.trim();
20560
+ }
20482
20561
  if (!message) return void 0;
20483
- const cause = o.cause instanceof Error ? o.cause.message : typeof o.cause === "object" && o.cause !== null && typeof o.cause.message === "string" ? o.cause.message : void 0;
20562
+ let cause;
20563
+ if (o.cause instanceof Error) {
20564
+ cause = o.cause.message;
20565
+ } else if (typeof o.cause === "object" && o.cause !== null && typeof o.cause.message === "string") {
20566
+ cause = o.cause.message;
20567
+ }
20484
20568
  return {
20485
20569
  code: inferCode(o),
20486
20570
  message,
@@ -20540,7 +20624,6 @@ async function emitSessionError(connection, sessionId, payload, options) {
20540
20624
  // src/event-handler.ts
20541
20625
  var SUBSCRIPTION_RETRY_MS = 3e3;
20542
20626
  var SUBSCRIPTION_ERROR_BROADCAST_MS = 3e4;
20543
- var QUESTION_TIMEOUT_MS = 3e5;
20544
20627
  var permissionOptions = [
20545
20628
  { optionId: "once", kind: "allow_once", name: "Allow once" },
20546
20629
  { optionId: "always", kind: "allow_always", name: "Always allow" },
@@ -20702,8 +20785,8 @@ var EventHandler = class {
20702
20785
  }
20703
20786
  if (res.outcome.optionId !== "reject" && permission.permission === "edit") {
20704
20787
  const metadata = permission.metadata || {};
20705
- const filepath = typeof metadata["filepath"] === "string" ? metadata["filepath"] : "";
20706
- const diff = typeof metadata["diff"] === "string" ? metadata["diff"] : "";
20788
+ const filepath = typeof metadata.filepath === "string" ? metadata.filepath : "";
20789
+ const diff = typeof metadata.diff === "string" ? metadata.diff : "";
20707
20790
  if (filepath && diff) {
20708
20791
  const fileResult = await this.sdk.file.read({ path: filepath, directory }).then((x) => x.data).catch(() => void 0);
20709
20792
  const original = typeof fileResult?.content === "string" ? fileResult.content : "";
@@ -20756,17 +20839,12 @@ var EventHandler = class {
20756
20839
  const sessionId = resolved.sessionId;
20757
20840
  const prev = this.questionQueues.get(q.sessionID) ?? Promise.resolve();
20758
20841
  const next = prev.then(async () => {
20759
- const extResult = await Promise.race([
20760
- this.connection.extMethod("questionAsked", {
20761
- sessionId,
20762
- questionId: q.id,
20763
- questions: q.questions,
20764
- ...q.tool && { tool: q.tool }
20765
- }),
20766
- new Promise(
20767
- (_, reject) => setTimeout(() => reject(new Error("questionAsked timeout")), QUESTION_TIMEOUT_MS)
20768
- )
20769
- ]).catch((err) => {
20842
+ const extResult = await this.connection.extMethod("questionAsked", {
20843
+ sessionId,
20844
+ questionId: q.id,
20845
+ questions: q.questions,
20846
+ ...q.tool && { tool: q.tool }
20847
+ }).catch((err) => {
20770
20848
  console.error("[event-handler] extMethod questionAsked failed:", err?.message ?? err);
20771
20849
  return void 0;
20772
20850
  });
@@ -20877,9 +20955,8 @@ var EventHandler = class {
20877
20955
  this.fileDiffStats.set(resolved.sessionId, { additions, deletions, files: diff.length });
20878
20956
  return;
20879
20957
  }
20880
- default: {
20958
+ default:
20881
20959
  return;
20882
- }
20883
20960
  }
20884
20961
  }
20885
20962
  // ─── Child Session Announcement ──────────────────────────────────
@@ -20978,7 +21055,7 @@ var EventHandler = class {
20978
21055
  }
20979
21056
  this.bashSnapshots.delete(part.callID);
20980
21057
  if (part.tool === "task" && part.state.metadata) {
20981
- const childSessionId = typeof part.state.metadata["sessionId"] === "string" ? part.state.metadata["sessionId"] : void 0;
21058
+ const childSessionId = typeof part.state.metadata.sessionId === "string" ? part.state.metadata.sessionId : void 0;
20982
21059
  if (childSessionId && this.sessionManager.tryGet(childSessionId)) {
20983
21060
  const tracker = this.childToolCounts.get(childSessionId);
20984
21061
  await this.sendChildSessionFinished(
@@ -21008,9 +21085,14 @@ var EventHandler = class {
21008
21085
  ];
21009
21086
  if (kind === "edit") {
21010
21087
  const input = part.state.input;
21011
- const filePath = typeof input["filePath"] === "string" ? input["filePath"] : "";
21012
- const oldText = typeof input["oldString"] === "string" ? input["oldString"] : "";
21013
- const newText = typeof input["newString"] === "string" ? input["newString"] : typeof input["content"] === "string" ? input["content"] : "";
21088
+ const filePath = typeof input.filePath === "string" ? input.filePath : "";
21089
+ const oldText = typeof input.oldString === "string" ? input.oldString : "";
21090
+ let newText = "";
21091
+ if (typeof input.newString === "string") {
21092
+ newText = input.newString;
21093
+ } else if (typeof input.content === "string") {
21094
+ newText = input.content;
21095
+ }
21014
21096
  content.push({ type: "diff", path: filePath, oldText, newText });
21015
21097
  this.accumulateAICodeChangeStats(sessionId, part);
21016
21098
  }
@@ -21059,7 +21141,7 @@ var EventHandler = class {
21059
21141
  }
21060
21142
  this.bashSnapshots.delete(part.callID);
21061
21143
  if (part.tool === "task" && part.state.metadata) {
21062
- const childSessionId = typeof part.state.metadata["sessionId"] === "string" ? part.state.metadata["sessionId"] : void 0;
21144
+ const childSessionId = typeof part.state.metadata.sessionId === "string" ? part.state.metadata.sessionId : void 0;
21063
21145
  if (childSessionId && this.sessionManager.tryGet(childSessionId)) {
21064
21146
  const tracker = this.childToolCounts.get(childSessionId);
21065
21147
  const errText = typeof part.state.error === "string" && part.state.error.trim() ? part.state.error.trim() : "Sub-agent task failed";
@@ -21104,6 +21186,8 @@ var EventHandler = class {
21104
21186
  });
21105
21187
  return;
21106
21188
  }
21189
+ default:
21190
+ return;
21107
21191
  }
21108
21192
  }
21109
21193
  /**
@@ -21136,7 +21220,7 @@ var EventHandler = class {
21136
21220
  bashOutput(part) {
21137
21221
  if (part.tool !== "bash") return;
21138
21222
  if (!("metadata" in part.state) || !part.state.metadata || typeof part.state.metadata !== "object") return;
21139
- const output = part.state.metadata["output"];
21223
+ const output = part.state.metadata.output;
21140
21224
  if (typeof output !== "string") return;
21141
21225
  return output;
21142
21226
  }
@@ -21155,7 +21239,7 @@ var EventHandler = class {
21155
21239
  accumulateAICodeChangeStats(sessionId, part) {
21156
21240
  if (part.state.status !== "completed") return;
21157
21241
  const meta3 = part.state.metadata ?? {};
21158
- const aiCodeChange = meta3["aiCodeChange"];
21242
+ const aiCodeChange = meta3.aiCodeChange;
21159
21243
  if (!aiCodeChange || aiCodeChange.additions === 0 && aiCodeChange.deletions === 0) return;
21160
21244
  const fileMap = this.aiCodeChangeStats.get(sessionId) ?? /* @__PURE__ */ new Map();
21161
21245
  const existing = fileMap.get(aiCodeChange.file) ?? { additions: 0, deletions: 0 };
@@ -21296,14 +21380,19 @@ function sessionIdFromEvent(event) {
21296
21380
  return typeof props.sessionID === "string" ? props.sessionID : void 0;
21297
21381
  case "session.diff":
21298
21382
  case "session.updated":
21299
- case "session.created":
21300
- return typeof props.sessionID === "string" ? props.sessionID : typeof props.info?.id === "string" ? props.info.id : void 0;
21383
+ case "session.created": {
21384
+ if (typeof props.sessionID === "string") return props.sessionID;
21385
+ const info = props.info;
21386
+ if (typeof info?.id === "string") return info.id;
21387
+ return void 0;
21388
+ }
21301
21389
  case "permission.asked": {
21302
21390
  const permission = props;
21303
21391
  return typeof permission.sessionID === "string" ? permission.sessionID : void 0;
21304
21392
  }
21393
+ default:
21394
+ return void 0;
21305
21395
  }
21306
- return void 0;
21307
21396
  }
21308
21397
  var TurnTimeoutTracker = class {
21309
21398
  constructor(sessionManager) {
@@ -21923,7 +22012,7 @@ var Agent = class {
21923
22012
  const session = this.sessionManager.get(params.sessionId);
21924
22013
  const providers = await this.sdk.config.providers({ directory: session.cwd }).then((x) => x.data?.providers ?? []).catch(() => []);
21925
22014
  const sortedProviders = [...providers].sort(
21926
- (a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : a.name.toLowerCase() > b.name.toLowerCase() ? 1 : 0
22015
+ (a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())
21927
22016
  );
21928
22017
  if (params.configId === "model") {
21929
22018
  if (typeof params.value !== "string") throw RequestError.invalidParams("model value must be a string");
@@ -22052,7 +22141,7 @@ var Agent = class {
22052
22141
  const model = await this.defaultModel(params.cwd);
22053
22142
  const providers = await this.sdk.config.providers({ directory: params.cwd }).then((x) => x.data?.providers ?? []).catch(() => []);
22054
22143
  const sortedProviders = [...providers].sort(
22055
- (a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : a.name.toLowerCase() > b.name.toLowerCase() ? 1 : 0
22144
+ (a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())
22056
22145
  );
22057
22146
  const availableModels = sortedProviders.flatMap(
22058
22147
  (provider) => Object.values(provider.models).flatMap((modelInfo) => {
@@ -22174,7 +22263,12 @@ var Agent = class {
22174
22263
  if (part.type === "tool") {
22175
22264
  await this.replayToolPart(sessionId, part);
22176
22265
  } else if (part.type === "text" && part.text) {
22177
- const audience = part.synthetic ? ["assistant"] : part.ignored ? ["user"] : void 0;
22266
+ let audience;
22267
+ if (part.synthetic) {
22268
+ audience = ["assistant"];
22269
+ } else if (part.ignored) {
22270
+ audience = ["user"];
22271
+ }
22178
22272
  await sendToClient(this.connection, {
22179
22273
  sessionId,
22180
22274
  update: {
@@ -22274,11 +22368,17 @@ var Agent = class {
22274
22368
  const content = [{ type: "content", content: { type: "text", text: part.state.output } }];
22275
22369
  if (kind === "edit") {
22276
22370
  const input = part.state.input;
22371
+ let newText = "";
22372
+ if (typeof input.newString === "string") {
22373
+ newText = input.newString;
22374
+ } else if (typeof input.content === "string") {
22375
+ newText = input.content;
22376
+ }
22277
22377
  content.push({
22278
22378
  type: "diff",
22279
- path: typeof input["filePath"] === "string" ? input["filePath"] : "",
22280
- oldText: typeof input["oldString"] === "string" ? input["oldString"] : "",
22281
- newText: typeof input["newString"] === "string" ? input["newString"] : typeof input["content"] === "string" ? input["content"] : ""
22379
+ path: typeof input.filePath === "string" ? input.filePath : "",
22380
+ oldText: typeof input.oldString === "string" ? input.oldString : "",
22381
+ newText
22282
22382
  });
22283
22383
  this.eventHandler.accumulateAICodeChangeStats(sessionId, part);
22284
22384
  }
@@ -22334,6 +22434,8 @@ var Agent = class {
22334
22434
  }).catch(() => {
22335
22435
  });
22336
22436
  break;
22437
+ default:
22438
+ break;
22337
22439
  }
22338
22440
  }
22339
22441
  async sendUsageUpdate(sessionId, directory, turnError) {
@@ -22423,11 +22525,18 @@ var Agent = class {
22423
22525
  } : { ...cs };
22424
22526
  }
22425
22527
  }
22426
- const totalAICodeChangeStats = parentStats ? childAICodeChangeStats ? {
22427
- additions: parentStats.additions + childAICodeChangeStats.additions,
22428
- deletions: parentStats.deletions + childAICodeChangeStats.deletions,
22429
- files: parentStats.files + childAICodeChangeStats.files
22430
- } : parentStats : childAICodeChangeStats;
22528
+ let totalAICodeChangeStats;
22529
+ if (parentStats && childAICodeChangeStats) {
22530
+ totalAICodeChangeStats = {
22531
+ additions: parentStats.additions + childAICodeChangeStats.additions,
22532
+ deletions: parentStats.deletions + childAICodeChangeStats.deletions,
22533
+ files: parentStats.files + childAICodeChangeStats.files
22534
+ };
22535
+ } else if (parentStats) {
22536
+ totalAICodeChangeStats = parentStats;
22537
+ } else {
22538
+ totalAICodeChangeStats = childAICodeChangeStats;
22539
+ }
22431
22540
  if (totalAICodeChangeStats) {
22432
22541
  _meta.aiCodeChange = totalAICodeChangeStats;
22433
22542
  }
@@ -22483,6 +22592,8 @@ var Agent = class {
22483
22592
  }
22484
22593
  break;
22485
22594
  }
22595
+ default:
22596
+ break;
22486
22597
  }
22487
22598
  }
22488
22599
  return result;