@openacp/cli 2026.408.2 → 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.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
  });
@@ -11358,18 +11381,41 @@ function resolveAgentCommand(cmd) {
11358
11381
  try {
11359
11382
  const fullPath = execFileSync("which", [cmd], { encoding: "utf-8" }).trim();
11360
11383
  if (fullPath) {
11361
- const content = fs8.readFileSync(fullPath, "utf-8");
11362
- if (content.startsWith("#!/usr/bin/env node")) {
11363
- return { command: process.execPath, args: [fullPath] };
11384
+ try {
11385
+ const content = fs8.readFileSync(fullPath, "utf-8");
11386
+ if (content.startsWith("#!/usr/bin/env node")) {
11387
+ return { command: process.execPath, args: [fullPath] };
11388
+ }
11389
+ } catch {
11364
11390
  }
11391
+ return { command: fullPath, args: [] };
11365
11392
  }
11366
11393
  } catch {
11367
11394
  }
11368
11395
  if (cmd === "npx" || cmd === "uvx") {
11369
- const sibling = path7.join(path7.dirname(process.execPath), cmd);
11370
- if (fs8.existsSync(sibling)) {
11371
- return { command: sibling, args: [] };
11396
+ const seen = /* @__PURE__ */ new Set();
11397
+ const candidates = [];
11398
+ const addCandidate = (dir) => {
11399
+ if (!seen.has(dir)) {
11400
+ seen.add(dir);
11401
+ candidates.push(dir);
11402
+ }
11403
+ };
11404
+ addCandidate(path7.dirname(process.execPath));
11405
+ try {
11406
+ addCandidate(path7.dirname(fs8.realpathSync(process.execPath)));
11407
+ } catch {
11408
+ }
11409
+ addCandidate("/opt/homebrew/bin");
11410
+ addCandidate("/usr/local/bin");
11411
+ for (const dir of candidates) {
11412
+ const candidate = path7.join(dir, cmd);
11413
+ if (fs8.existsSync(candidate)) {
11414
+ log4.info({ cmd, resolved: candidate }, "Resolved package runner from fallback search");
11415
+ return { command: candidate, args: [] };
11416
+ }
11372
11417
  }
11418
+ log4.warn({ cmd, execPath: process.execPath, candidates }, "Could not find package runner");
11373
11419
  }
11374
11420
  return { command: cmd, args: [] };
11375
11421
  }
@@ -11427,7 +11473,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
11427
11473
  env: filterEnv(process.env, agentDef.env)
11428
11474
  }
11429
11475
  );
11430
- await new Promise((resolve6, reject) => {
11476
+ await new Promise((resolve7, reject) => {
11431
11477
  instance.child.on("error", (err) => {
11432
11478
  reject(
11433
11479
  new Error(
@@ -11435,7 +11481,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
11435
11481
  )
11436
11482
  );
11437
11483
  });
11438
- instance.child.on("spawn", () => resolve6());
11484
+ instance.child.on("spawn", () => resolve7());
11439
11485
  });
11440
11486
  instance.stderrCapture = new StderrCapture(50);
11441
11487
  instance.child.stderr.on("data", (chunk) => {
@@ -11941,15 +11987,15 @@ ${skipNote}`;
11941
11987
  this._destroying = true;
11942
11988
  this.terminalManager.destroyAll();
11943
11989
  if (this.child.exitCode !== null) return;
11944
- await new Promise((resolve6) => {
11990
+ await new Promise((resolve7) => {
11945
11991
  this.child.on("exit", () => {
11946
11992
  clearTimeout(forceKillTimer);
11947
- resolve6();
11993
+ resolve7();
11948
11994
  });
11949
11995
  this.child.kill("SIGTERM");
11950
11996
  const forceKillTimer = setTimeout(() => {
11951
11997
  if (this.child.exitCode === null) this.child.kill("SIGKILL");
11952
- resolve6();
11998
+ resolve7();
11953
11999
  }, 1e4);
11954
12000
  if (typeof forceKillTimer === "object" && forceKillTimer !== null && "unref" in forceKillTimer) {
11955
12001
  forceKillTimer.unref();
@@ -12003,8 +12049,8 @@ var PromptQueue = class {
12003
12049
  processorSettled = null;
12004
12050
  async enqueue(text3, attachments, routing, turnId) {
12005
12051
  if (this.processing) {
12006
- return new Promise((resolve6) => {
12007
- 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 });
12008
12054
  });
12009
12055
  }
12010
12056
  await this.process(text3, attachments, routing, turnId);
@@ -12078,8 +12124,8 @@ var PermissionGate = class {
12078
12124
  this.request = request;
12079
12125
  this.settled = false;
12080
12126
  this.clearTimeout();
12081
- return new Promise((resolve6, reject) => {
12082
- this.resolveFn = resolve6;
12127
+ return new Promise((resolve7, reject) => {
12128
+ this.resolveFn = resolve7;
12083
12129
  this.rejectFn = reject;
12084
12130
  this.timeoutTimer = setTimeout(() => {
12085
12131
  this.reject("Permission request timed out (no response received)");
@@ -12688,7 +12734,11 @@ ${result.text}` : result.text;
12688
12734
  import * as path8 from "path";
12689
12735
 
12690
12736
  // src/core/utils/extract-file-info.ts
12691
- 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
+ }
12692
12742
  if (kind && !["read", "edit", "write"].includes(kind)) return null;
12693
12743
  let info = null;
12694
12744
  if (meta) {
@@ -12743,6 +12793,37 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
12743
12793
  if (!info.filePath || !info.content) return null;
12744
12794
  return info;
12745
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
+ }
12746
12827
  function resolveToolResponse(meta) {
12747
12828
  const claudeCode = meta.claudeCode;
12748
12829
  if (claudeCode?.toolResponse && typeof claudeCode.toolResponse === "object") {
@@ -12810,6 +12891,7 @@ function parseContent(content) {
12810
12891
  }
12811
12892
 
12812
12893
  // src/core/message-transformer.ts
12894
+ init_apply_patch_detection();
12813
12895
  init_log();
12814
12896
  var log5 = createChildLogger({ module: "message-transformer" });
12815
12897
  var BINARY_VIEWER_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -12865,6 +12947,63 @@ function computeLineDiff(oldStr, newStr) {
12865
12947
  removed: Math.max(0, oldLines.length - prefixLen - suffixLen)
12866
12948
  };
12867
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
+ }
12868
13007
  var MessageTransformer = class {
12869
13008
  tunnelService;
12870
13009
  /** Cache rawInput from tool_call so it's available in tool_update (which often lacks it) */
@@ -13014,22 +13153,11 @@ var MessageTransformer = class {
13014
13153
  return input2 !== null && input2 !== void 0 && typeof input2 === "object" && !Array.isArray(input2) && Object.keys(input2).length > 0;
13015
13154
  }
13016
13155
  enrichWithViewerLinks(event, metadata, sessionContext) {
13156
+ const name = "name" in event ? event.name || "" : "";
13017
13157
  const kind = "kind" in event ? event.kind : void 0;
13018
- if (!metadata.diffStats && (kind === "edit" || kind === "write")) {
13019
- const ri = event.rawInput;
13020
- if (ri) {
13021
- const oldStr = typeof ri.old_string === "string" ? ri.old_string : typeof ri.oldText === "string" ? ri.oldText : null;
13022
- const newStr = typeof ri.new_string === "string" ? ri.new_string : typeof ri.newText === "string" ? ri.newText : typeof ri.content === "string" ? ri.content : null;
13023
- if (oldStr !== null && newStr !== null) {
13024
- const stats = computeLineDiff(oldStr, newStr);
13025
- if (stats.added > 0 || stats.removed > 0) {
13026
- metadata.diffStats = stats;
13027
- }
13028
- } else if (oldStr === null && newStr !== null && kind === "write") {
13029
- const added = newStr.split("\n").length;
13030
- if (added > 0) metadata.diffStats = { added, removed: 0 };
13031
- }
13032
- }
13158
+ if (!metadata.diffStats) {
13159
+ const stats = extractDiffStatsFromToolPayload(name, kind, event.rawInput, event.rawOutput);
13160
+ if (stats) metadata.diffStats = stats;
13033
13161
  }
13034
13162
  if (!this.tunnelService || !sessionContext) {
13035
13163
  log5.debug(
@@ -13038,7 +13166,6 @@ var MessageTransformer = class {
13038
13166
  );
13039
13167
  return;
13040
13168
  }
13041
- const name = "name" in event ? event.name || "" : "";
13042
13169
  log5.debug(
13043
13170
  { name, kind, status: event.status, hasContent: !!event.content, hasRawInput: !!event.rawInput },
13044
13171
  "enrichWithViewerLinks: inspecting event"
@@ -13048,7 +13175,8 @@ var MessageTransformer = class {
13048
13175
  kind,
13049
13176
  event.content,
13050
13177
  event.rawInput,
13051
- event.meta
13178
+ event.meta,
13179
+ event.rawOutput
13052
13180
  );
13053
13181
  if (!fileInfo) {
13054
13182
  log5.debug(
@@ -14587,6 +14715,7 @@ var REGISTRY_URL = "https://cdn.agentclientprotocol.com/registry/v1/latest/regis
14587
14715
  var DEFAULT_TTL_HOURS = 24;
14588
14716
  var AgentCatalog = class {
14589
14717
  store;
14718
+ globalStore = null;
14590
14719
  registryAgents = [];
14591
14720
  cachePath;
14592
14721
  agentsDir;
@@ -14594,9 +14723,15 @@ var AgentCatalog = class {
14594
14723
  this.store = store ?? new AgentStore();
14595
14724
  this.cachePath = cachePath ?? path13.join(os6.homedir(), ".openacp", "registry-cache.json");
14596
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
+ }
14597
14731
  }
14598
14732
  load() {
14599
14733
  this.store.load();
14734
+ this.globalStore?.load();
14600
14735
  this.loadRegistryFromCacheOrSnapshot();
14601
14736
  this.enrichInstalledFromRegistry();
14602
14737
  }
@@ -14636,19 +14771,20 @@ var AgentCatalog = class {
14636
14771
  if (byId) return byId;
14637
14772
  return this.registryAgents.find((a) => getAgentAlias(a.id) === keyOrId);
14638
14773
  }
14639
- // --- Installed ---
14774
+ // --- Installed (instance-first, global-fallback) ---
14640
14775
  getInstalled() {
14641
- return Object.values(this.store.getInstalled());
14776
+ const merged = { ...this.globalStore?.getInstalled(), ...this.store.getInstalled() };
14777
+ return Object.values(merged);
14642
14778
  }
14643
14779
  getInstalledEntries() {
14644
- return this.store.getInstalled();
14780
+ return { ...this.globalStore?.getInstalled(), ...this.store.getInstalled() };
14645
14781
  }
14646
14782
  getInstalledAgent(key) {
14647
- return this.store.getAgent(key);
14783
+ return this.store.getAgent(key) ?? this.globalStore?.getAgent(key);
14648
14784
  }
14649
14785
  // --- Discovery ---
14650
14786
  getAvailable() {
14651
- const installed = this.store.getInstalled();
14787
+ const installed = this.getInstalledEntries();
14652
14788
  const items = [];
14653
14789
  const seenKeys = /* @__PURE__ */ new Set();
14654
14790
  for (const [key, agent] of Object.entries(installed)) {
@@ -14722,15 +14858,18 @@ var AgentCatalog = class {
14722
14858
  this.store.addAgent(key, data);
14723
14859
  }
14724
14860
  async uninstall(key) {
14725
- if (!this.store.hasAgent(key)) {
14726
- 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.` };
14727
14867
  }
14728
- await uninstallAgent(key, this.store);
14729
- return { ok: true };
14868
+ return { ok: false, error: `"${key}" is not installed.` };
14730
14869
  }
14731
14870
  // --- Resolution (for AgentManager) ---
14732
14871
  resolve(key) {
14733
- const agent = this.store.getAgent(key);
14872
+ const agent = this.store.getAgent(key) ?? this.globalStore?.getAgent(key);
14734
14873
  if (!agent) return void 0;
14735
14874
  return {
14736
14875
  name: key,
@@ -15332,13 +15471,13 @@ init_events();
15332
15471
  var SETUP_TIMEOUT_MS = 3e4;
15333
15472
  var TEARDOWN_TIMEOUT_MS = 1e4;
15334
15473
  function withTimeout(promise, ms, label) {
15335
- return new Promise((resolve6, reject) => {
15474
+ return new Promise((resolve7, reject) => {
15336
15475
  const timer = setTimeout(() => reject(new Error(`Timeout: ${label} exceeded ${ms}ms`)), ms);
15337
15476
  if (typeof timer === "object" && timer !== null && "unref" in timer) {
15338
15477
  ;
15339
15478
  timer.unref();
15340
15479
  }
15341
- promise.then(resolve6, reject).finally(() => clearTimeout(timer));
15480
+ promise.then(resolve7, reject).finally(() => clearTimeout(timer));
15342
15481
  });
15343
15482
  }
15344
15483
  function resolvePluginConfig(pluginName, configManager) {
@@ -17665,7 +17804,7 @@ function startDaemon(pidPath = getPidPath(), logDir2, instanceRoot) {
17665
17804
  return { pid: child.pid };
17666
17805
  }
17667
17806
  function sleep(ms) {
17668
- return new Promise((resolve6) => setTimeout(resolve6, ms));
17807
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
17669
17808
  }
17670
17809
  function isProcessAlive2(pid) {
17671
17810
  try {