@ycodium-ai/agent-acp 0.2.3236 → 0.2.3240

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 (2) hide show
  1. package/dist/index.js +147 -101
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -25053,6 +25053,83 @@ function createAcpConnection(options) {
25053
25053
  };
25054
25054
  }
25055
25055
 
25056
+ // ../../packages/ycodium-acp-executor/src/model-selection.ts
25057
+ function normalizeModelId(value) {
25058
+ return value?.trim() || void 0;
25059
+ }
25060
+ function modelSelectionStateFromSetup(setup) {
25061
+ const sessionModels = isRecord(setup.models) ? setup.models : void 0;
25062
+ const modelConfigId = extractModelConfigId(setup);
25063
+ const strategy = sessionModels === void 0 && modelConfigId !== void 0 ? "config-option" : "session/set_model";
25064
+ const modelConfigOption = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId);
25065
+ const configModel = modelConfigOption?.currentValue;
25066
+ const currentModelId = strategy === "config-option" ? typeof configModel === "string" ? normalizeModelId(configModel) : void 0 : typeof sessionModels?.["currentModelId"] === "string" ? normalizeModelId(sessionModels["currentModelId"]) : void 0;
25067
+ return {
25068
+ strategy,
25069
+ sessionModelsAdvertised: sessionModels !== void 0,
25070
+ modelConfigId,
25071
+ currentModelId,
25072
+ advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels),
25073
+ configOptions: setup.configOptions
25074
+ };
25075
+ }
25076
+
25077
+ // ../../packages/ycodium-acp-executor/src/mutex.ts
25078
+ var Mutex = class {
25079
+ locked = false;
25080
+ waiters = [];
25081
+ get isLocked() {
25082
+ return this.locked;
25083
+ }
25084
+ acquire() {
25085
+ if (!this.locked) {
25086
+ this.locked = true;
25087
+ return Promise.resolve();
25088
+ }
25089
+ return new Promise((resolve2) => {
25090
+ this.waiters.push(resolve2);
25091
+ });
25092
+ }
25093
+ tryAcquire() {
25094
+ if (this.locked) {
25095
+ return false;
25096
+ }
25097
+ this.locked = true;
25098
+ return true;
25099
+ }
25100
+ release() {
25101
+ const next = this.waiters.shift();
25102
+ if (next) {
25103
+ next();
25104
+ return;
25105
+ }
25106
+ this.locked = false;
25107
+ }
25108
+ async runExclusive(fn) {
25109
+ await this.acquire();
25110
+ try {
25111
+ return await fn();
25112
+ } finally {
25113
+ this.release();
25114
+ }
25115
+ }
25116
+ };
25117
+
25118
+ // ../../packages/ycodium-acp-executor/src/user-input-pending.ts
25119
+ var userInputAnswerOptionId = (question, answers) => {
25120
+ const answer = answers[question.id];
25121
+ const selected = typeof answer === "string" ? answer : Array.isArray(answer) && answer.length === 1 && typeof answer[0] === "string" ? answer[0] : void 0;
25122
+ return selected !== void 0 && question.options.some((option) => option.id === selected) ? selected : void 0;
25123
+ };
25124
+
25125
+ // ../../packages/ycodium-acp-executor/src/vendor-hooks.ts
25126
+ var defaultVendorHooks = {
25127
+ completedStopReason: (response) => response.stopReason
25128
+ };
25129
+
25130
+ // ../../packages/ycodium-acp-executor/src/executor/support.ts
25131
+ var DEFAULT_RESULT_DRAIN_TIMEOUT_MS = 2e3;
25132
+
25056
25133
  // ../../packages/ycodium-acp-executor/src/executor/lifecycle.ts
25057
25134
  function createExecutorLifecycle({
25058
25135
  failIfDisposed,
@@ -25195,16 +25272,30 @@ function createExecutorLifecycle({
25195
25272
  }
25196
25273
  };
25197
25274
  let disposing;
25198
- const dispose = () => {
25275
+ const dispose = (exitCode) => {
25199
25276
  if (disposing) return disposing;
25200
25277
  shared.disposing = true;
25201
- promptResults.stop();
25202
- settleAllApprovals("cancel");
25203
- settleAllUserInputs();
25204
- abortPendingSettlement("executor disposed");
25205
- notifyPromptSettled();
25206
- shared.abortLocalPrompt?.();
25207
25278
  disposing = (async () => {
25279
+ if (exitCode !== void 0) {
25280
+ let timer;
25281
+ await Promise.race([
25282
+ connection.closed.catch(() => void 0),
25283
+ new Promise((resolve2) => {
25284
+ timer = setTimeout(
25285
+ resolve2,
25286
+ options.resultDrainTimeoutMs ?? DEFAULT_RESULT_DRAIN_TIMEOUT_MS
25287
+ );
25288
+ })
25289
+ ]);
25290
+ clearTimeout(timer);
25291
+ await promptResults.wait(void 0, true);
25292
+ }
25293
+ promptResults.stop();
25294
+ settleAllApprovals("cancel");
25295
+ settleAllUserInputs();
25296
+ abortPendingSettlement("executor disposed");
25297
+ notifyPromptSettled();
25298
+ shared.abortLocalPrompt?.();
25208
25299
  promptResults.finishUnreceived();
25209
25300
  await promptResults.wait();
25210
25301
  emit({
@@ -25212,7 +25303,10 @@ function createExecutorLifecycle({
25212
25303
  ...stamp(),
25213
25304
  provider: options.provider,
25214
25305
  threadId: options.threadId,
25215
- payload: { reason: "disposed" }
25306
+ payload: exitCode === void 0 ? { reason: "disposed" } : {
25307
+ reason: exitCode === null ? "process exited without an exit code" : `process exited with code ${exitCode}`,
25308
+ exitKind: exitCode === 0 ? "graceful" : "error"
25309
+ }
25216
25310
  });
25217
25311
  shared.disposed = true;
25218
25312
  shared.generation += 1;
@@ -25307,11 +25401,6 @@ var acpPromptFailureCategory = (error62, input2) => {
25307
25401
  return error62 instanceof AcpConnectionClosedError ? acpTransportIfEstablished(input2.sessionEstablished, input2.disposed) : void 0;
25308
25402
  };
25309
25403
 
25310
- // ../../packages/ycodium-acp-executor/src/vendor-hooks.ts
25311
- var defaultVendorHooks = {
25312
- completedStopReason: (response) => response.stopReason
25313
- };
25314
-
25315
25404
  // ../../packages/ycodium-acp-executor/src/executor/prompt.ts
25316
25405
  function createExecutorPrompt({
25317
25406
  isLive,
@@ -25417,8 +25506,12 @@ function createExecutorPrompt({
25417
25506
  }
25418
25507
  }
25419
25508
  };
25420
- const prompt = async (input2) => {
25509
+ const failIfPromptUnavailable = () => {
25421
25510
  failIfDisposed();
25511
+ if (shared.connectionClosed) throw new AcpExecutorDisposedError();
25512
+ };
25513
+ const prompt = async (input2) => {
25514
+ failIfPromptUnavailable();
25422
25515
  const started = requireStarted();
25423
25516
  let record2;
25424
25517
  const settle2 = (rest) => settleTurn({
@@ -25430,7 +25523,7 @@ function createExecutorPrompt({
25430
25523
  let turnId;
25431
25524
  await threadMutex.acquire();
25432
25525
  try {
25433
- failIfDisposed();
25526
+ failIfPromptUnavailable();
25434
25527
  turnId = selectPromptTurnId(
25435
25528
  input2,
25436
25529
  shared.promptsInFlight > 0 ? shared.activeTurnId : void 0,
@@ -25539,7 +25632,7 @@ function createExecutorPrompt({
25539
25632
  sessionEstablished: shared.startResult !== void 0,
25540
25633
  disposed: shared.disposed
25541
25634
  });
25542
- if (written && input2.onDispatched !== void 0 && !interruptedTurnIds.has(turnId)) {
25635
+ if (!interruptedTurnIds.has(turnId)) {
25543
25636
  const errorMessage = error62 instanceof Error && error62.message.trim() || "ACP prompt failed";
25544
25637
  if (settle2({
25545
25638
  turnId,
@@ -25547,7 +25640,7 @@ function createExecutorPrompt({
25547
25640
  state: "failed",
25548
25641
  errorMessage,
25549
25642
  ...failureCategory === void 0 ? {} : { failureCategory }
25550
- }))
25643
+ }) && written && input2.onDispatched !== void 0)
25551
25644
  options.log?.warn?.("turn failed after dispatch", {
25552
25645
  threadId: options.threadId,
25553
25646
  failure: errorMessage
@@ -25648,9 +25741,9 @@ function createPromptResults({
25648
25741
  }
25649
25742
  }
25650
25743
  };
25651
- const wait = async (turnId) => {
25744
+ const wait = async (turnId, includeUnreceived = false) => {
25652
25745
  const claimed = [...prompts.values()].filter(
25653
- (record2) => !record2.finished && record2.response !== void 0 && (turnId === void 0 || record2.turnId === turnId)
25746
+ (record2) => !record2.finished && (includeUnreceived || record2.response !== void 0) && (turnId === void 0 || record2.turnId === turnId)
25654
25747
  );
25655
25748
  if (claimed.length === 0) return;
25656
25749
  let timer;
@@ -25658,7 +25751,10 @@ function createPromptResults({
25658
25751
  const drained = await Promise.race([
25659
25752
  Promise.all(claimed.map((record2) => record2.done)).then(() => true),
25660
25753
  new Promise((resolve2) => {
25661
- timer = setTimeout(() => resolve2(false), options.resultDrainTimeoutMs ?? 2e3);
25754
+ timer = setTimeout(
25755
+ () => resolve2(false),
25756
+ options.resultDrainTimeoutMs ?? DEFAULT_RESULT_DRAIN_TIMEOUT_MS
25757
+ );
25662
25758
  })
25663
25759
  ]);
25664
25760
  if (drained) return;
@@ -25682,13 +25778,6 @@ function createPromptResults({
25682
25778
  return { add, receive, reject, stop, complete, hasInterrupted, forget, wait, finishUnreceived };
25683
25779
  }
25684
25780
 
25685
- // ../../packages/ycodium-acp-executor/src/user-input-pending.ts
25686
- var userInputAnswerOptionId = (question, answers) => {
25687
- const answer = answers[question.id];
25688
- const selected = typeof answer === "string" ? answer : Array.isArray(answer) && answer.length === 1 && typeof answer[0] === "string" ? answer[0] : void 0;
25689
- return selected !== void 0 && question.options.some((option) => option.id === selected) ? selected : void 0;
25690
- };
25691
-
25692
25781
  // ../../packages/ycodium-acp-executor/src/executor/requests.ts
25693
25782
  function createExecutorRequests({
25694
25783
  emit,
@@ -26252,27 +26341,6 @@ function acpInitializePayload(clientInfo, overrides) {
26252
26341
  };
26253
26342
  }
26254
26343
 
26255
- // ../../packages/ycodium-acp-executor/src/model-selection.ts
26256
- function normalizeModelId(value) {
26257
- return value?.trim() || void 0;
26258
- }
26259
- function modelSelectionStateFromSetup(setup) {
26260
- const sessionModels = isRecord(setup.models) ? setup.models : void 0;
26261
- const modelConfigId = extractModelConfigId(setup);
26262
- const strategy = sessionModels === void 0 && modelConfigId !== void 0 ? "config-option" : "session/set_model";
26263
- const modelConfigOption = modelConfigId === void 0 ? void 0 : findSessionConfigOption(setup.configOptions, modelConfigId);
26264
- const configModel = modelConfigOption?.currentValue;
26265
- const currentModelId = strategy === "config-option" ? typeof configModel === "string" ? normalizeModelId(configModel) : void 0 : typeof sessionModels?.["currentModelId"] === "string" ? normalizeModelId(sessionModels["currentModelId"]) : void 0;
26266
- return {
26267
- strategy,
26268
- sessionModelsAdvertised: sessionModels !== void 0,
26269
- modelConfigId,
26270
- currentModelId,
26271
- advertisedModelValues: strategy === "config-option" ? modelConfigOption === void 0 ? [] : collectSessionConfigOptionValues(modelConfigOption) : collectSessionModelIds(sessionModels),
26272
- configOptions: setup.configOptions
26273
- };
26274
- }
26275
-
26276
26344
  // ../../packages/ycodium-acp-executor/src/runtime-mode.ts
26277
26345
  function isRecord5(value) {
26278
26346
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -26869,47 +26937,6 @@ function createExecutorUpdates({
26869
26937
  return { emitCompactionState, handleSessionUpdate };
26870
26938
  }
26871
26939
 
26872
- // ../../packages/ycodium-acp-executor/src/mutex.ts
26873
- var Mutex = class {
26874
- locked = false;
26875
- waiters = [];
26876
- get isLocked() {
26877
- return this.locked;
26878
- }
26879
- acquire() {
26880
- if (!this.locked) {
26881
- this.locked = true;
26882
- return Promise.resolve();
26883
- }
26884
- return new Promise((resolve2) => {
26885
- this.waiters.push(resolve2);
26886
- });
26887
- }
26888
- tryAcquire() {
26889
- if (this.locked) {
26890
- return false;
26891
- }
26892
- this.locked = true;
26893
- return true;
26894
- }
26895
- release() {
26896
- const next = this.waiters.shift();
26897
- if (next) {
26898
- next();
26899
- return;
26900
- }
26901
- this.locked = false;
26902
- }
26903
- async runExclusive(fn) {
26904
- await this.acquire();
26905
- try {
26906
- return await fn();
26907
- } finally {
26908
- this.release();
26909
- }
26910
- }
26911
- };
26912
-
26913
26940
  // ../../packages/ycodium-acp-executor/src/openCommands.ts
26914
26941
  function readOpenToolCalls(toolCalls, turnId) {
26915
26942
  const running = [];
@@ -27281,10 +27308,10 @@ function createAcpExecutor(options) {
27281
27308
  setNativeMode,
27282
27309
  compact,
27283
27310
  openToolCalls: () => readOpenToolCalls(toolCalls, shared.activeTurnId),
27284
- dispose: () => {
27311
+ dispose: (exitCode) => {
27285
27312
  disposeOnce ??= (async () => {
27286
27313
  try {
27287
- await disposeLifecycle();
27314
+ await disposeLifecycle(exitCode);
27288
27315
  } finally {
27289
27316
  await protocolLogSink?.close();
27290
27317
  }
@@ -27295,7 +27322,8 @@ function createAcpExecutor(options) {
27295
27322
  }
27296
27323
 
27297
27324
  // ../../packages/ycodium-acp-executor/src/session-registry.ts
27298
- function createAcpSessionRegistry(threads, stopBooked2) {
27325
+ function createAcpSessionRegistry(threads, stopBooked2, log) {
27326
+ const exits = /* @__PURE__ */ new WeakMap();
27299
27327
  const requireThread = (threadId) => {
27300
27328
  const booked = threads.get(threadId);
27301
27329
  if (!booked) throw new Error(`unknown thread ${threadId}`);
@@ -27304,7 +27332,8 @@ function createAcpSessionRegistry(threads, stopBooked2) {
27304
27332
  };
27305
27333
  const stopSession = (threadId, booked = threads.get(threadId)) => {
27306
27334
  if (!booked) return Promise.resolve();
27307
- booked.closing ??= Promise.resolve().then(() => stopBooked2(booked)).then(() => {
27335
+ const close = { exitCode: exits.get(booked), hasExited: () => exits.has(booked) };
27336
+ booked.closing ??= Promise.resolve().then(() => stopBooked2(booked, close)).then(() => {
27308
27337
  if (threads.get(threadId) === booked) threads.delete(threadId);
27309
27338
  }).catch((cause) => {
27310
27339
  if (threads.get(threadId) === booked) delete booked.closing;
@@ -27315,7 +27344,16 @@ function createAcpSessionRegistry(threads, stopBooked2) {
27315
27344
  const stopAll = () => Promise.all([...threads].map(([threadId, booked]) => stopSession(threadId, booked))).then(
27316
27345
  () => void 0
27317
27346
  );
27318
- return { requireThread, stopSession, stopAll };
27347
+ const registerSession = (threadId, booked) => {
27348
+ threads.set(threadId, booked);
27349
+ void booked.handle.exited.then(async (exitCode) => {
27350
+ exits.set(booked, exitCode);
27351
+ await stopSession(threadId, booked);
27352
+ }).catch((cause) => {
27353
+ log?.warn("ACP process exit cleanup failed", { threadId, error: String(cause) });
27354
+ });
27355
+ };
27356
+ return { requireThread, registerSession, stopSession, stopAll };
27319
27357
  }
27320
27358
 
27321
27359
  // ../../packages/ycodium-acp-executor/src/session-cleanup.ts
@@ -27328,8 +27366,11 @@ async function cleanupAcpSession(input2) {
27328
27366
  failures.push(new Error(`forwarding failed: ${String(cause)}`, { cause }));
27329
27367
  });
27330
27368
  for (const [step, cleanup] of [
27331
- ["executor.dispose", () => input2.executor.dispose()],
27332
- ["stopProcess", input2.stopProcess],
27369
+ ["executor.dispose", () => input2.executor.dispose(input2.processExit?.exitCode)],
27370
+ [
27371
+ "stopProcess",
27372
+ () => input2.processExit?.hasExited() ? Promise.resolve() : input2.stopProcess()
27373
+ ],
27333
27374
  ["forwarding", () => forwarding]
27334
27375
  ]) {
27335
27376
  try {
@@ -27892,11 +27933,12 @@ function acpContinuationKey(recipe) {
27892
27933
  const resolvedHomePath = homePath.length > 0 ? NodePath4.resolve(expandHomePath(homePath)) : "";
27893
27934
  return `acp:home:${recipe.binaryPath.trim()}\0${resolvedHomePath}`;
27894
27935
  }
27895
- async function stopBooked(booked) {
27936
+ async function stopBooked(booked, processExit) {
27896
27937
  await cleanupAcpSession({
27897
27938
  executor: booked.executor,
27898
27939
  stopProcess: () => booked.handle.kill("SIGTERM"),
27899
- forwarding: booked.forwarding
27940
+ forwarding: booked.forwarding,
27941
+ processExit
27900
27942
  });
27901
27943
  }
27902
27944
  var createAcpInstance = (config2, host) => {
@@ -27904,7 +27946,11 @@ var createAcpInstance = (config2, host) => {
27904
27946
  const events = new EventBus();
27905
27947
  const threads = /* @__PURE__ */ new Map();
27906
27948
  let disposed = false;
27907
- const { requireThread, stopSession, stopAll } = createAcpSessionRegistry(threads, stopBooked);
27949
+ const { requireThread, registerSession, stopSession, stopAll } = createAcpSessionRegistry(
27950
+ threads,
27951
+ stopBooked,
27952
+ host.log
27953
+ );
27908
27954
  const disposeOnce = createRetryableClose(async () => {
27909
27955
  await stopAll();
27910
27956
  events.close();
@@ -28056,7 +28102,7 @@ var createAcpInstance = (config2, host) => {
28056
28102
  ...started.modelId === void 0 ? {} : { model: started.modelId },
28057
28103
  ...input2.bindingGeneration === void 0 ? {} : { bindingGeneration: input2.bindingGeneration }
28058
28104
  };
28059
- threads.set(input2.threadId, {
28105
+ registerSession(input2.threadId, {
28060
28106
  session,
28061
28107
  turns: [],
28062
28108
  executor,
@@ -28174,7 +28220,7 @@ var executors = [
28174
28220
  ];
28175
28221
  var index_default = definePlugin({
28176
28222
  id: "agent-acp",
28177
- apiVersion: 25,
28223
+ apiVersion: 26,
28178
28224
  executors,
28179
28225
  activate(ctx) {
28180
28226
  ctx.log.info("generic ACP executor ready");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-acp",
3
- "version": "0.2.3236",
3
+ "version": "0.2.3240",
4
4
  "description": "Generic ACP executor: user-supplied CLI over Agent Client Protocol stdio.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -21,9 +21,9 @@
21
21
  "@types/node": "24.12.4",
22
22
  "esbuild": "0.28.1",
23
23
  "vite-plus": "0.2.2",
24
- "@ycodium-ai/plugin-api": "0.2.3236",
25
- "ycodium-acp-executor": "0.2.3236",
26
- "ycodium-executor-testing": "0.0.0"
24
+ "@ycodium-ai/plugin-api": "0.2.3240",
25
+ "ycodium-executor-testing": "0.0.0",
26
+ "ycodium-acp-executor": "0.2.3240"
27
27
  },
28
28
  "ycodium": {
29
29
  "plugin": "./dist/index.js",