@openacp/cli 2026.408.3 → 2026.408.4

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/dist/index.d.ts CHANGED
@@ -875,7 +875,7 @@ declare class AgentInstance extends TypedEmitter<AgentInstanceEvents> {
875
875
 
876
876
  declare class AgentStore {
877
877
  private data;
878
- private filePath;
878
+ readonly filePath: string;
879
879
  constructor(filePath?: string);
880
880
  load(): void;
881
881
  exists(): boolean;
@@ -889,6 +889,7 @@ declare class AgentStore {
889
889
 
890
890
  declare class AgentCatalog {
891
891
  private store;
892
+ private globalStore;
892
893
  private registryAgents;
893
894
  private cachePath;
894
895
  private agentsDir;
package/dist/index.js CHANGED
@@ -198,11 +198,11 @@ async function shutdownLogger() {
198
198
  logDir = void 0;
199
199
  initialized = false;
200
200
  if (transport) {
201
- await new Promise((resolve6) => {
202
- const timeout = setTimeout(resolve6, 3e3);
201
+ await new Promise((resolve7) => {
202
+ const timeout = setTimeout(resolve7, 3e3);
203
203
  transport.on("close", () => {
204
204
  clearTimeout(timeout);
205
- resolve6();
205
+ resolve7();
206
206
  });
207
207
  transport.end();
208
208
  });
@@ -289,7 +289,8 @@ __export(instance_context_exports, {
289
289
  createInstanceContext: () => createInstanceContext,
290
290
  generateSlug: () => generateSlug,
291
291
  getGlobalRoot: () => getGlobalRoot,
292
- resolveInstanceRoot: () => resolveInstanceRoot
292
+ resolveInstanceRoot: () => resolveInstanceRoot,
293
+ resolveRunningInstance: () => resolveRunningInstance
293
294
  });
294
295
  import path2 from "path";
295
296
  import fs2 from "fs";
@@ -341,6 +342,38 @@ function resolveInstanceRoot(opts) {
341
342
  function getGlobalRoot() {
342
343
  return path2.join(os2.homedir(), ".openacp");
343
344
  }
345
+ async function resolveRunningInstance(cwd) {
346
+ const globalRoot = getGlobalRoot();
347
+ let dir = path2.resolve(cwd);
348
+ while (true) {
349
+ const candidate = path2.join(dir, ".openacp");
350
+ if (candidate !== globalRoot && fs2.existsSync(candidate)) {
351
+ if (await isInstanceRunning(candidate)) return candidate;
352
+ }
353
+ const parent = path2.dirname(dir);
354
+ if (parent === dir) break;
355
+ dir = parent;
356
+ }
357
+ if (fs2.existsSync(globalRoot) && await isInstanceRunning(globalRoot)) return globalRoot;
358
+ return null;
359
+ }
360
+ async function isInstanceRunning(instanceRoot) {
361
+ const portFile = path2.join(instanceRoot, "api.port");
362
+ try {
363
+ const content = fs2.readFileSync(portFile, "utf-8").trim();
364
+ const port = parseInt(content, 10);
365
+ if (isNaN(port)) return false;
366
+ const controller = new AbortController();
367
+ const timeout = setTimeout(() => controller.abort(), 2e3);
368
+ const res = await fetch(`http://127.0.0.1:${port}/api/v1/system/health`, {
369
+ signal: controller.signal
370
+ });
371
+ clearTimeout(timeout);
372
+ return res.ok;
373
+ } catch {
374
+ return false;
375
+ }
376
+ }
344
377
  var init_instance_context = __esm({
345
378
  "src/core/instance/instance-context.ts"() {
346
379
  "use strict";
@@ -875,6 +908,25 @@ var init_events = __esm({
875
908
  }
876
909
  });
877
910
 
911
+ // src/core/utils/apply-patch-detection.ts
912
+ function asRecord(value) {
913
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
914
+ }
915
+ function hasApplyPatchPatchText(rawInput) {
916
+ const input2 = asRecord(rawInput);
917
+ return !!input2 && (typeof input2.patchText === "string" || typeof input2.patch_text === "string");
918
+ }
919
+ function isApplyPatchOtherTool(kind, name, rawInput) {
920
+ if (kind !== "other") return false;
921
+ if (name.toLowerCase() === "apply_patch") return true;
922
+ return hasApplyPatchPatchText(rawInput);
923
+ }
924
+ var init_apply_patch_detection = __esm({
925
+ "src/core/utils/apply-patch-detection.ts"() {
926
+ "use strict";
927
+ }
928
+ });
929
+
878
930
  // src/core/utils/bypass-detection.ts
879
931
  function isPermissionBypass(value) {
880
932
  const lower = value.toLowerCase();
@@ -2013,12 +2065,12 @@ function isProcessAlive(pid) {
2013
2065
  }
2014
2066
  }
2015
2067
  function checkPortInUse(port) {
2016
- return new Promise((resolve6) => {
2068
+ return new Promise((resolve7) => {
2017
2069
  const server = net.createServer();
2018
- server.once("error", () => resolve6(true));
2070
+ server.once("error", () => resolve7(true));
2019
2071
  server.once("listening", () => {
2020
2072
  server.close();
2021
- resolve6(false);
2073
+ resolve7(false);
2022
2074
  });
2023
2075
  server.listen(port, "127.0.0.1");
2024
2076
  });
@@ -2110,7 +2162,7 @@ import https from "https";
2110
2162
  import os9 from "os";
2111
2163
  import { execFileSync as execFileSync4 } from "child_process";
2112
2164
  function downloadFile(url, dest, maxRedirects = 10) {
2113
- return new Promise((resolve6, reject) => {
2165
+ return new Promise((resolve7, reject) => {
2114
2166
  const file = fs23.createWriteStream(dest);
2115
2167
  const cleanup = () => {
2116
2168
  try {
@@ -2129,7 +2181,7 @@ function downloadFile(url, dest, maxRedirects = 10) {
2129
2181
  }
2130
2182
  file.close(() => {
2131
2183
  cleanup();
2132
- downloadFile(response.headers.location, dest, maxRedirects - 1).then(resolve6).catch(reject);
2184
+ downloadFile(response.headers.location, dest, maxRedirects - 1).then(resolve7).catch(reject);
2133
2185
  });
2134
2186
  return;
2135
2187
  }
@@ -2153,7 +2205,7 @@ function downloadFile(url, dest, maxRedirects = 10) {
2153
2205
  }
2154
2206
  });
2155
2207
  response.pipe(file);
2156
- file.on("finish", () => file.close(() => resolve6(dest)));
2208
+ file.on("finish", () => file.close(() => resolve7(dest)));
2157
2209
  file.on("error", (err) => {
2158
2210
  file.close(() => {
2159
2211
  cleanup();
@@ -4975,10 +5027,10 @@ var init_send_queue = __esm({
4975
5027
  const type = opts?.type ?? "other";
4976
5028
  const key = opts?.key;
4977
5029
  const category = opts?.category;
4978
- let resolve6;
5030
+ let resolve7;
4979
5031
  let reject;
4980
5032
  const promise = new Promise((res, rej) => {
4981
- resolve6 = res;
5033
+ resolve7 = res;
4982
5034
  reject = rej;
4983
5035
  });
4984
5036
  promise.catch(() => {
@@ -4989,12 +5041,12 @@ var init_send_queue = __esm({
4989
5041
  );
4990
5042
  if (idx !== -1) {
4991
5043
  this.items[idx].resolve(void 0);
4992
- this.items[idx] = { fn, type, key, category, resolve: resolve6, reject, promise };
5044
+ this.items[idx] = { fn, type, key, category, resolve: resolve7, reject, promise };
4993
5045
  this.scheduleProcess();
4994
5046
  return promise;
4995
5047
  }
4996
5048
  }
4997
- this.items.push({ fn, type, key, category, resolve: resolve6, reject, promise });
5049
+ this.items.push({ fn, type, key, category, resolve: resolve7, reject, promise });
4998
5050
  this.scheduleProcess();
4999
5051
  return promise;
5000
5052
  }
@@ -5256,7 +5308,7 @@ var init_stream_accumulator = __esm({
5256
5308
  });
5257
5309
 
5258
5310
  // src/core/adapter-primitives/display-spec-builder.ts
5259
- function asRecord(value) {
5311
+ function asRecord3(value) {
5260
5312
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
5261
5313
  return value;
5262
5314
  }
@@ -5289,8 +5341,21 @@ function parseApplyPatchTargets(patchText) {
5289
5341
  function buildTitle(entry, kind) {
5290
5342
  if (entry.displayTitle) return entry.displayTitle;
5291
5343
  if (entry.displaySummary) return entry.displaySummary;
5292
- const input2 = asRecord(entry.rawInput);
5344
+ const input2 = asRecord3(entry.rawInput);
5293
5345
  const nameLower = entry.name.toLowerCase();
5346
+ if (isApplyPatchOtherTool(entry.kind, entry.name, entry.rawInput)) {
5347
+ const patchText = getStringField(input2, ["patchText", "patch_text"]);
5348
+ if (patchText) {
5349
+ const targets = parseApplyPatchTargets(patchText);
5350
+ if (targets.length === 1) return targets[0];
5351
+ if (targets.length > 1) {
5352
+ const shown = targets.slice(0, 2).join(", ");
5353
+ const rest = targets.length - 2;
5354
+ return rest > 0 ? `${shown} (+${rest} more)` : shown;
5355
+ }
5356
+ }
5357
+ return "apply_patch";
5358
+ }
5294
5359
  if (kind === "read") {
5295
5360
  const filePath = getStringField(input2, ["file_path", "filePath", "path"]);
5296
5361
  if (filePath) {
@@ -5339,36 +5404,6 @@ function buildTitle(entry, kind) {
5339
5404
  }
5340
5405
  return capitalize(entry.name);
5341
5406
  }
5342
- if (nameLower === "apply_patch") {
5343
- const patchText = getStringField(input2, ["patchText", "patch_text"]);
5344
- if (patchText) {
5345
- const targets = parseApplyPatchTargets(patchText);
5346
- if (targets.length === 1) return targets[0];
5347
- if (targets.length > 1) {
5348
- const shown = targets.slice(0, 2).join(", ");
5349
- const remaining = targets.length - 2;
5350
- return remaining > 0 ? `${shown} (+${remaining} more)` : shown;
5351
- }
5352
- }
5353
- return "apply_patch";
5354
- }
5355
- if (nameLower === "todowrite") {
5356
- const todos = Array.isArray(input2.todos) ? input2.todos : [];
5357
- if (todos.length > 0) {
5358
- const inProgress = todos.filter((t) => {
5359
- if (!t || typeof t !== "object") return false;
5360
- const status = t.status;
5361
- return status === "in_progress";
5362
- }).length;
5363
- const completed = todos.filter((t) => {
5364
- if (!t || typeof t !== "object") return false;
5365
- const status = t.status;
5366
- return status === "completed";
5367
- }).length;
5368
- return `Todo list (${completed}/${todos.length} done${inProgress > 0 ? `, ${inProgress} active` : ""})`;
5369
- }
5370
- return "Todo list";
5371
- }
5372
5407
  if (kind === "fetch" || kind === "web") {
5373
5408
  const url = typeof input2.url === "string" ? input2.url : null;
5374
5409
  if (url && url !== "undefined") return url.length > 60 ? url.slice(0, 57) + "..." : url;
@@ -5379,19 +5414,6 @@ function buildTitle(entry, kind) {
5379
5414
  if (nameLower === "skill" && typeof input2.skill === "string" && input2.skill) {
5380
5415
  return input2.skill;
5381
5416
  }
5382
- if (nameLower === "apply_patch") {
5383
- const patchText = getStringField(input2, ["patchText", "patch_text"]);
5384
- if (patchText) {
5385
- const targets = parseApplyPatchTargets(patchText);
5386
- if (targets.length === 1) return targets[0];
5387
- if (targets.length > 1) {
5388
- const shown = targets.slice(0, 2).join(", ");
5389
- const rest = targets.length - 2;
5390
- return rest > 0 ? `${shown} (+${rest} more)` : shown;
5391
- }
5392
- }
5393
- return "apply_patch";
5394
- }
5395
5417
  if (nameLower === "todowrite") {
5396
5418
  const todos = Array.isArray(input2.todos) ? input2.todos : [];
5397
5419
  if (todos.length > 0) {
@@ -5421,6 +5443,7 @@ var init_display_spec_builder = __esm({
5421
5443
  "src/core/adapter-primitives/display-spec-builder.ts"() {
5422
5444
  "use strict";
5423
5445
  init_format_types();
5446
+ init_apply_patch_detection();
5424
5447
  EXECUTE_KINDS = /* @__PURE__ */ new Set(["execute", "bash", "command", "terminal"]);
5425
5448
  INLINE_MAX_LINES = 15;
5426
5449
  INLINE_MAX_CHARS = 800;
@@ -5429,12 +5452,12 @@ var init_display_spec_builder = __esm({
5429
5452
  this.tunnelService = tunnelService;
5430
5453
  }
5431
5454
  buildToolSpec(entry, mode, sessionContext) {
5432
- const effectiveKind = entry.displayKind ?? entry.kind;
5455
+ const effectiveKind = entry.displayKind ?? (isApplyPatchOtherTool(entry.kind, entry.name, entry.rawInput) ? "edit" : entry.kind);
5433
5456
  const icon = KIND_ICONS[effectiveKind] ?? KIND_ICONS["other"] ?? "\u{1F6E0}\uFE0F";
5434
5457
  const title = buildTitle(entry, effectiveKind);
5435
5458
  const isHidden = entry.isNoise && mode !== "high";
5436
5459
  const includeMeta = mode !== "low";
5437
- const input2 = asRecord(entry.rawInput);
5460
+ const input2 = asRecord3(entry.rawInput);
5438
5461
  const rawDescription = typeof input2.description === "string" ? input2.description : null;
5439
5462
  const descLower = rawDescription?.toLowerCase();
5440
5463
  const description = includeMeta && rawDescription && rawDescription !== title && descLower !== effectiveKind && descLower !== entry.name.toLowerCase() ? rawDescription : null;
@@ -5819,14 +5842,14 @@ __export(version_exports, {
5819
5842
  runUpdate: () => runUpdate
5820
5843
  });
5821
5844
  import { fileURLToPath as fileURLToPath2 } from "url";
5822
- import { dirname as dirname10, join as join20, resolve as resolve5 } from "path";
5845
+ import { dirname as dirname10, join as join20, resolve as resolve6 } from "path";
5823
5846
  import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
5824
5847
  function findPackageJson() {
5825
5848
  let dir = dirname10(fileURLToPath2(import.meta.url));
5826
5849
  for (let i = 0; i < 5; i++) {
5827
5850
  const candidate = join20(dir, "package.json");
5828
5851
  if (existsSync17(candidate)) return candidate;
5829
- const parent = resolve5(dir, "..");
5852
+ const parent = resolve6(dir, "..");
5830
5853
  if (parent === dir) break;
5831
5854
  dir = parent;
5832
5855
  }
@@ -5865,21 +5888,21 @@ function compareVersions(current, latest) {
5865
5888
  }
5866
5889
  async function runUpdate() {
5867
5890
  const { spawn: spawn4 } = await import("child_process");
5868
- return new Promise((resolve6) => {
5891
+ return new Promise((resolve7) => {
5869
5892
  const child = spawn4("npm", ["install", "-g", `${NPM_PACKAGE}@latest`], {
5870
5893
  stdio: "inherit",
5871
5894
  shell: true
5872
5895
  });
5873
5896
  const onSignal = () => {
5874
5897
  child.kill("SIGTERM");
5875
- resolve6(false);
5898
+ resolve7(false);
5876
5899
  };
5877
5900
  process.on("SIGINT", onSignal);
5878
5901
  process.on("SIGTERM", onSignal);
5879
5902
  child.on("close", (code) => {
5880
5903
  process.off("SIGINT", onSignal);
5881
5904
  process.off("SIGTERM", onSignal);
5882
- resolve6(code === 0);
5905
+ resolve7(code === 0);
5883
5906
  });
5884
5907
  });
5885
5908
  }
@@ -10778,15 +10801,15 @@ var ChannelAdapter = class {
10778
10801
  function nodeToWebWritable(nodeStream) {
10779
10802
  return new WritableStream({
10780
10803
  write(chunk) {
10781
- return new Promise((resolve6, reject) => {
10804
+ return new Promise((resolve7, reject) => {
10782
10805
  const ok2 = nodeStream.write(chunk);
10783
10806
  if (ok2) {
10784
- resolve6();
10807
+ resolve7();
10785
10808
  return;
10786
10809
  }
10787
10810
  const onDrain = () => {
10788
10811
  nodeStream.removeListener("error", onError);
10789
- resolve6();
10812
+ resolve7();
10790
10813
  };
10791
10814
  const onError = (err) => {
10792
10815
  nodeStream.removeListener("drain", onDrain);
@@ -11222,12 +11245,12 @@ var TerminalManager = class {
11222
11245
  signal: state.exitStatus.signal
11223
11246
  };
11224
11247
  }
11225
- return new Promise((resolve6) => {
11248
+ return new Promise((resolve7) => {
11226
11249
  state.process.on("exit", (code, signal) => {
11227
- resolve6({ exitCode: code, signal });
11250
+ resolve7({ exitCode: code, signal });
11228
11251
  });
11229
11252
  if (state.exitStatus !== null) {
11230
- resolve6({
11253
+ resolve7({
11231
11254
  exitCode: state.exitStatus.exitCode,
11232
11255
  signal: state.exitStatus.signal
11233
11256
  });
@@ -11450,7 +11473,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
11450
11473
  env: filterEnv(process.env, agentDef.env)
11451
11474
  }
11452
11475
  );
11453
- await new Promise((resolve6, reject) => {
11476
+ await new Promise((resolve7, reject) => {
11454
11477
  instance.child.on("error", (err) => {
11455
11478
  reject(
11456
11479
  new Error(
@@ -11458,7 +11481,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
11458
11481
  )
11459
11482
  );
11460
11483
  });
11461
- instance.child.on("spawn", () => resolve6());
11484
+ instance.child.on("spawn", () => resolve7());
11462
11485
  });
11463
11486
  instance.stderrCapture = new StderrCapture(50);
11464
11487
  instance.child.stderr.on("data", (chunk) => {
@@ -11964,15 +11987,15 @@ ${skipNote}`;
11964
11987
  this._destroying = true;
11965
11988
  this.terminalManager.destroyAll();
11966
11989
  if (this.child.exitCode !== null) return;
11967
- await new Promise((resolve6) => {
11990
+ await new Promise((resolve7) => {
11968
11991
  this.child.on("exit", () => {
11969
11992
  clearTimeout(forceKillTimer);
11970
- resolve6();
11993
+ resolve7();
11971
11994
  });
11972
11995
  this.child.kill("SIGTERM");
11973
11996
  const forceKillTimer = setTimeout(() => {
11974
11997
  if (this.child.exitCode === null) this.child.kill("SIGKILL");
11975
- resolve6();
11998
+ resolve7();
11976
11999
  }, 1e4);
11977
12000
  if (typeof forceKillTimer === "object" && forceKillTimer !== null && "unref" in forceKillTimer) {
11978
12001
  forceKillTimer.unref();
@@ -12026,8 +12049,8 @@ var PromptQueue = class {
12026
12049
  processorSettled = null;
12027
12050
  async enqueue(text3, attachments, routing, turnId) {
12028
12051
  if (this.processing) {
12029
- return new Promise((resolve6) => {
12030
- this.queue.push({ text: text3, attachments, routing, turnId, resolve: resolve6 });
12052
+ return new Promise((resolve7) => {
12053
+ this.queue.push({ text: text3, attachments, routing, turnId, resolve: resolve7 });
12031
12054
  });
12032
12055
  }
12033
12056
  await this.process(text3, attachments, routing, turnId);
@@ -12101,8 +12124,8 @@ var PermissionGate = class {
12101
12124
  this.request = request;
12102
12125
  this.settled = false;
12103
12126
  this.clearTimeout();
12104
- return new Promise((resolve6, reject) => {
12105
- this.resolveFn = resolve6;
12127
+ return new Promise((resolve7, reject) => {
12128
+ this.resolveFn = resolve7;
12106
12129
  this.rejectFn = reject;
12107
12130
  this.timeoutTimer = setTimeout(() => {
12108
12131
  this.reject("Permission request timed out (no response received)");
@@ -12711,7 +12734,11 @@ ${result.text}` : result.text;
12711
12734
  import * as path8 from "path";
12712
12735
 
12713
12736
  // src/core/utils/extract-file-info.ts
12714
- function extractFileInfo(name, kind, content, rawInput, meta) {
12737
+ init_apply_patch_detection();
12738
+ function extractFileInfo(name, kind, content, rawInput, meta, rawOutput) {
12739
+ if (isApplyPatchOtherTool(kind, name, rawInput)) {
12740
+ return parseApplyPatchRawOutput(rawOutput);
12741
+ }
12715
12742
  if (kind && !["read", "edit", "write"].includes(kind)) return null;
12716
12743
  let info = null;
12717
12744
  if (meta) {
@@ -12766,6 +12793,37 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
12766
12793
  if (!info.filePath || !info.content) return null;
12767
12794
  return info;
12768
12795
  }
12796
+ function parseApplyPatchRawOutput(rawOutput) {
12797
+ if (!rawOutput || typeof rawOutput !== "object" || Array.isArray(rawOutput)) return null;
12798
+ const output = rawOutput;
12799
+ const metadata = output.metadata;
12800
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return null;
12801
+ const files = metadata.files;
12802
+ if (!Array.isArray(files)) return null;
12803
+ const sortedFiles = [...files].sort((a, b) => getApplyPatchFileScore(b) - getApplyPatchFileScore(a));
12804
+ for (const file of sortedFiles) {
12805
+ if (!file || typeof file !== "object" || Array.isArray(file)) continue;
12806
+ const f = file;
12807
+ const filePath = typeof f.filePath === "string" ? f.filePath : typeof f.relativePath === "string" ? f.relativePath : null;
12808
+ if (!filePath) continue;
12809
+ const after = typeof f.after === "string" ? f.after : null;
12810
+ if (!after) continue;
12811
+ const before = typeof f.before === "string" ? f.before : void 0;
12812
+ return {
12813
+ filePath,
12814
+ content: after,
12815
+ oldContent: before
12816
+ };
12817
+ }
12818
+ return null;
12819
+ }
12820
+ function getApplyPatchFileScore(file) {
12821
+ if (!file || typeof file !== "object" || Array.isArray(file)) return 0;
12822
+ const f = file;
12823
+ const additions = typeof f.additions === "number" && Number.isFinite(f.additions) && f.additions >= 0 ? f.additions : 0;
12824
+ const deletions = typeof f.deletions === "number" && Number.isFinite(f.deletions) && f.deletions >= 0 ? f.deletions : 0;
12825
+ return additions + deletions;
12826
+ }
12769
12827
  function resolveToolResponse(meta) {
12770
12828
  const claudeCode = meta.claudeCode;
12771
12829
  if (claudeCode?.toolResponse && typeof claudeCode.toolResponse === "object") {
@@ -12833,6 +12891,7 @@ function parseContent(content) {
12833
12891
  }
12834
12892
 
12835
12893
  // src/core/message-transformer.ts
12894
+ init_apply_patch_detection();
12836
12895
  init_log();
12837
12896
  var log5 = createChildLogger({ module: "message-transformer" });
12838
12897
  var BINARY_VIEWER_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -12888,6 +12947,63 @@ function computeLineDiff(oldStr, newStr) {
12888
12947
  removed: Math.max(0, oldLines.length - prefixLen - suffixLen)
12889
12948
  };
12890
12949
  }
12950
+ function asRecord2(value) {
12951
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
12952
+ }
12953
+ function asNonNegativeNumber(value) {
12954
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
12955
+ }
12956
+ function extractDiffStatsFromRawOutput(rawOutput) {
12957
+ const output = asRecord2(rawOutput);
12958
+ const metadata = asRecord2(output?.metadata);
12959
+ if (!metadata) return null;
12960
+ const files = Array.isArray(metadata.files) ? metadata.files : null;
12961
+ if (files && files.length > 0) {
12962
+ let added2 = 0;
12963
+ let removed2 = 0;
12964
+ let hasAny = false;
12965
+ for (const file of files) {
12966
+ const fileMeta = asRecord2(file);
12967
+ if (!fileMeta) continue;
12968
+ const fileAdded = asNonNegativeNumber(fileMeta.additions);
12969
+ const fileRemoved = asNonNegativeNumber(fileMeta.deletions);
12970
+ if (fileAdded === null && fileRemoved === null) continue;
12971
+ added2 += fileAdded ?? 0;
12972
+ removed2 += fileRemoved ?? 0;
12973
+ hasAny = true;
12974
+ }
12975
+ if (hasAny && (added2 > 0 || removed2 > 0)) return { added: added2, removed: removed2 };
12976
+ }
12977
+ const added = asNonNegativeNumber(metadata.additions);
12978
+ const removed = asNonNegativeNumber(metadata.deletions);
12979
+ if (added === null && removed === null) return null;
12980
+ if ((added ?? 0) === 0 && (removed ?? 0) === 0) return null;
12981
+ return {
12982
+ added: added ?? 0,
12983
+ removed: removed ?? 0
12984
+ };
12985
+ }
12986
+ function extractDiffStatsFromToolPayload(name, kind, rawInput, rawOutput) {
12987
+ if (kind === "edit" || kind === "write") {
12988
+ const ri = asRecord2(rawInput);
12989
+ if (!ri) return null;
12990
+ const oldStr = typeof ri.old_string === "string" ? ri.old_string : typeof ri.oldText === "string" ? ri.oldText : null;
12991
+ const newStr = typeof ri.new_string === "string" ? ri.new_string : typeof ri.newText === "string" ? ri.newText : typeof ri.content === "string" ? ri.content : null;
12992
+ if (oldStr !== null && newStr !== null) {
12993
+ const stats = computeLineDiff(oldStr, newStr);
12994
+ return stats.added > 0 || stats.removed > 0 ? stats : null;
12995
+ }
12996
+ if (oldStr === null && newStr !== null && kind === "write") {
12997
+ const added = newStr.split("\n").length;
12998
+ return added > 0 ? { added, removed: 0 } : null;
12999
+ }
13000
+ return null;
13001
+ }
13002
+ if (isApplyPatchOtherTool(kind, name, rawInput)) {
13003
+ return extractDiffStatsFromRawOutput(rawOutput);
13004
+ }
13005
+ return null;
13006
+ }
12891
13007
  var MessageTransformer = class {
12892
13008
  tunnelService;
12893
13009
  /** Cache rawInput from tool_call so it's available in tool_update (which often lacks it) */
@@ -13037,22 +13153,11 @@ var MessageTransformer = class {
13037
13153
  return input2 !== null && input2 !== void 0 && typeof input2 === "object" && !Array.isArray(input2) && Object.keys(input2).length > 0;
13038
13154
  }
13039
13155
  enrichWithViewerLinks(event, metadata, sessionContext) {
13156
+ const name = "name" in event ? event.name || "" : "";
13040
13157
  const kind = "kind" in event ? event.kind : void 0;
13041
- if (!metadata.diffStats && (kind === "edit" || kind === "write")) {
13042
- const ri = event.rawInput;
13043
- if (ri) {
13044
- const oldStr = typeof ri.old_string === "string" ? ri.old_string : typeof ri.oldText === "string" ? ri.oldText : null;
13045
- const newStr = typeof ri.new_string === "string" ? ri.new_string : typeof ri.newText === "string" ? ri.newText : typeof ri.content === "string" ? ri.content : null;
13046
- if (oldStr !== null && newStr !== null) {
13047
- const stats = computeLineDiff(oldStr, newStr);
13048
- if (stats.added > 0 || stats.removed > 0) {
13049
- metadata.diffStats = stats;
13050
- }
13051
- } else if (oldStr === null && newStr !== null && kind === "write") {
13052
- const added = newStr.split("\n").length;
13053
- if (added > 0) metadata.diffStats = { added, removed: 0 };
13054
- }
13055
- }
13158
+ if (!metadata.diffStats) {
13159
+ const stats = extractDiffStatsFromToolPayload(name, kind, event.rawInput, event.rawOutput);
13160
+ if (stats) metadata.diffStats = stats;
13056
13161
  }
13057
13162
  if (!this.tunnelService || !sessionContext) {
13058
13163
  log5.debug(
@@ -13061,7 +13166,6 @@ var MessageTransformer = class {
13061
13166
  );
13062
13167
  return;
13063
13168
  }
13064
- const name = "name" in event ? event.name || "" : "";
13065
13169
  log5.debug(
13066
13170
  { name, kind, status: event.status, hasContent: !!event.content, hasRawInput: !!event.rawInput },
13067
13171
  "enrichWithViewerLinks: inspecting event"
@@ -13071,7 +13175,8 @@ var MessageTransformer = class {
13071
13175
  kind,
13072
13176
  event.content,
13073
13177
  event.rawInput,
13074
- event.meta
13178
+ event.meta,
13179
+ event.rawOutput
13075
13180
  );
13076
13181
  if (!fileInfo) {
13077
13182
  log5.debug(
@@ -14610,6 +14715,7 @@ var REGISTRY_URL = "https://cdn.agentclientprotocol.com/registry/v1/latest/regis
14610
14715
  var DEFAULT_TTL_HOURS = 24;
14611
14716
  var AgentCatalog = class {
14612
14717
  store;
14718
+ globalStore = null;
14613
14719
  registryAgents = [];
14614
14720
  cachePath;
14615
14721
  agentsDir;
@@ -14617,9 +14723,15 @@ var AgentCatalog = class {
14617
14723
  this.store = store ?? new AgentStore();
14618
14724
  this.cachePath = cachePath ?? path13.join(os6.homedir(), ".openacp", "registry-cache.json");
14619
14725
  this.agentsDir = agentsDir;
14726
+ const globalPath = path13.join(os6.homedir(), ".openacp", "agents.json");
14727
+ const storePath = this.store.filePath;
14728
+ if (path13.resolve(storePath) !== path13.resolve(globalPath)) {
14729
+ this.globalStore = new AgentStore(globalPath);
14730
+ }
14620
14731
  }
14621
14732
  load() {
14622
14733
  this.store.load();
14734
+ this.globalStore?.load();
14623
14735
  this.loadRegistryFromCacheOrSnapshot();
14624
14736
  this.enrichInstalledFromRegistry();
14625
14737
  }
@@ -14659,19 +14771,20 @@ var AgentCatalog = class {
14659
14771
  if (byId) return byId;
14660
14772
  return this.registryAgents.find((a) => getAgentAlias(a.id) === keyOrId);
14661
14773
  }
14662
- // --- Installed ---
14774
+ // --- Installed (instance-first, global-fallback) ---
14663
14775
  getInstalled() {
14664
- return Object.values(this.store.getInstalled());
14776
+ const merged = { ...this.globalStore?.getInstalled(), ...this.store.getInstalled() };
14777
+ return Object.values(merged);
14665
14778
  }
14666
14779
  getInstalledEntries() {
14667
- return this.store.getInstalled();
14780
+ return { ...this.globalStore?.getInstalled(), ...this.store.getInstalled() };
14668
14781
  }
14669
14782
  getInstalledAgent(key) {
14670
- return this.store.getAgent(key);
14783
+ return this.store.getAgent(key) ?? this.globalStore?.getAgent(key);
14671
14784
  }
14672
14785
  // --- Discovery ---
14673
14786
  getAvailable() {
14674
- const installed = this.store.getInstalled();
14787
+ const installed = this.getInstalledEntries();
14675
14788
  const items = [];
14676
14789
  const seenKeys = /* @__PURE__ */ new Set();
14677
14790
  for (const [key, agent] of Object.entries(installed)) {
@@ -14745,15 +14858,18 @@ var AgentCatalog = class {
14745
14858
  this.store.addAgent(key, data);
14746
14859
  }
14747
14860
  async uninstall(key) {
14748
- if (!this.store.hasAgent(key)) {
14749
- return { ok: false, error: `"${key}" is not installed.` };
14861
+ if (this.store.hasAgent(key)) {
14862
+ await uninstallAgent(key, this.store);
14863
+ return { ok: true };
14864
+ }
14865
+ if (this.globalStore?.getAgent(key)) {
14866
+ return { ok: false, error: `"${key}" is installed globally. Uninstall it from the global instance instead.` };
14750
14867
  }
14751
- await uninstallAgent(key, this.store);
14752
- return { ok: true };
14868
+ return { ok: false, error: `"${key}" is not installed.` };
14753
14869
  }
14754
14870
  // --- Resolution (for AgentManager) ---
14755
14871
  resolve(key) {
14756
- const agent = this.store.getAgent(key);
14872
+ const agent = this.store.getAgent(key) ?? this.globalStore?.getAgent(key);
14757
14873
  if (!agent) return void 0;
14758
14874
  return {
14759
14875
  name: key,
@@ -15355,13 +15471,13 @@ init_events();
15355
15471
  var SETUP_TIMEOUT_MS = 3e4;
15356
15472
  var TEARDOWN_TIMEOUT_MS = 1e4;
15357
15473
  function withTimeout(promise, ms, label) {
15358
- return new Promise((resolve6, reject) => {
15474
+ return new Promise((resolve7, reject) => {
15359
15475
  const timer = setTimeout(() => reject(new Error(`Timeout: ${label} exceeded ${ms}ms`)), ms);
15360
15476
  if (typeof timer === "object" && timer !== null && "unref" in timer) {
15361
15477
  ;
15362
15478
  timer.unref();
15363
15479
  }
15364
- promise.then(resolve6, reject).finally(() => clearTimeout(timer));
15480
+ promise.then(resolve7, reject).finally(() => clearTimeout(timer));
15365
15481
  });
15366
15482
  }
15367
15483
  function resolvePluginConfig(pluginName, configManager) {
@@ -17688,7 +17804,7 @@ function startDaemon(pidPath = getPidPath(), logDir2, instanceRoot) {
17688
17804
  return { pid: child.pid };
17689
17805
  }
17690
17806
  function sleep(ms) {
17691
- return new Promise((resolve6) => setTimeout(resolve6, ms));
17807
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
17692
17808
  }
17693
17809
  function isProcessAlive2(pid) {
17694
17810
  try {