maka-agent 0.2.0-dev.67.20261002 → 0.2.0-dev.69.20261004

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 (28) hide show
  1. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  2. package/node_modules/@maka/acp-executor-plugin/dist/acp-catalog.js +132 -0
  3. package/node_modules/@maka/acp-executor-plugin/dist/index.js +251 -113
  4. package/node_modules/@maka/acp-executor-plugin/dist/plugin.mjs +431 -108
  5. package/node_modules/@maka/core/dist/executor-catalog.js +25 -3
  6. package/node_modules/@maka/core/dist/recall.js +13 -14
  7. package/node_modules/@maka/runtime/dist/admission-limiter.js +3 -2
  8. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +0 -3
  9. package/node_modules/@maka/runtime/dist/model-history.js +4 -14
  10. package/node_modules/@maka/runtime/dist/plugin-data-services.js +7 -0
  11. package/node_modules/@maka/runtime/dist/plugin-executor-service.js +19 -3
  12. package/node_modules/@maka/runtime/dist/runtime-read-model.js +23 -4
  13. package/node_modules/@maka/runtime/dist/session-manager.js +1 -0
  14. package/node_modules/@maka/runtime/package.json +1 -0
  15. package/node_modules/@maka/runtime-host/dist/antigravity-program.js +49 -0
  16. package/node_modules/@maka/runtime-host/dist/protocol/index.js +3 -1
  17. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +8 -2
  18. package/node_modules/@maka/runtime-host/dist/server/acp/antigravity.js +5 -19
  19. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +14 -3
  20. package/node_modules/@maka/runtime-host/dist/server/external-agent-setup-coordinator.js +3 -0
  21. package/node_modules/@maka/runtime-host/dist/server/plugin-data-runtime.js +6 -0
  22. package/node_modules/@maka/runtime-host/dist/server/plugin-scratch-directory.js +55 -0
  23. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +40 -6
  24. package/node_modules/@maka/runtime-host/package.json +1 -0
  25. package/node_modules/@maka/storage/dist/execution-stores.js +10 -0
  26. package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
  27. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +74 -5
  28. package/package.json +1 -1
@@ -7,9 +7,199 @@ var __export = (target, all) => {
7
7
  // src/index.ts
8
8
  import { createHash } from "node:crypto";
9
9
  import { constants as constants2, createReadStream } from "node:fs";
10
- import { access, mkdtemp, rm, realpath as realpath2, stat as stat2 } from "node:fs/promises";
10
+ import { access, mkdir, mkdtemp, realpath as realpath2, rm, stat as stat2 } from "node:fs/promises";
11
11
  import { tmpdir } from "node:os";
12
- import { dirname as dirname2, isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
12
+
13
+ // ../runtime/dist/admission-limiter.js
14
+ var AdmissionLimiter = class {
15
+ capacity;
16
+ active = 0;
17
+ waiters = [];
18
+ closedError;
19
+ constructor(capacity) {
20
+ this.capacity = capacity;
21
+ if (!Number.isSafeInteger(capacity) || capacity < 1) {
22
+ throw new Error("Admission capacity must be a positive safe integer");
23
+ }
24
+ }
25
+ get activeCount() {
26
+ return this.active;
27
+ }
28
+ get waitingCount() {
29
+ return this.waiters.length;
30
+ }
31
+ acquire(signal) {
32
+ if (this.closedError)
33
+ return Promise.reject(this.closedError);
34
+ if (signal.aborted)
35
+ return Promise.reject(abortReason(signal));
36
+ if (this.active < this.capacity && this.waiters.length === 0) {
37
+ this.active += 1;
38
+ return Promise.resolve(this.createPermit());
39
+ }
40
+ return new Promise((resolve3, reject) => {
41
+ const waiter = {
42
+ signal,
43
+ resolve: resolve3,
44
+ reject,
45
+ onAbort: () => {
46
+ const index = this.waiters.indexOf(waiter);
47
+ if (index < 0)
48
+ return;
49
+ this.waiters.splice(index, 1);
50
+ signal.removeEventListener("abort", waiter.onAbort);
51
+ reject(abortReason(signal));
52
+ }
53
+ };
54
+ this.waiters.push(waiter);
55
+ signal.addEventListener("abort", waiter.onAbort, { once: true });
56
+ });
57
+ }
58
+ close(error62) {
59
+ if (this.closedError)
60
+ return;
61
+ this.closedError = error62;
62
+ for (const waiter of this.waiters.splice(0)) {
63
+ waiter.signal.removeEventListener("abort", waiter.onAbort);
64
+ waiter.reject(error62);
65
+ }
66
+ }
67
+ createPermit() {
68
+ let released = false;
69
+ return {
70
+ release: () => {
71
+ if (released)
72
+ return;
73
+ released = true;
74
+ this.active -= 1;
75
+ this.grantWaiting();
76
+ }
77
+ };
78
+ }
79
+ grantWaiting() {
80
+ if (this.closedError)
81
+ return;
82
+ while (this.active < this.capacity && this.waiters.length > 0) {
83
+ const waiter = this.waiters.shift();
84
+ waiter.signal.removeEventListener("abort", waiter.onAbort);
85
+ if (waiter.signal.aborted) {
86
+ waiter.reject(abortReason(waiter.signal));
87
+ continue;
88
+ }
89
+ this.active += 1;
90
+ waiter.resolve(this.createPermit());
91
+ }
92
+ }
93
+ };
94
+ function abortReason(signal) {
95
+ return signal.reason instanceof Error ? signal.reason : new Error("Child agent run cancelled while waiting for runtime capacity");
96
+ }
97
+
98
+ // src/acp-catalog.ts
99
+ var CATALOG_TTL_MS = 6e4;
100
+ var PROBE_TIMEOUT_MS = 3e4;
101
+ var AcpCatalog = class {
102
+ constructor(admission, probe, unavailable) {
103
+ this.admission = admission;
104
+ this.probe = probe;
105
+ this.unavailable = unavailable;
106
+ }
107
+ admission;
108
+ probe;
109
+ unavailable;
110
+ #cached;
111
+ #pending;
112
+ #tail;
113
+ #operations = /* @__PURE__ */ new Set();
114
+ #revision = 0;
115
+ #disposed = false;
116
+ async get(signal, refresh = false) {
117
+ signal.throwIfAborted();
118
+ if (this.#disposed) return this.unavailable();
119
+ if (refresh) this.invalidate();
120
+ while (true) {
121
+ signal.throwIfAborted();
122
+ if (this.#disposed) return this.unavailable();
123
+ const cached2 = this.#cached;
124
+ if (cached2 && cached2.expires > Date.now()) return cached2.entry;
125
+ const pending = this.#ensureProbe();
126
+ const result = await waitForSignal(pending.promise, signal);
127
+ signal.throwIfAborted();
128
+ if (this.#disposed) return this.unavailable();
129
+ if (pending.revision !== this.#revision) continue;
130
+ return result;
131
+ }
132
+ }
133
+ #ensureProbe() {
134
+ if (!this.#pending) {
135
+ const revision = this.#revision;
136
+ const controller = new AbortController();
137
+ const startup = AbortSignal.any([controller.signal, AbortSignal.timeout(PROBE_TIMEOUT_MS)]);
138
+ const previous = this.#tail;
139
+ const run = async () => {
140
+ try {
141
+ await previous;
142
+ startup.throwIfAborted();
143
+ const permit = await this.admission.acquire(startup);
144
+ let entry;
145
+ try {
146
+ startup.throwIfAborted();
147
+ entry = await this.probe(startup);
148
+ } finally {
149
+ permit.release();
150
+ }
151
+ if (startup.aborted || revision !== this.#revision || this.#disposed)
152
+ return this.unavailable();
153
+ if (entry.readiness === "ready")
154
+ this.#cached = { entry, expires: Date.now() + CATALOG_TTL_MS };
155
+ return entry;
156
+ } catch {
157
+ return this.unavailable();
158
+ }
159
+ };
160
+ const promise2 = run().finally(() => {
161
+ this.#operations.delete(promise2);
162
+ if (this.#pending?.promise === promise2) this.#pending = void 0;
163
+ if (this.#tail === promise2) this.#tail = void 0;
164
+ });
165
+ this.#pending = { revision, controller, promise: promise2 };
166
+ this.#tail = promise2;
167
+ this.#operations.add(promise2);
168
+ }
169
+ return this.#pending;
170
+ }
171
+ invalidate() {
172
+ this.#revision++;
173
+ this.#cached = void 0;
174
+ this.#pending?.controller.abort(new Error("ACP catalog invalidated"));
175
+ this.#pending = void 0;
176
+ }
177
+ async dispose() {
178
+ this.#disposed = true;
179
+ this.invalidate();
180
+ await Promise.allSettled(this.#operations);
181
+ }
182
+ };
183
+ async function waitForSignal(promise2, signal) {
184
+ signal.throwIfAborted();
185
+ return await new Promise((resolve3, reject) => {
186
+ const aborted2 = () => reject(signal.reason);
187
+ signal.addEventListener("abort", aborted2, { once: true });
188
+ void promise2.then(
189
+ (value) => {
190
+ signal.removeEventListener("abort", aborted2);
191
+ resolve3(value);
192
+ },
193
+ (error62) => {
194
+ signal.removeEventListener("abort", aborted2);
195
+ reject(error62);
196
+ }
197
+ );
198
+ });
199
+ }
200
+
201
+ // src/index.ts
202
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join, resolve as resolve2 } from "node:path";
13
203
 
14
204
  // ../../node_modules/@agentclientprotocol/sdk/dist/schema/index.js
15
205
  var AGENT_METHODS = {
@@ -23412,6 +23602,28 @@ var legacyClientNotificationMethods = /* @__PURE__ */ new Set([
23412
23602
  CLIENT_METHODS.elicitation_complete
23413
23603
  ]);
23414
23604
 
23605
+ // ../runtime/dist/plugin-runtime.js
23606
+ var MakaPluginRuntimeError = class extends Error {
23607
+ code;
23608
+ name = "MakaPluginRuntimeError";
23609
+ constructor(code, message, options) {
23610
+ super(message, options);
23611
+ this.code = code;
23612
+ }
23613
+ };
23614
+ function pluginIdentity(ctx) {
23615
+ const metadata = ctx.maka;
23616
+ if (!metadata) {
23617
+ throw new MakaPluginRuntimeError("activation_failed", "Contribution registration requires a composition entry Context");
23618
+ }
23619
+ return Object.freeze({
23620
+ entryId: metadata.entryId,
23621
+ scopeId: metadata.rootId,
23622
+ extensionId: metadata.packageId,
23623
+ generation: metadata.generation
23624
+ });
23625
+ }
23626
+
23415
23627
  // src/acp-errors.ts
23416
23628
  var AcpRuntimeError = class extends Error {
23417
23629
  constructor(message, code) {
@@ -23889,7 +24101,8 @@ var AcpExecutor = class {
23889
24101
  #sessions = /* @__PURE__ */ new Map();
23890
24102
  #disposed = false;
23891
24103
  #catalog;
23892
- #discovery;
24104
+ #withCatalogDirectory;
24105
+ #temporaryDirectory;
23893
24106
  constructor(adapter, config2, options = {}) {
23894
24107
  this.#adapter = validateAdapter(adapter);
23895
24108
  this.id = adapter.id;
@@ -23897,79 +24110,107 @@ var AcpExecutor = class {
23897
24110
  this.#configured = validateConfiguredAgent(adapter.configure(config2));
23898
24111
  this.#createConnection = options.createConnection ?? createAcpConnection;
23899
24112
  this.#state = options.state;
24113
+ this.#withCatalogDirectory = options.withCatalogDirectory ?? (async (signal, use) => {
24114
+ signal.throwIfAborted();
24115
+ this.#temporaryDirectory ??= mkdtemp(join(tmpdir(), "maka-acp-catalog-"));
24116
+ const directory = await this.#temporaryDirectory;
24117
+ await mkdir(directory, { recursive: true, mode: 448 });
24118
+ try {
24119
+ return await use(await realpath2(directory));
24120
+ } finally {
24121
+ await rm(directory, { recursive: true, force: true });
24122
+ }
24123
+ });
24124
+ this.#catalog = new AcpCatalog(
24125
+ options.catalogAdmission ?? new AdmissionLimiter(1),
24126
+ (signal) => this.#probeCatalog(signal),
24127
+ () => this.#catalogEntry("unavailable")
24128
+ );
23900
24129
  }
23901
24130
  async discover(input2) {
23902
- if (this.#disposed) return this.#catalogEntry("unavailable");
23903
- if (this.#catalog) return this.#catalog;
23904
- if (this.#discovery) return this.#discovery;
23905
- const probe = async () => {
23906
- const cwd = await mkdtemp(resolve2(tmpdir(), "maka-acp-catalog-"));
23907
- const session = {
24131
+ return await this.#catalog.get(input2.signal, input2.refresh);
24132
+ }
24133
+ async #probeCatalog(signal) {
24134
+ return await this.#withCatalogDirectory(
24135
+ signal,
24136
+ (directory) => this.#readCatalog(directory, signal)
24137
+ );
24138
+ }
24139
+ async #readCatalog(directory, signal) {
24140
+ let session;
24141
+ try {
24142
+ signal.throwIfAborted();
24143
+ session = {
23908
24144
  conversationKey: "catalog-probe",
23909
- cwd,
24145
+ cwd: await realpath2(directory),
23910
24146
  configOptions: [],
23911
24147
  lost: false
23912
24148
  };
23913
- try {
23914
- await this.#initialize(session, input2.signal, true);
23915
- const result = this.#catalogEntry("ready", session.configOptions);
23916
- this.#catalog = result;
23917
- return result;
23918
- } catch (error62) {
23919
- return this.#catalogEntry(
23920
- error62?.code === -32e3 ? "authentication_required" : "unavailable"
23921
- );
23922
- } finally {
23923
- await this.#disposeSession(session);
23924
- await rm(cwd, { recursive: true, force: true });
23925
- }
23926
- };
23927
- this.#discovery = probe().finally(() => {
23928
- this.#discovery = void 0;
23929
- });
23930
- return this.#discovery;
24149
+ await this.#initialize(session, signal, true);
24150
+ return this.#catalogEntry("ready", session.configOptions);
24151
+ } catch (error62) {
24152
+ return this.#catalogEntry(
24153
+ isAuthenticationFailure(error62) ? "authentication_required" : "unavailable"
24154
+ );
24155
+ } finally {
24156
+ if (session) await (session.loss ?? this.#disposeSession(session));
24157
+ }
24158
+ }
24159
+ invalidateCatalog() {
24160
+ this.#catalog.invalidate();
23931
24161
  }
23932
24162
  async inspectConversation(input2) {
23933
24163
  const session = this.#sessions.get(input2.conversationKey);
23934
24164
  if (this.#disposed) return this.#catalogEntry("unavailable");
23935
24165
  if (session && session.cwd !== resolve2(input2.cwd))
23936
- return this.#catalogEntry("history_only", [], input2.configuration?.model);
24166
+ return this.#catalogEntry("history_only", [], input2.configuration);
23937
24167
  if (session?.restoring)
23938
- return this.#catalogEntry("restoring", session.configOptions, input2.configuration?.model);
24168
+ return this.#catalogEntry("restoring", session.configOptions, input2.configuration);
23939
24169
  if (session?.restoreFailed)
23940
- return this.#catalogEntry(
23941
- "restore_failed",
23942
- session.configOptions,
23943
- input2.configuration?.model
23944
- );
24170
+ return this.#catalogEntry("restore_failed", session.configOptions, input2.configuration);
23945
24171
  if (session?.historyGap)
23946
- return this.#catalogEntry("history_gap", session.configOptions, input2.configuration?.model);
24172
+ return this.#catalogEntry("history_gap", session.configOptions, input2.configuration);
23947
24173
  if (session && !session.lost)
23948
- return this.#catalogEntry("ready", session.configOptions, input2.configuration?.model);
24174
+ return this.#catalogEntry("ready", session.configOptions, input2.configuration);
23949
24175
  if (session?.lost && !session.record)
23950
- return this.#catalogEntry("history_only", session.configOptions, input2.configuration?.model);
24176
+ return this.#catalogEntry("history_only", session.configOptions, input2.configuration);
23951
24177
  const stored = this.#state?.read ? decodeContinuity(await this.#state.read(input2.conversationKey)) : await this.#state?.has(input2.conversationKey, input2.cwd) ? "legacy" : void 0;
23952
- if (!stored) return this.#catalogEntry("ready", [], input2.configuration?.model);
24178
+ if (!stored) return this.#catalogEntry("ready", [], input2.configuration);
23953
24179
  if (stored === "invalid" || stored === "legacy" || stored.cwd !== resolve2(input2.cwd))
23954
- return this.#catalogEntry("history_only", [], input2.configuration?.model);
24180
+ return this.#catalogEntry("history_only", [], input2.configuration);
23955
24181
  if (stored.phase === "reserved")
23956
- return this.#catalogEntry("history_only", [], input2.configuration?.model);
24182
+ return this.#catalogEntry("history_only", [], input2.configuration);
23957
24183
  return this.#catalogEntry(
23958
24184
  stored.phase === "prompt_pending" || stored.phase === "history_gap" ? "history_gap" : "restorable",
23959
24185
  [],
23960
- input2.configuration?.model ?? stored.confirmedModel
24186
+ {
24187
+ model: input2.configuration?.model ?? stored.confirmedModel,
24188
+ mode: input2.configuration?.mode ?? stored.confirmedMode
24189
+ }
23961
24190
  );
23962
24191
  }
23963
24192
  async configureConversation(input2, signal) {
23964
- if (!input2.configuration?.model) throw new Error("ACP model change is unavailable");
24193
+ if (!input2.configuration?.model && !input2.configuration?.mode)
24194
+ throw new Error("ACP configuration change is unavailable");
23965
24195
  const session = await this.#session(input2);
23966
- if (session.active || session.configuring) throw new Error("ACP model change is unavailable");
24196
+ if (session.active || session.configuring || session.restoring || session.awaitingAck)
24197
+ throw new Error("ACP configuration change is unavailable while the Session is busy");
23967
24198
  session.configuring = true;
23968
24199
  const wasConnected = !!session.connection;
23969
24200
  try {
23970
- await this.#ensureInitialized(session, signal);
23971
- await this.#applyInitialConfig(session, { model: input2.configuration.model }, signal, true);
24201
+ await this.#ensureInitialized(session, signal, true);
24202
+ await this.#applyInitialConfig(
24203
+ session,
24204
+ !wasConnected && !session.restoredFromState ? { ...this.#configured.launch.initialConfig, ...input2.configuration } : input2.configuration,
24205
+ signal,
24206
+ true
24207
+ );
24208
+ return {
24209
+ ...currentAcpModel(session.configOptions) ? { model: currentAcpModel(session.configOptions) } : {},
24210
+ ...acpOption(session.configOptions, "mode")?.currentValue ? { mode: acpOption(session.configOptions, "mode").currentValue } : {}
24211
+ };
23972
24212
  } catch (error62) {
24213
+ if (isAuthenticationFailure(error62)) this.invalidateCatalog();
23973
24214
  if (!wasConnected || session.lost) {
23974
24215
  await this.#lose(session);
23975
24216
  if (session.record?.sessionId && !session.historyGap) session.restoreFailed = true;
@@ -23986,16 +24227,24 @@ var AcpExecutor = class {
23986
24227
  const model = options.find(
23987
24228
  (option) => option.type === "select" && (option.category === "model" || option.id === "model")
23988
24229
  );
23989
- const models = model?.type === "select" ? model.options.flatMap((entry) => "options" in entry ? entry.options : [entry]).map((entry) => ({ id: entry.value, name: entry.name })) : this.#catalog?.models ?? [];
24230
+ const models = model?.type === "select" ? model.options.flatMap((entry) => "options" in entry ? entry.options : [entry]).map((entry) => ({ id: entry.value, name: entry.name })) : selected?.model ? [{ id: selected.model, name: selected.model }] : [];
24231
+ const mode = options.find(
24232
+ (option) => option.type === "select" && (option.category === "mode" || option.id === "mode")
24233
+ );
24234
+ const modes = mode?.type === "select" ? mode.options.flatMap((entry) => "options" in entry ? entry.options : [entry]).map((entry) => ({ id: entry.value, name: entry.name })) : selected?.mode ? [{ id: selected.mode, name: selected.mode }] : [];
23990
24235
  return {
23991
24236
  id: this.id,
23992
24237
  displayName: this.displayName,
23993
24238
  readiness,
23994
24239
  ...model?.type === "select" ? this.#adapter.describeModels?.(models) ?? { models } : {
23995
- models,
23996
- ...this.#catalog?.modelGroups ? { modelGroups: this.#catalog.modelGroups } : {}
24240
+ models
24241
+ },
24242
+ ...model?.type === "select" ? { currentModel: model.currentValue } : selected?.model ? { currentModel: selected.model } : {},
24243
+ ...mode?.type === "select" ? { modes, currentMode: mode.currentValue, supportsModeChange: true } : {
24244
+ modes,
24245
+ ...selected?.mode ? { currentMode: selected.mode } : {},
24246
+ supportsModeChange: false
23997
24247
  },
23998
- ...model?.type === "select" ? { currentModel: model.currentValue } : selected ? { currentModel: selected } : {},
23999
24248
  supportsAttachments: false,
24000
24249
  supportsModelChange: model?.type === "select"
24001
24250
  };
@@ -24011,7 +24260,7 @@ var AcpExecutor = class {
24011
24260
  if (request.configuration?.model && request.model && request.model !== this.id && request.configuration.model !== request.model)
24012
24261
  return failure2("Conflicting executor models", "acp_config_invalid");
24013
24262
  const model = request.configuration?.model ?? (request.model === this.id ? void 0 : request.model);
24014
- if (model) request = { ...request, configuration: { model } };
24263
+ if (model) request = { ...request, configuration: { ...request.configuration, model } };
24015
24264
  let session;
24016
24265
  try {
24017
24266
  session = await this.#session(request);
@@ -24025,12 +24274,8 @@ var AcpExecutor = class {
24025
24274
  try {
24026
24275
  await this.#ensureInitialized(session, context.signal);
24027
24276
  context.signal.throwIfAborted();
24028
- if (request.configuration?.model)
24029
- await this.#applyInitialConfig(
24030
- session,
24031
- { model: request.configuration.model },
24032
- context.signal
24033
- );
24277
+ if (request.configuration?.model || request.configuration?.mode)
24278
+ await this.#applyInitialConfig(session, request.configuration, context.signal);
24034
24279
  await this.#beginPrompt(session, request.turnId);
24035
24280
  const prompt = session.connection.agent.request(methods.agent.session.prompt, {
24036
24281
  sessionId: session.acpSessionId,
@@ -24053,6 +24298,7 @@ var AcpExecutor = class {
24053
24298
  }
24054
24299
  return { status: "completed", text: active.text };
24055
24300
  } catch (error62) {
24301
+ if (isAuthenticationFailure(error62)) this.invalidateCatalog();
24056
24302
  if (errorCode(error62) === "acp_history_gap") session.historyGap = true;
24057
24303
  await this.#lose(session);
24058
24304
  if (session.record?.sessionId && !session.historyGap) session.restoreFailed = true;
@@ -24076,6 +24322,7 @@ var AcpExecutor = class {
24076
24322
  async dispose() {
24077
24323
  if (this.#disposed) return;
24078
24324
  this.#disposed = true;
24325
+ await this.#catalog.dispose();
24079
24326
  const sessions = [...this.#sessions.values()];
24080
24327
  this.#sessions.clear();
24081
24328
  const settlements = await Promise.allSettled(
@@ -24114,9 +24361,9 @@ var AcpExecutor = class {
24114
24361
  this.#sessions.set(request.conversationKey, created);
24115
24362
  return created;
24116
24363
  }
24117
- async #ensureInitialized(session, signal) {
24364
+ async #ensureInitialized(session, signal, deferSelectedConfig = false) {
24118
24365
  if (session.initialization) return await session.initialization;
24119
- const initialization = this.#initialize(session, signal);
24366
+ const initialization = this.#initialize(session, signal, false, deferSelectedConfig);
24120
24367
  session.initialization = initialization;
24121
24368
  try {
24122
24369
  await initialization;
@@ -24127,7 +24374,7 @@ var AcpExecutor = class {
24127
24374
  throw error62;
24128
24375
  }
24129
24376
  }
24130
- async #initialize(session, signal, probe = false) {
24377
+ async #initialize(session, signal, probe = false, deferSelectedConfig = false) {
24131
24378
  const timeout = AbortSignal.timeout(INITIALIZE_TIMEOUT_MS);
24132
24379
  const startupSignal = AbortSignal.any([signal, timeout]);
24133
24380
  startupSignal.throwIfAborted();
@@ -24148,6 +24395,7 @@ var AcpExecutor = class {
24148
24395
  "External Session history cannot be aligned with saved conversation events",
24149
24396
  "acp_history_gap"
24150
24397
  );
24398
+ session.restoredFromState = !!stored;
24151
24399
  if (stored) {
24152
24400
  session.record = stored;
24153
24401
  session.acpSessionId = stored.sessionId;
@@ -24204,7 +24452,27 @@ var AcpExecutor = class {
24204
24452
  ]);
24205
24453
  session.configOptions = restored.configOptions ?? [];
24206
24454
  const restoredModel = currentAcpModel(session.configOptions);
24207
- if (restoredModel) await this.#persistConfirmedModel(session, restoredModel);
24455
+ const restoredMode = acpOption(session.configOptions, "mode")?.currentValue;
24456
+ if (stored.confirmedModel && restoredModel !== stored.confirmedModel || stored.confirmedMode && restoredMode !== stored.confirmedMode) {
24457
+ const confirmed = await this.#applyInitialConfig(
24458
+ session,
24459
+ {
24460
+ ...stored.confirmedModel ? { model: stored.confirmedModel } : {},
24461
+ ...stored.confirmedMode ? { mode: stored.confirmedMode } : {}
24462
+ },
24463
+ startupSignal,
24464
+ false,
24465
+ // Only an explicit model-only configuration can recover a removed
24466
+ // saved mode. Ordinary execution still requires the exact saved pair.
24467
+ deferSelectedConfig && !!session.configuration?.model && !session.configuration.mode
24468
+ );
24469
+ if (confirmed.model && currentAcpModel(session.configOptions) !== confirmed.model || confirmed.mode && acpOption(session.configOptions, "mode")?.currentValue !== confirmed.mode)
24470
+ throw new AcpRuntimeError(
24471
+ "Restored Agent configuration differs from the saved task",
24472
+ "acp_config_unconfirmed"
24473
+ );
24474
+ }
24475
+ await this.#persistConfirmedConfig(session);
24208
24476
  session.restoring = false;
24209
24477
  if (pending) {
24210
24478
  session.historyGap = true;
@@ -24261,13 +24529,12 @@ var AcpExecutor = class {
24261
24529
  };
24262
24530
  await this.#state?.write?.(session.conversationKey, established);
24263
24531
  session.record = established;
24264
- const createdModel = currentAcpModel(session.configOptions);
24265
- if (createdModel) await this.#persistConfirmedModel(session, createdModel);
24532
+ await this.#persistConfirmedConfig(session);
24266
24533
  }
24267
- if (!probe) {
24534
+ if (!probe && !deferSelectedConfig) {
24268
24535
  await this.#applyInitialConfig(
24269
24536
  session,
24270
- session.configuration?.model ? { model: session.configuration.model } : launch.initialConfig ?? {},
24537
+ session.configuration?.model || session.configuration?.mode ? { ...launch.initialConfig, ...session.configuration } : launch.initialConfig ?? {},
24271
24538
  startupSignal
24272
24539
  );
24273
24540
  }
@@ -24321,71 +24588,104 @@ var AcpExecutor = class {
24321
24588
  await this.#lose(session);
24322
24589
  }
24323
24590
  }
24324
- async #applyInitialConfig(session, values, signal, restoreOnFailure = false) {
24325
- for (const [key, value] of Object.entries(values)) {
24326
- const option = session.configOptions.find(
24327
- (candidate) => candidate.type === "select" && (candidate.id === key || candidate.category === key)
24328
- );
24329
- if (!option || option.type !== "select")
24591
+ async #applyInitialConfig(session, values, signal, restoreOnFailure = false, omitUnavailableMode = false) {
24592
+ let target = Object.entries(values).filter(
24593
+ (entry) => entry[1] !== void 0
24594
+ );
24595
+ const before = session.configOptions;
24596
+ const selectedModel = target.find(([key]) => key === "model")?.[1];
24597
+ const changingModel = selectedModel !== void 0 && acpOption(before, "model")?.currentValue !== selectedModel;
24598
+ const validate2 = (options, key, value) => {
24599
+ const option = acpOption(options, key);
24600
+ if (!option)
24330
24601
  throw new AcpRuntimeError(
24331
24602
  `ACP configuration is unavailable: ${key}`,
24332
24603
  "acp_config_unavailable"
24333
24604
  );
24334
- const options = option.options.flatMap(
24605
+ const candidates = option.options.flatMap(
24335
24606
  (entry) => "options" in entry ? entry.options : [entry]
24336
24607
  );
24337
- if (!options.some((entry) => entry.value === value))
24608
+ if (!candidates.some((entry) => entry.value === value))
24338
24609
  throw new AcpRuntimeError(
24339
24610
  `ACP configuration value is unavailable: ${key}`,
24340
24611
  "acp_config_invalid"
24341
24612
  );
24342
- if (option.currentValue === value) {
24343
- if (key === "model") await this.#persistConfirmedModel(session, value);
24344
- continue;
24345
- }
24346
- signal.throwIfAborted();
24347
- try {
24613
+ };
24614
+ for (const [key, value] of target) {
24615
+ if (omitUnavailableMode && key === "mode") continue;
24616
+ if (!changingModel || key === "model") validate2(before, key, value);
24617
+ }
24618
+ const ordered = [
24619
+ ...target.filter(([key]) => key === "model"),
24620
+ ...target.filter(([key]) => key !== "model")
24621
+ ];
24622
+ let mutationAttempted = false;
24623
+ try {
24624
+ for (const [key, value] of ordered) {
24625
+ const option = acpOption(session.configOptions, key);
24626
+ if (omitUnavailableMode && key === "mode" && !option?.options.flatMap((entry) => "options" in entry ? entry.options : [entry]).some((entry) => entry.value === value)) {
24627
+ target = target.filter(([candidate]) => candidate !== "mode");
24628
+ continue;
24629
+ }
24630
+ validate2(session.configOptions, key, value);
24631
+ if (!option) throw new Error("Validated ACP option disappeared");
24632
+ if (option.currentValue === value) continue;
24633
+ signal.throwIfAborted();
24634
+ mutationAttempted = true;
24348
24635
  const updated = await session.connection.agent.request(
24349
24636
  methods.agent.session.setConfigOption,
24350
24637
  { sessionId: session.acpSessionId, configId: option.id, value },
24351
24638
  { cancellationSignal: signal }
24352
24639
  );
24353
- const confirmed = updated.configOptions.find((candidate) => candidate.id === option.id);
24354
- if (confirmed?.type !== "select" || confirmed.currentValue !== value)
24640
+ session.configOptions = updated.configOptions;
24641
+ if (acpOption(updated.configOptions, key)?.currentValue !== value)
24355
24642
  throw new AcpRuntimeError(
24356
24643
  "Agent did not confirm the selected configuration",
24357
24644
  "acp_config_unconfirmed"
24358
24645
  );
24359
- session.configOptions = updated.configOptions;
24360
- if (key === "model") await this.#persistConfirmedModel(session, value);
24361
- } catch (error62) {
24362
- let restored = false;
24363
- if (restoreOnFailure && !session.lost && !signal.aborted) {
24364
- try {
24646
+ }
24647
+ if (target.some(([key, value]) => acpOption(session.configOptions, key)?.currentValue !== value))
24648
+ throw new AcpRuntimeError(
24649
+ "Agent changed another selected configuration",
24650
+ "acp_config_unconfirmed"
24651
+ );
24652
+ await this.#persistConfirmedConfig(session);
24653
+ return Object.fromEntries(target);
24654
+ } catch (error62) {
24655
+ let restored = false;
24656
+ if (restoreOnFailure && !session.lost && !signal.aborted) {
24657
+ try {
24658
+ for (const key of ["model", "mode"]) {
24659
+ const original = acpOption(before, key);
24660
+ const current = acpOption(session.configOptions, key);
24661
+ if (!original || !current || !mutationAttempted && current.currentValue === original.currentValue)
24662
+ continue;
24365
24663
  const rollback = await session.connection.agent.request(
24366
24664
  methods.agent.session.setConfigOption,
24367
- { sessionId: session.acpSessionId, configId: option.id, value: option.currentValue },
24665
+ {
24666
+ sessionId: session.acpSessionId,
24667
+ configId: current.id,
24668
+ value: original.currentValue
24669
+ },
24368
24670
  { cancellationSignal: AbortSignal.timeout(5e3) }
24369
24671
  );
24370
- const confirmed = rollback.configOptions.find(
24371
- (candidate) => candidate.id === option.id
24372
- );
24373
- if (confirmed?.type === "select" && confirmed.currentValue === option.currentValue && !session.lost) {
24374
- session.configOptions = rollback.configOptions;
24375
- if (key === "model") await this.#persistConfirmedModel(session, option.currentValue);
24376
- restored = true;
24377
- }
24378
- } catch {
24672
+ session.configOptions = rollback.configOptions;
24379
24673
  }
24674
+ restored = ["model", "mode"].every((key) => {
24675
+ const original = acpOption(before, key);
24676
+ return !original || acpOption(session.configOptions, key)?.currentValue === original.currentValue;
24677
+ });
24678
+ } catch {
24380
24679
  }
24381
- if (!restored) await this.#lose(session);
24382
- throw error62;
24383
24680
  }
24681
+ if (!restored) await this.#lose(session);
24682
+ throw error62;
24384
24683
  }
24385
24684
  }
24386
- async #persistConfirmedModel(session, model) {
24387
- if (!session.record || session.record.confirmedModel === model) return;
24388
- const record2 = { ...session.record, confirmedModel: model };
24685
+ async #persistConfirmedConfig(session, configOptions = session.configOptions) {
24686
+ if (!session.record) return;
24687
+ const record2 = withConfirmedConfiguration(session.record, configOptions);
24688
+ if (record2 === session.record) return;
24389
24689
  await this.#state?.write?.(session.conversationKey, record2);
24390
24690
  session.record = record2;
24391
24691
  }
@@ -24430,7 +24730,9 @@ var AcpExecutor = class {
24430
24730
  }
24431
24731
  if (session.historyGap || session.lost) return;
24432
24732
  if (update.sessionUpdate === "config_option_update") {
24433
- if (!session.configuring && !session.lost) session.configOptions = update.configOptions;
24733
+ if (!session.configuring && !session.lost) {
24734
+ session.configOptions = update.configOptions;
24735
+ }
24434
24736
  return;
24435
24737
  }
24436
24738
  const active = session.active;
@@ -24555,10 +24857,26 @@ var AcpExecutor = class {
24555
24857
  }
24556
24858
  };
24557
24859
  function currentAcpModel(options) {
24558
- const option = options.find(
24559
- (candidate) => candidate.type === "select" && (candidate.id === "model" || candidate.category === "model")
24860
+ return acpOption(options, "model")?.currentValue;
24861
+ }
24862
+ function withConfirmedConfiguration(record2, options) {
24863
+ const model = currentAcpModel(options);
24864
+ const mode = acpOption(options, "mode")?.currentValue;
24865
+ const { confirmedModel, confirmedMode, ...base } = record2;
24866
+ if (confirmedModel === model && confirmedMode === mode) return record2;
24867
+ return {
24868
+ ...base,
24869
+ ...model ? { confirmedModel: model } : {},
24870
+ ...mode ? { confirmedMode: mode } : {}
24871
+ };
24872
+ }
24873
+ function isAuthenticationFailure(error62) {
24874
+ return error62?.code === -32e3 || error62 instanceof Error && /authentication required|unauthorized/u.test(error62.message.toLowerCase());
24875
+ }
24876
+ function acpOption(options, key) {
24877
+ return options.find(
24878
+ (candidate) => candidate.type === "select" && (candidate.id === key || candidate.category === key)
24560
24879
  );
24561
- return option?.type === "select" ? option.currentValue : void 0;
24562
24880
  }
24563
24881
  function toolTextContent(content) {
24564
24882
  return content.flatMap(
@@ -24566,14 +24884,19 @@ function toolTextContent(content) {
24566
24884
  ).join("\n");
24567
24885
  }
24568
24886
  var AcpRuntimeService = class {
24887
+ // Shared by every adapter below this ACP runtime Entry, including queued probes.
24888
+ #catalogAdmission = new AdmissionLimiter(2);
24569
24889
  constructor(ctx) {
24570
24890
  ctx.provide("acp", this);
24571
24891
  }
24572
24892
  register(consumer, adapter, config2) {
24573
24893
  const storage = consumer.get("storage");
24574
24894
  if (!storage) throw new Error("ACP continuity storage is unavailable");
24895
+ const directoryKey = `acp-catalog/${adapter.id}/${createHash("sha256").update(pluginIdentity(consumer).entryId).digest("hex")}`;
24575
24896
  const provider = new AcpExecutor(adapter, config2, {
24576
- state: pluginStateStore(storage, adapter.id)
24897
+ state: pluginStateStore(storage, adapter.id),
24898
+ catalogAdmission: this.#catalogAdmission,
24899
+ withCatalogDirectory: (signal, use) => storage.withScratchDirectory(directoryKey, signal, use)
24577
24900
  });
24578
24901
  consumer.effect(() => () => provider.dispose(), `acp.dispose(${JSON.stringify(adapter.id)})`);
24579
24902
  return consumer.executors.register(provider);
@@ -24603,7 +24926,7 @@ function decodeContinuity(value) {
24603
24926
  if (record2.version === 1) return "legacy";
24604
24927
  if (record2.version !== 2 || typeof record2.cwd !== "string" || !isAbsolute2(record2.cwd) || typeof record2.binding !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(record2.binding) || !Number.isSafeInteger(record2.committedPrompts) || record2.committedPrompts < 0 || !["reserved", "established", "prompt_pending", "committed", "history_gap"].includes(
24605
24928
  String(record2.phase)
24606
- ) || record2.sessionId !== void 0 && (typeof record2.sessionId !== "string" || !record2.sessionId || record2.sessionId.length > 4096) || record2.pendingTurnId !== void 0 && (typeof record2.pendingTurnId !== "string" || !record2.pendingTurnId) || record2.confirmedModel !== void 0 && (typeof record2.confirmedModel !== "string" || !record2.confirmedModel) || record2.phase === "reserved" && record2.sessionId !== void 0 || record2.phase !== "reserved" && !record2.sessionId || record2.phase === "prompt_pending" && !record2.pendingTurnId || record2.phase !== "prompt_pending" && record2.pendingTurnId !== void 0 || record2.gapEvidence !== void 0 && (record2.phase !== "history_gap" || !record2.gapEvidence || typeof record2.gapEvidence !== "object" || !Number.isSafeInteger(record2.gapEvidence.replayedUpdates) || !Number.isSafeInteger(record2.gapEvidence.replayedUserChunks) || record2.gapEvidence.replayedUpdates < 0 || record2.gapEvidence.replayedUserChunks < 0))
24929
+ ) || record2.sessionId !== void 0 && (typeof record2.sessionId !== "string" || !record2.sessionId || record2.sessionId.length > 4096) || record2.pendingTurnId !== void 0 && (typeof record2.pendingTurnId !== "string" || !record2.pendingTurnId) || record2.confirmedModel !== void 0 && (typeof record2.confirmedModel !== "string" || !record2.confirmedModel) || record2.confirmedMode !== void 0 && (typeof record2.confirmedMode !== "string" || !record2.confirmedMode) || record2.phase === "reserved" && record2.sessionId !== void 0 || record2.phase !== "reserved" && !record2.sessionId || record2.phase === "prompt_pending" && !record2.pendingTurnId || record2.phase !== "prompt_pending" && record2.pendingTurnId !== void 0 || record2.gapEvidence !== void 0 && (record2.phase !== "history_gap" || !record2.gapEvidence || typeof record2.gapEvidence !== "object" || !Number.isSafeInteger(record2.gapEvidence.replayedUpdates) || !Number.isSafeInteger(record2.gapEvidence.replayedUserChunks) || record2.gapEvidence.replayedUpdates < 0 || record2.gapEvidence.replayedUserChunks < 0))
24607
24930
  return "invalid";
24608
24931
  return record2;
24609
24932
  }