zidane 5.13.4 → 5.13.6

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.
@@ -7,8 +7,10 @@ import { l as errorMessage } from "./errors-BpPfMo_4.js";
7
7
  import { t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
8
8
  import { i as basic_default } from "./presets-HDIxliiq.js";
9
9
  import { r as loadSession, t as createSession } from "./session-BDWZZaYa.js";
10
- import { resolve } from "node:path";
10
+ import { basename, resolve } from "node:path";
11
+ import { readFileSync } from "node:fs";
11
12
  import { Buffer } from "node:buffer";
13
+ import { parseArgs } from "node:util";
12
14
  //#region src/acp/json-rpc.ts
13
15
  function createJsonRpcConnection(options) {
14
16
  let nextId = 1;
@@ -183,6 +185,342 @@ function isObject$1(value) {
183
185
  return value !== null && typeof value === "object" && !Array.isArray(value);
184
186
  }
185
187
  //#endregion
188
+ //#region src/start/shared-options.ts
189
+ const validThinkingLevels = [
190
+ "off",
191
+ "minimal",
192
+ "low",
193
+ "medium",
194
+ "high",
195
+ "xhigh",
196
+ "max",
197
+ "adaptive"
198
+ ];
199
+ const startProviderNames = [
200
+ "anthropic",
201
+ "openai",
202
+ "openrouter",
203
+ "cerebras",
204
+ "xai",
205
+ "arcee",
206
+ "baseten",
207
+ "local",
208
+ "openai-compat"
209
+ ];
210
+ const startPresetNames = ["basic"];
211
+ const startFormatNames = ["zidane", "provider"];
212
+ var StartUsageError = class extends Error {
213
+ constructor(message) {
214
+ super(message);
215
+ this.name = "StartUsageError";
216
+ }
217
+ };
218
+ /**
219
+ * The shared `bun start` / `zidane run` option table. Exported so
220
+ * `parseStartArgs` (start CLI) can compose it with the Restate-only options
221
+ * and parse argv ONCE — two diverging tables would let new flags silently
222
+ * drift between the two parsers.
223
+ */
224
+ const startLocalArgOptions = {
225
+ "help": {
226
+ type: "boolean",
227
+ short: "h",
228
+ default: false
229
+ },
230
+ "prompt": {
231
+ type: "string",
232
+ short: "p"
233
+ },
234
+ "model": {
235
+ type: "string",
236
+ short: "m"
237
+ },
238
+ "preset": {
239
+ type: "string",
240
+ short: "t",
241
+ default: "basic"
242
+ },
243
+ "system": {
244
+ type: "string",
245
+ short: "s"
246
+ },
247
+ "thinking": {
248
+ type: "string",
249
+ default: "off"
250
+ },
251
+ "provider": {
252
+ type: "string",
253
+ default: "anthropic"
254
+ },
255
+ "base-url": { type: "string" },
256
+ "api-key-env": { type: "string" },
257
+ "headers-env": { type: "string" },
258
+ "header": {
259
+ type: "string",
260
+ multiple: true
261
+ },
262
+ "vision": { type: "boolean" },
263
+ "image-in-tool-result": { type: "boolean" },
264
+ "temperature": { type: "string" },
265
+ "seed": { type: "string" },
266
+ "context": {
267
+ type: "string",
268
+ short: "c",
269
+ default: "process"
270
+ },
271
+ "image": { type: "string" },
272
+ "cwd": { type: "string" },
273
+ "env": {
274
+ type: "string",
275
+ multiple: true
276
+ },
277
+ "pass-env": {
278
+ type: "string",
279
+ multiple: true
280
+ },
281
+ "pregame": { type: "string" },
282
+ "sandbox": { type: "string" },
283
+ "sandbox-on-destroy": { type: "string" },
284
+ "mcp": { type: "string" },
285
+ "session-db": { type: "string" },
286
+ "session-id": { type: "string" },
287
+ "json": {
288
+ type: "boolean",
289
+ default: false
290
+ },
291
+ "stream-json": {
292
+ type: "boolean",
293
+ default: false
294
+ },
295
+ "format": {
296
+ type: "string",
297
+ default: "zidane"
298
+ }
299
+ };
300
+ /**
301
+ * Strict argv parse shared by the start CLIs. `strict: true` rejects unknown
302
+ * flags and stray positionals loudly — without it, a typo'd flag's value
303
+ * silently leaks into the run (e.g. as a bogus prompt). Parse failures are
304
+ * rethrown as {@link StartUsageError}.
305
+ */
306
+ function parseStartArgValues(argv, options) {
307
+ try {
308
+ const { values } = parseArgs({
309
+ args: [...argv],
310
+ options,
311
+ strict: true
312
+ });
313
+ return values;
314
+ } catch (err) {
315
+ throw new StartUsageError(`${err instanceof Error ? err.message : String(err)}\n\nRun with --help for usage.`);
316
+ }
317
+ }
318
+ /** Assemble {@link StartLocalOptions} from already-parsed argv values. */
319
+ function buildStartLocalOptions(values, config = {}) {
320
+ const { requirePrompt = true, usage = startLocalUsage } = config;
321
+ if (values.help === true) throw new StartUsageError(usage());
322
+ const prompt = values.prompt;
323
+ if (requirePrompt && (!prompt || prompt.trim() === "")) throw new StartUsageError(usage());
324
+ const thinkingRaw = values.thinking || "off";
325
+ if (!validThinkingLevels.includes(thinkingRaw)) throw new Error(`Unknown thinking level: ${thinkingRaw}. Available: ${validThinkingLevels.join(", ")}`);
326
+ const provider = values.provider || "anthropic";
327
+ if (!startProviderNames.includes(provider)) throw new Error(`Unknown provider: ${provider}. Available: ${startProviderNames.join(", ")}`);
328
+ const preset = values.preset || "basic";
329
+ if (!startPresetNames.includes(preset)) throw new Error(`Unknown preset: ${preset}. Available: ${startPresetNames.join(", ")}`);
330
+ if (values.json === true && values["stream-json"] === true) throw new StartUsageError("Use either `--json` or `--stream-json`, not both.");
331
+ const format = values.format || "zidane";
332
+ if (!startFormatNames.includes(format)) throw new Error(`Unknown format: ${format}. Available: ${startFormatNames.join(", ")}`);
333
+ const baseURL = values["base-url"];
334
+ const apiKeyEnv = values["api-key-env"];
335
+ const headersEnv = values["headers-env"];
336
+ const extraHeaders = parseHeaderArgs(values.header, "--header");
337
+ const temperature = parseOptionalNumber(values.temperature, "--temperature");
338
+ const seed = parseOptionalInteger(values.seed, "--seed");
339
+ const env = parseEnvArgs(values.env, values["pass-env"]);
340
+ const context = values.context || "process";
341
+ const pregame = loadPregame(values.pregame, context);
342
+ const sandboxId = resolveSandboxId(values.sandbox, context);
343
+ const sandboxOnDestroy = resolveSandboxOnDestroy(values["sandbox-on-destroy"], context);
344
+ return {
345
+ system: values.system,
346
+ prompt: prompt ?? "",
347
+ model: values.model,
348
+ ...baseURL !== void 0 ? { baseURL } : {},
349
+ ...apiKeyEnv !== void 0 ? { apiKeyEnv } : {},
350
+ ...headersEnv !== void 0 ? { headersEnv } : {},
351
+ ...Object.keys(extraHeaders).length > 0 ? { extraHeaders } : {},
352
+ ...values.vision === true ? { vision: true } : {},
353
+ ...values["image-in-tool-result"] === true ? { imageInToolResult: true } : {},
354
+ ...temperature !== void 0 ? { temperature } : {},
355
+ ...seed !== void 0 ? { seed } : {},
356
+ preset,
357
+ thinking: thinkingRaw,
358
+ provider,
359
+ context,
360
+ image: values.image,
361
+ cwd: values.cwd,
362
+ ...Object.keys(env).length > 0 ? { env } : {},
363
+ ...pregame ? { pregame } : {},
364
+ ...sandboxId ? { sandboxId } : {},
365
+ ...sandboxOnDestroy ? { sandboxOnDestroy } : {},
366
+ mcp: parseMcpConfig(values.mcp),
367
+ sessionDb: values["session-db"],
368
+ sessionId: values["session-id"],
369
+ json: values.json === true,
370
+ streamJson: values["stream-json"] === true,
371
+ format
372
+ };
373
+ }
374
+ function startLocalUsage() {
375
+ return [
376
+ `Usage: bun start --prompt "your message" \\`,
377
+ ` [--provider ${startProviderNames.join("|")}] [--model <id>] [--thinking ${validThinkingLevels.join("|")}]`,
378
+ ` [--base-url <url>] [--api-key-env <NAME>] [--headers-env <NAME>] [--header 'Name: value']`,
379
+ ` [--vision] [--image-in-tool-result] [--temperature <f>] [--seed <n>]`,
380
+ ` [--context process|docker|e2b|daytona] [--image node:22] [--cwd /workspace]`,
381
+ ` [--env KEY=VALUE ...] [--pass-env VARNAME ...] [--pregame setup.sh] [--sandbox <id>] [--sandbox-on-destroy kill|pause|leave]`,
382
+ ` [--mcp '[...]'] [--session-db ./sessions.db] [--session-id <id>] [--json|--stream-json] [--format zidane|provider]`,
383
+ ``,
384
+ ` --stream-json streams COMPLETE TURNS as JSONL (one line per turn), then the result.`,
385
+ ` For the full event stream (text deltas, tool calls, …) use the headless CLI's`,
386
+ ` --output-format stream-json instead.`
387
+ ].join("\n");
388
+ }
389
+ /**
390
+ * Parse the `--mcp` argument: inline JSON (object or array) or a path to a JSON
391
+ * file. Validates that each server declares the fields its transport requires.
392
+ */
393
+ function parseMcpConfig(raw) {
394
+ if (!raw) return void 0;
395
+ let text = raw.trim();
396
+ if (!text.startsWith("[") && !text.startsWith("{")) text = readFileSync(text, "utf8");
397
+ const parsed = JSON.parse(text);
398
+ const configs = Array.isArray(parsed) ? parsed : [parsed];
399
+ for (const config of configs) {
400
+ if (!config.name || !config.transport) throw new Error(`Invalid MCP config: each server needs "name" and "transport". Got: ${JSON.stringify(config)}`);
401
+ if (config.transport === "stdio" && !config.command) throw new Error(`MCP server "${config.name}": stdio transport requires "command"`);
402
+ if ((config.transport === "sse" || config.transport === "streamable-http") && !config.url) throw new Error(`MCP server "${config.name}": ${config.transport} transport requires "url"`);
403
+ }
404
+ return configs;
405
+ }
406
+ function parseOptionalNumber(raw, flag) {
407
+ if (raw === void 0) return void 0;
408
+ const value = Number(raw);
409
+ if (Number.isNaN(value)) throw new Error(`${flag} must be a number, got '${raw}'.`);
410
+ return value;
411
+ }
412
+ function parseOptionalInteger(raw, flag) {
413
+ if (raw === void 0) return void 0;
414
+ const value = Number(raw);
415
+ if (!Number.isInteger(value)) throw new Error(`${flag} must be an integer, got '${raw}'.`);
416
+ return value;
417
+ }
418
+ /**
419
+ * Build the execution-context environment from `--pass-env VARNAME` (forward
420
+ * the named variable from the current shell) and `--env KEY=VALUE` (set a
421
+ * value explicitly). `--pass-env` is applied first so an explicit `--env`
422
+ * wins when both name the same key. An unset `--pass-env` variable is a hard
423
+ * error rather than a silently-empty value.
424
+ */
425
+ function parseEnvArgs(envArgs, passEnvArgs, source = process.env) {
426
+ const env = {};
427
+ for (const name of passEnvArgs ?? []) {
428
+ const key = name.trim();
429
+ if (!key) throw new Error("--pass-env requires a variable name.");
430
+ const value = source[key];
431
+ if (value === void 0) throw new Error(`--pass-env ${key}: environment variable ${key} is not set.`);
432
+ env[key] = value;
433
+ }
434
+ for (const item of envArgs ?? []) {
435
+ const eq = item.indexOf("=");
436
+ const key = eq === -1 ? "" : item.slice(0, eq).trim();
437
+ if (!key) throw new Error(`--env must be formatted as KEY=VALUE, got '${item}'.`);
438
+ env[key] = item.slice(eq + 1);
439
+ }
440
+ return env;
441
+ }
442
+ /**
443
+ * Resolve the `--pregame <file>` setup script. Returns `undefined` when no path
444
+ * is given. The script only runs in remote sandbox contexts (it's uploaded into
445
+ * the ready sandbox before prompting); for any other context this warns and
446
+ * ignores it rather than failing. For supported contexts the file is read
447
+ * eagerly — consistent with `--prompt-file`/`--schema` — so a missing or
448
+ * unreadable file surfaces as a usage error up front rather than mid-spawn.
449
+ * The returned `name` is the basename used for the uploaded file.
450
+ */
451
+ function loadPregame(path, context) {
452
+ if (!path) return void 0;
453
+ if (!supportsRemoteSandboxFlags(context)) {
454
+ console.error(`--pregame is only supported with --context e2b or daytona; ignoring it for context '${context}'.`);
455
+ return;
456
+ }
457
+ let content;
458
+ try {
459
+ content = readFileSync(path, "utf8");
460
+ } catch (err) {
461
+ throw new Error(`--pregame: failed to read '${path}': ${err.message}`);
462
+ }
463
+ return {
464
+ name: basename(path),
465
+ content
466
+ };
467
+ }
468
+ /**
469
+ * Resolve the `--sandbox <id>` argument. Returns `undefined` when no id is
470
+ * given. Connecting to a pre-existing sandbox is supported by the remote
471
+ * sandbox providers; for any other context this warns and ignores it rather
472
+ * than failing, mirroring {@link loadPregame}. A blank value (e.g.
473
+ * `--sandbox ''`) is treated as absent.
474
+ */
475
+ function resolveSandboxId(raw, context) {
476
+ const id = raw?.trim();
477
+ if (!id) return void 0;
478
+ if (!supportsRemoteSandboxFlags(context)) {
479
+ console.error(`--sandbox is only supported with --context e2b or daytona; ignoring it for context '${context}'.`);
480
+ return;
481
+ }
482
+ return id;
483
+ }
484
+ const validSandboxOnDestroyValues = [
485
+ "kill",
486
+ "pause",
487
+ "leave"
488
+ ];
489
+ /**
490
+ * Resolve the `--sandbox-on-destroy kill|pause|leave` argument. Returns
491
+ * `undefined` when not set (the provider defaults to `'kill'` / delete).
492
+ * Applies to remote sandbox contexts; warns and ignores for any other context,
493
+ * mirroring {@link resolveSandboxId} and {@link loadPregame}.
494
+ */
495
+ function resolveSandboxOnDestroy(raw, context) {
496
+ const value = raw?.trim();
497
+ if (!value) return void 0;
498
+ if (!supportsRemoteSandboxFlags(context)) {
499
+ console.error(`--sandbox-on-destroy is only supported with --context e2b or daytona; ignoring it for context '${context}'.`);
500
+ return;
501
+ }
502
+ if (!validSandboxOnDestroyValues.includes(value)) throw new StartUsageError(`--sandbox-on-destroy must be kill|pause|leave, got '${value}'.`);
503
+ return value;
504
+ }
505
+ function supportsRemoteSandboxFlags(context) {
506
+ return context === "e2b" || context === "daytona";
507
+ }
508
+ function parseHeaderArgs(raw, flag) {
509
+ if (!raw || raw.length === 0) return {};
510
+ const headers = {};
511
+ for (const item of raw) {
512
+ const colon = item.indexOf(":");
513
+ const equals = item.indexOf("=");
514
+ const separator = colon === -1 ? equals : equals === -1 ? colon : Math.min(colon, equals);
515
+ if (separator <= 0) throw new Error(`${flag} must be formatted as "Name: value" or "Name=value".`);
516
+ const name = item.slice(0, separator).trim();
517
+ const value = item.slice(separator + 1).trim();
518
+ if (!name || !value) throw new Error(`${flag} must include both a header name and value.`);
519
+ headers[name] = value;
520
+ }
521
+ return headers;
522
+ }
523
+ //#endregion
186
524
  //#region src/acp/mapping.ts
187
525
  function acpMcpServersToZidane(servers) {
188
526
  if (!servers) return [];
@@ -635,6 +973,19 @@ function seedReadState(ctx, path, content, offset, limit) {
635
973
  //#region src/acp/server.ts
636
974
  const ACP_AGENT_VERSION = "1";
637
975
  const MODEL_CONFIG_ID = "model";
976
+ const MODE_CONFIG_ID = "mode";
977
+ const THINKING_CONFIG_ID = "thinking";
978
+ const MODEL_OPTION_CONFIG_PREFIX = "model_option:";
979
+ const PLAN_MODE_ID = "plan";
980
+ const PLAN_READ_ONLY_TOOLS = new Set([
981
+ "read_file",
982
+ "list_files",
983
+ "grep",
984
+ "glob",
985
+ "web_search",
986
+ "fetch_url",
987
+ "skills_read"
988
+ ]);
638
989
  const DEFAULT_PERMISSION_OPTIONS = [
639
990
  {
640
991
  optionId: "allow_once",
@@ -689,18 +1040,35 @@ var AcpServerImpl = class {
689
1040
  return ((this.options.model ? this.modelChoices.find((choice) => choice.id === this.options.model || choice.model === this.options.model) : void 0) ?? this.modelChoices[0]).id;
690
1041
  }
691
1042
  buildDefaultConfigOptions(userConfig) {
692
- if (this.modelChoices.length === 0) return userConfig;
693
- return [{
694
- id: MODEL_CONFIG_ID,
695
- name: "Model",
1043
+ const reserved = new Set([MODE_CONFIG_ID]);
1044
+ const options = [{
1045
+ id: MODE_CONFIG_ID,
1046
+ name: "Mode",
696
1047
  type: "select",
697
- category: "model",
698
- currentValue: this.defaultModelChoiceId,
699
- options: this.modelChoices.map((choice) => ({
700
- value: choice.id,
701
- name: choice.name ?? choice.model
1048
+ category: "mode",
1049
+ currentValue: this.defaultModeId,
1050
+ options: this.modes.map((mode) => ({
1051
+ value: mode.id,
1052
+ name: mode.name,
1053
+ ...mode.description ? { description: mode.description } : {}
702
1054
  }))
703
- }, ...userConfig.filter((option) => option.id !== MODEL_CONFIG_ID)];
1055
+ }];
1056
+ if (this.modelChoices.length > 0) {
1057
+ reserved.add(MODEL_CONFIG_ID);
1058
+ options.push({
1059
+ id: MODEL_CONFIG_ID,
1060
+ name: "Model",
1061
+ type: "select",
1062
+ category: "model",
1063
+ currentValue: this.defaultModelChoiceId,
1064
+ options: this.modelChoices.map((choice) => ({
1065
+ value: choice.id,
1066
+ name: choice.name ?? choice.model
1067
+ }))
1068
+ });
1069
+ }
1070
+ options.push(...this.modelDependentConfigOptions(this.defaultModelChoiceId));
1071
+ return [...options, ...userConfig.filter((option) => !reserved.has(option.id) && !isGeneratedModelOptionConfig(option.id) && option.id !== THINKING_CONFIG_ID)];
704
1072
  }
705
1073
  providerForChoiceId(id) {
706
1074
  const choice = this.modelChoices.find((candidate) => candidate.id === id);
@@ -911,6 +1279,8 @@ var AcpServerImpl = class {
911
1279
  stats = await runtime.agent.run({
912
1280
  prompt: mapped.prompt,
913
1281
  ...runtime.runModel ? { model: runtime.runModel } : {},
1282
+ ...this.runConfig(runtime),
1283
+ ...this.toolsForMode(runtime),
914
1284
  ...this.options.system ? { system: this.options.system } : {},
915
1285
  signal: abortController.signal
916
1286
  });
@@ -938,21 +1308,33 @@ var AcpServerImpl = class {
938
1308
  }
939
1309
  async setMode(params) {
940
1310
  const runtime = this.requireSession(params.sessionId);
941
- if (!this.modes.some((mode) => mode.id === params.modeId)) throw new AcpProtocolError(-32602, `Unknown mode: ${params.modeId}`);
942
- runtime.modeId = params.modeId;
1311
+ const configOptions = this.selectMode(runtime, params.modeId);
943
1312
  await this.sessionUpdate(params.sessionId, {
944
1313
  sessionUpdate: "current_mode_update",
945
1314
  currentModeId: params.modeId
946
1315
  });
1316
+ await this.sessionUpdate(params.sessionId, {
1317
+ sessionUpdate: "config_option_update",
1318
+ configOptions
1319
+ });
947
1320
  return {};
948
1321
  }
949
1322
  async setConfigOption(params) {
950
1323
  const runtime = this.requireSession(params.sessionId);
951
1324
  const isModelSwitch = this.modelChoices.length > 0 && params.configId === MODEL_CONFIG_ID;
1325
+ const isModeSwitch = params.configId === MODE_CONFIG_ID;
952
1326
  if (isModelSwitch && runtime.inFlight) throw new AcpProtocolError(-32600, "Cannot switch model while a prompt is in flight.");
953
- const configOptions = this.applyConfigSelection(runtime, params.configId, params.value);
1327
+ let configOptions = this.applyConfigSelection(runtime, params.configId, params.value);
954
1328
  if (isModelSwitch) this.selectModel(runtime, params.value);
1329
+ if (isModeSwitch) {
1330
+ runtime.modeId = params.value;
1331
+ await this.sessionUpdate(params.sessionId, {
1332
+ sessionUpdate: "current_mode_update",
1333
+ currentModeId: params.value
1334
+ });
1335
+ }
955
1336
  runtime.config.set(params.configId, params.value);
1337
+ if (isModelSwitch) configOptions = runtime.configOptions;
956
1338
  await this.sessionUpdate(params.sessionId, {
957
1339
  sessionUpdate: "config_option_update",
958
1340
  configOptions
@@ -969,6 +1351,78 @@ var AcpServerImpl = class {
969
1351
  if (!choice) throw new AcpProtocolError(-32602, `Unknown model: ${choiceId}`);
970
1352
  runtime.setProviderDelegate?.(this.providerForChoiceId(choice.id));
971
1353
  runtime.runModel = choice.model;
1354
+ this.refreshModelDependentConfigOptions(runtime, choice.id);
1355
+ }
1356
+ selectMode(runtime, modeId) {
1357
+ if (!this.modes.some((mode) => mode.id === modeId)) throw new AcpProtocolError(-32602, `Unknown mode: ${modeId}`);
1358
+ runtime.modeId = modeId;
1359
+ return this.applyConfigSelection(runtime, MODE_CONFIG_ID, modeId);
1360
+ }
1361
+ runConfig(runtime) {
1362
+ const thinking = configValue(runtime.configOptions, THINKING_CONFIG_ID);
1363
+ const modelOptions = {};
1364
+ for (const option of runtime.configOptions) {
1365
+ if (!isGeneratedModelOptionConfig(option.id) || option.currentValue !== "on") continue;
1366
+ modelOptions[option.id.slice(13)] = true;
1367
+ }
1368
+ return {
1369
+ ...isThinkingLevel(thinking) ? { thinking } : {},
1370
+ ...Object.keys(modelOptions).length > 0 ? { modelOptions } : {}
1371
+ };
1372
+ }
1373
+ toolsForMode(runtime) {
1374
+ if (runtime.modeId !== PLAN_MODE_ID) return {};
1375
+ return { tools: filterPlanTools(runtime.tools) };
1376
+ }
1377
+ refreshModelDependentConfigOptions(runtime, choiceId) {
1378
+ const insertAfter = (option) => option.id === MODEL_CONFIG_ID;
1379
+ const base = runtime.configOptions.filter((option) => option.id !== THINKING_CONFIG_ID && !isGeneratedModelOptionConfig(option.id));
1380
+ const generated = this.modelDependentConfigOptions(choiceId, runtime.config);
1381
+ const index = base.findIndex(insertAfter);
1382
+ runtime.configOptions = index === -1 ? [...base, ...generated] : [
1383
+ ...base.slice(0, index + 1),
1384
+ ...generated,
1385
+ ...base.slice(index + 1)
1386
+ ];
1387
+ }
1388
+ modelDependentConfigOptions(choiceId, previous) {
1389
+ if (!choiceId) return [];
1390
+ const choice = this.modelChoices.find((candidate) => candidate.id === choiceId);
1391
+ if (!choice) return [];
1392
+ const options = [];
1393
+ if (choice.reasoning === true) {
1394
+ const current = previous?.get(THINKING_CONFIG_ID);
1395
+ options.push({
1396
+ id: THINKING_CONFIG_ID,
1397
+ name: "Thinking",
1398
+ type: "select",
1399
+ category: "thought_level",
1400
+ currentValue: isThinkingLevel(current) ? current : "off",
1401
+ options: validThinkingLevels.map((level) => ({
1402
+ value: level,
1403
+ name: thinkingLabel(level)
1404
+ }))
1405
+ });
1406
+ }
1407
+ for (const option of choice.options ?? []) {
1408
+ const id = `${MODEL_OPTION_CONFIG_PREFIX}${option.id}`;
1409
+ const current = previous?.get(id);
1410
+ options.push({
1411
+ id,
1412
+ name: option.label,
1413
+ type: "select",
1414
+ currentValue: current === "on" ? "on" : "off",
1415
+ ...option.description ? { description: option.description } : {},
1416
+ options: [{
1417
+ value: "off",
1418
+ name: "Off"
1419
+ }, {
1420
+ value: "on",
1421
+ name: "On"
1422
+ }]
1423
+ });
1424
+ }
1425
+ return options;
972
1426
  }
973
1427
  /** Reflect a `select` choice back into the advertised option set's `currentValue`. */
974
1428
  applyConfigSelection(runtime, configId, value) {
@@ -1035,6 +1489,7 @@ var AcpServerImpl = class {
1035
1489
  execution,
1036
1490
  agent,
1037
1491
  provider,
1492
+ tools: wrappedTools,
1038
1493
  modeId: this.defaultModeId,
1039
1494
  configOptions: cloneConfigOptions(this.defaultConfigOptions),
1040
1495
  config: /* @__PURE__ */ new Map(),
@@ -1086,6 +1541,16 @@ var AcpServerImpl = class {
1086
1541
  }
1087
1542
  installHookAdapter(runtime) {
1088
1543
  const seenTools = /* @__PURE__ */ new Set();
1544
+ runtime.agent.hooks.hook("tool:gate", (ctx) => {
1545
+ if (runtime.modeId !== PLAN_MODE_ID || isPlanReadOnlyTool(ctx.name)) return;
1546
+ ctx.block = true;
1547
+ ctx.reason = `Plan mode is read-only. Switch to Build mode before using \`${ctx.name}\`.`;
1548
+ });
1549
+ runtime.agent.hooks.hook("mcp:tool:gate", (ctx) => {
1550
+ if (runtime.modeId !== PLAN_MODE_ID) return;
1551
+ ctx.block = true;
1552
+ ctx.reason = `Plan mode is read-only. Switch to Build mode before using MCP tool \`${ctx.tool}\`.`;
1553
+ });
1089
1554
  runtime.agent.hooks.hook("stream:text", ({ delta, turnId }) => {
1090
1555
  this.sessionUpdate(runtime.sessionId, {
1091
1556
  sessionUpdate: "agent_message_chunk",
@@ -1308,6 +1773,14 @@ function mergeMcpServers(...sources) {
1308
1773
  for (const source of sources) for (const server of source ?? []) byName.set(server.name, server);
1309
1774
  return [...byName.values()];
1310
1775
  }
1776
+ function filterPlanTools(tools) {
1777
+ const filtered = {};
1778
+ for (const [key, tool] of Object.entries(tools ?? {})) if (isPlanReadOnlyTool(tool.spec.name)) filtered[key] = tool;
1779
+ return filtered;
1780
+ }
1781
+ function isPlanReadOnlyTool(name) {
1782
+ return PLAN_READ_ONLY_TOOLS.has(name);
1783
+ }
1311
1784
  function cloneConfigOptions(options) {
1312
1785
  return options.map((option) => ({
1313
1786
  ...option,
@@ -1323,6 +1796,18 @@ function configOptionHasValue(option, value) {
1323
1796
  } else if (item.value === value) return true;
1324
1797
  return false;
1325
1798
  }
1799
+ function configValue(options, id) {
1800
+ return options.find((option) => option.id === id)?.currentValue;
1801
+ }
1802
+ function isGeneratedModelOptionConfig(id) {
1803
+ return id.startsWith(MODEL_OPTION_CONFIG_PREFIX);
1804
+ }
1805
+ function isThinkingLevel(value) {
1806
+ return typeof value === "string" && validThinkingLevels.includes(value);
1807
+ }
1808
+ function thinkingLabel(level) {
1809
+ return level === "xhigh" ? "Extra high" : level.charAt(0).toUpperCase() + level.slice(1);
1810
+ }
1326
1811
  function sameStringArray(left, right) {
1327
1812
  return left.length === right.length && left.every((value, index) => value === right[index]);
1328
1813
  }
@@ -1405,6 +1890,6 @@ function runAcpStdioServer(options) {
1405
1890
  };
1406
1891
  }
1407
1892
  //#endregion
1408
- export { acpMcpServersToZidane as a, stopReasonFromRun as c, createJsonRpcConnection as d, wrapToolsForAcpClient as i, toolResultToAcpContent as l, AcpProtocolError as n, acpPromptToPromptParts as o, createAcpServer as r, sessionBlocksToAcp as s, runAcpStdioServer as t, JsonRpcRemoteError as u };
1893
+ export { acpMcpServersToZidane as a, stopReasonFromRun as c, buildStartLocalOptions as d, parseStartArgValues as f, createJsonRpcConnection as g, JsonRpcRemoteError as h, wrapToolsForAcpClient as i, toolResultToAcpContent as l, startProviderNames as m, AcpProtocolError as n, acpPromptToPromptParts as o, startLocalArgOptions as p, createAcpServer as r, sessionBlocksToAcp as s, runAcpStdioServer as t, StartUsageError as u };
1409
1894
 
1410
- //# sourceMappingURL=acp-BqIU2mo-.js.map
1895
+ //# sourceMappingURL=acp-BRQvvdnO.js.map