flavor-code 1.3.9 → 1.3.11

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.
@@ -4256,6 +4256,8 @@ function normalizeProviderError(error) {
4256
4256
  code = "context_overflow";
4257
4257
  } else if (status === 404 || /model.*not.?found|not.?found.*model/.test(searchable)) {
4258
4258
  code = "model_not_found";
4259
+ } else if (status !== void 0 && status >= 500 && status <= 599) {
4260
+ code = "network";
4259
4261
  } else if (status === void 0 && /econn|enet|socket|network|fetch failed|connection|timeout/.test(searchable)) {
4260
4262
  code = "network";
4261
4263
  }
@@ -6093,9 +6095,10 @@ function withStructuredOutput(options) {
6093
6095
  throw new Error("Structured output retry delays must be non-negative integers");
6094
6096
  }
6095
6097
  }
6096
- const tool = modelToolFromZod(options.name, options.description, options.schema);
6098
+ const generatedTool = options.modelInputSchema === void 0 ? modelToolFromZod(options.name, options.description, options.schema) : { name: options.name, description: options.description, inputSchema: options.modelInputSchema };
6099
+ const tool = options.modelStrict === void 0 ? generatedTool : { ...generatedTool, strict: options.modelStrict };
6097
6100
  const maxAttempts = retry.maxRetries + 1;
6098
- const jsonSchema = tool.inputSchema;
6101
+ const validationSchema = jsonSchemaFromZod(options.schema);
6099
6102
  const model = {
6100
6103
  async *stream(request) {
6101
6104
  let candidate;
@@ -6168,7 +6171,7 @@ function withStructuredOutput(options) {
6168
6171
  }
6169
6172
  }
6170
6173
  if (candidateCount === 1 && candidate !== null && typeof candidate === "object") {
6171
- candidate = coerceByJsonSchema(candidate, jsonSchema);
6174
+ candidate = coerceByJsonSchema(candidate, validationSchema);
6172
6175
  }
6173
6176
  if (candidateCount !== 1) {
6174
6177
  attemptError = {
@@ -6240,7 +6243,13 @@ function withStructuredOutput(options) {
6240
6243
  return model;
6241
6244
  }
6242
6245
  function strictJsonSchema(schema) {
6243
- return ensureStrictSchema(z12.toJSONSchema(schema));
6246
+ return strictJsonSchemaObject(jsonSchemaFromZod(schema));
6247
+ }
6248
+ function jsonSchemaFromZod(schema) {
6249
+ return z12.toJSONSchema(schema);
6250
+ }
6251
+ function strictJsonSchemaObject(schema) {
6252
+ return ensureStrictSchema(schema);
6244
6253
  }
6245
6254
  function extractJsonObject(text) {
6246
6255
  const trimmed = text.trim();
@@ -6288,6 +6297,7 @@ function coerceByJsonSchema(value, schema) {
6288
6297
  const node = schema;
6289
6298
  const branches = [
6290
6299
  ...Array.isArray(node.anyOf) ? node.anyOf : [],
6300
+ ...Array.isArray(node.oneOf) ? node.oneOf : [],
6291
6301
  ...Array.isArray(node.allOf) ? node.allOf : []
6292
6302
  ].filter((branch) => branch !== null && typeof branch === "object");
6293
6303
  const types = [];
@@ -6307,40 +6317,108 @@ function coerceByJsonSchema(value, schema) {
6307
6317
  if (lowered === "true") return true;
6308
6318
  if (lowered === "false") return false;
6309
6319
  }
6320
+ if (types.includes("object") && !types.includes("string") && typeof value === "string") {
6321
+ const trimmed = value.trim();
6322
+ if (trimmed.startsWith("{")) {
6323
+ try {
6324
+ const parsed = JSON.parse(trimmed);
6325
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
6326
+ return coerceByJsonSchema(parsed, node);
6327
+ }
6328
+ } catch {
6329
+ }
6330
+ }
6331
+ }
6332
+ if (types.includes("array") && !Array.isArray(value) && value !== null && value !== void 0 && !types.some((type) => matchesJsonSchemaType(value, type))) {
6333
+ const arraySchema = [node, ...branches].find((candidate) => {
6334
+ const type = candidate.type;
6335
+ return type === "array" || Array.isArray(type) && type.includes("array");
6336
+ });
6337
+ if (typeof value === "string" && value.trim().startsWith("[")) {
6338
+ try {
6339
+ const parsed = JSON.parse(value.trim());
6340
+ if (Array.isArray(parsed)) return coerceByJsonSchema(parsed, arraySchema ?? node);
6341
+ } catch {
6342
+ }
6343
+ }
6344
+ return [arraySchema?.items === void 0 ? value : coerceByJsonSchema(value, arraySchema.items)];
6345
+ }
6310
6346
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
6311
- const properties = node.properties ?? branches.find((branch) => branch.properties)?.properties;
6347
+ const objectSchema = [node, ...branches].find((candidate) => candidate.type === "object" || candidate.properties !== void 0);
6348
+ const properties = objectSchema?.properties;
6312
6349
  if (properties !== void 0) {
6313
6350
  const output = {};
6351
+ const required2 = new Set(Array.isArray(objectSchema?.required) ? objectSchema.required : []);
6314
6352
  for (const [key, child] of Object.entries(value)) {
6315
- output[key] = properties[key] !== void 0 ? coerceByJsonSchema(child, properties[key]) : child;
6353
+ const childSchema = properties[key];
6354
+ if (child === null && childSchema !== void 0 && !required2.has(key) && !jsonSchemaAllowsNull(childSchema)) {
6355
+ continue;
6356
+ }
6357
+ output[key] = childSchema !== void 0 ? coerceByJsonSchema(child, childSchema) : child;
6316
6358
  }
6317
6359
  return output;
6318
6360
  }
6319
6361
  }
6320
- if (types.includes("array") && Array.isArray(value) && node.items !== void 0) {
6321
- return value.map((item) => coerceByJsonSchema(item, node.items));
6362
+ if (types.includes("array") && Array.isArray(value)) {
6363
+ const arraySchema = [node, ...branches].find((candidate) => {
6364
+ const type = candidate.type;
6365
+ return type === "array" || Array.isArray(type) && type.includes("array");
6366
+ });
6367
+ if (arraySchema?.items !== void 0) {
6368
+ return value.map((item) => coerceByJsonSchema(item, arraySchema.items));
6369
+ }
6322
6370
  }
6323
6371
  return value;
6324
6372
  }
6373
+ function matchesJsonSchemaType(value, type) {
6374
+ if (type === "null") return value === null;
6375
+ if (type === "array") return Array.isArray(value);
6376
+ if (type === "object") return value !== null && typeof value === "object" && !Array.isArray(value);
6377
+ if (type === "integer") return typeof value === "number" && Number.isInteger(value);
6378
+ if (type === "number") return typeof value === "number";
6379
+ return typeof value === type;
6380
+ }
6381
+ function jsonSchemaAllowsNull(schema) {
6382
+ if (schema === null || typeof schema !== "object") return false;
6383
+ const node = schema;
6384
+ if (node.type === "null" || Array.isArray(node.type) && node.type.includes("null")) return true;
6385
+ return [node.anyOf, node.oneOf].some((branches) => Array.isArray(branches) && branches.some((branch) => jsonSchemaAllowsNull(branch)));
6386
+ }
6325
6387
  function modelToolFromZod(name, description, schema) {
6326
6388
  return { name, description, inputSchema: strictJsonSchema(schema) };
6327
6389
  }
6328
6390
  function ensureStrictSchema(schema) {
6391
+ const output = { ...schema };
6392
+ for (const keyword of ["anyOf", "oneOf", "allOf", "prefixItems"]) {
6393
+ const branches = schema[keyword];
6394
+ if (Array.isArray(branches)) {
6395
+ output[keyword] = branches.map((branch) => typeof branch === "object" && branch !== null ? ensureStrictSchema(branch) : branch);
6396
+ }
6397
+ }
6398
+ if (typeof schema.items === "object" && schema.items !== null) {
6399
+ output.items = ensureStrictSchema(schema.items);
6400
+ }
6401
+ for (const keyword of ["$defs", "definitions"]) {
6402
+ const definitions = schema[keyword];
6403
+ if (typeof definitions === "object" && definitions !== null && !Array.isArray(definitions)) {
6404
+ output[keyword] = Object.fromEntries(Object.entries(definitions).map(([key, definition]) => [
6405
+ key,
6406
+ typeof definition === "object" && definition !== null ? ensureStrictSchema(definition) : definition
6407
+ ]));
6408
+ }
6409
+ }
6329
6410
  if (schema.type !== "object" || typeof schema.properties !== "object" || schema.properties === null) {
6330
- return schema;
6411
+ return output;
6331
6412
  }
6332
- const required2 = Array.isArray(schema.required) ? [...schema.required] : [];
6333
- const requiredSet = new Set(required2);
6413
+ const requiredSet = new Set(Array.isArray(schema.required) ? schema.required : []);
6334
6414
  const properties = {};
6335
6415
  for (const [key, value] of Object.entries(schema.properties)) {
6336
6416
  const child = typeof value === "object" && value !== null ? ensureStrictSchema(value) : value;
6337
- if (requiredSet.has(key)) properties[key] = child;
6338
- else {
6339
- properties[key] = { anyOf: [child, { type: "null" }] };
6340
- requiredSet.add(key);
6341
- }
6417
+ if (requiredSet.has(key) || jsonSchemaAllowsNull(child)) properties[key] = child;
6418
+ else properties[key] = { anyOf: [child, { type: "null" }] };
6419
+ requiredSet.add(key);
6342
6420
  }
6343
- return { ...schema, additionalProperties: false, required: [...requiredSet], properties };
6421
+ return { ...output, additionalProperties: false, required: [...requiredSet], properties };
6344
6422
  }
6345
6423
  function repairMessages(messages, tool, original, candidate, error) {
6346
6424
  const details = [
@@ -6710,19 +6788,11 @@ var AgentLoop = class {
6710
6788
  });
6711
6789
  continue;
6712
6790
  }
6713
- const validation = collected.kind === "valid" ? this.#options.runtime.validate(collected) : { ok: false, error: collected.error };
6791
+ const validation = collected.kind === "valid" ? this.#options.runtime.normalize(collected) : { ok: false, error: collected.error };
6714
6792
  if (validation.ok) {
6715
6793
  toolCalls.push({ id: collected.id, name: collected.name, input: validation.input });
6716
6794
  continue;
6717
6795
  }
6718
- if (collected.kind === "valid") {
6719
- const coerced = coerceByJsonSchema(collected.input, strictJsonSchema(definition.inputSchema));
6720
- const revalidation = this.#options.runtime.validate({ ...collected, input: coerced });
6721
- if (revalidation.ok) {
6722
- toolCalls.push({ id: collected.id, name: collected.name, input: revalidation.input });
6723
- continue;
6724
- }
6725
- }
6726
6796
  const repairModelId = this.#options.fallbackModelId ?? this.#options.modelId;
6727
6797
  const invalidOutput = collected.kind === "invalid" ? collected.rawInput : serializeToolInput(collected.input);
6728
6798
  const structured = withStructuredOutput({
@@ -6731,6 +6801,8 @@ var AgentLoop = class {
6731
6801
  name: definition.name,
6732
6802
  description: definition.description,
6733
6803
  schema: definition.inputSchema,
6804
+ ...definition.modelInputSchema === void 0 ? {} : { modelInputSchema: definition.modelInputSchema },
6805
+ ...definition.modelStrict === void 0 ? {} : { modelStrict: definition.modelStrict },
6734
6806
  beforeAttempt: async ({ attempt: repairAttempt, maxAttempts: repairMaxAttempts, messageCount }) => {
6735
6807
  const before = await this.#options.hooks.emit({
6736
6808
  version: 1,
@@ -7656,12 +7728,24 @@ var ToolRuntime = class {
7656
7728
  throw error;
7657
7729
  }
7658
7730
  }
7731
+ /**
7732
+ * Validates a call and applies the same conservative provider-argument
7733
+ * normalization used by the agent loop before falling back to repair.
7734
+ */
7735
+ normalize(call) {
7736
+ const validation = this.validate(call);
7737
+ if (validation.ok || validation.error.code === "unknown_tool") return validation;
7738
+ const tool = this.#tools.get(call.name);
7739
+ if (tool === void 0) return validation;
7740
+ const input = coerceByJsonSchema(call.input, jsonSchemaFromZod(tool.inputSchema));
7741
+ return this.validate({ ...call, input });
7742
+ }
7659
7743
  async #execute(call, context) {
7660
7744
  const tool = this.#tools.get(call.name);
7661
7745
  if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
7662
7746
  const signal = context.signal ?? new AbortController().signal;
7663
7747
  const toolCallId = call.id ?? randomUUID7();
7664
- const validation = this.validate(call);
7748
+ const validation = this.normalize(call);
7665
7749
  if (!validation.ok) {
7666
7750
  return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
7667
7751
  }
@@ -9412,6 +9496,7 @@ var PluginHost = class {
9412
9496
  description: contribution.description,
9413
9497
  inputSchema: z18.unknown(),
9414
9498
  ...contribution.modelInputSchema === void 0 ? {} : { modelInputSchema: contribution.modelInputSchema },
9499
+ ...contribution.modelInputSchema === void 0 ? {} : { modelStrict: false },
9415
9500
  ...contribution.readOnly === void 0 ? {} : { readOnly: contribution.readOnly },
9416
9501
  paths: () => [],
9417
9502
  execute: (input, _signal, toolContext) => session.invoke("tool", contribution.name, [
@@ -17894,10 +17979,17 @@ var FILE_TYPE_EXTENSIONS = {
17894
17979
  python: [".py", ".pyi"],
17895
17980
  typescript: [".ts", ".tsx", ".mts", ".cts"]
17896
17981
  };
17982
+ var BooleanInput = z30.preprocess((value) => {
17983
+ if (typeof value !== "string") return value;
17984
+ const normalized = value.trim().toLowerCase();
17985
+ if (normalized === "true") return true;
17986
+ if (normalized === "false") return false;
17987
+ return value;
17988
+ }, z30.boolean());
17897
17989
  var GlobInput = z30.object({
17898
17990
  pattern: z30.string().min(1),
17899
17991
  path: z30.string().min(1).describe('Workspace-relative search directory. Use JSON null, not the string "null", for the workspace root.').nullable().optional(),
17900
- includeIgnored: z30.boolean().describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
17992
+ includeIgnored: BooleanInput.describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
17901
17993
  limit: z30.coerce.number().int().positive().max(1e5).nullable().optional()
17902
17994
  });
17903
17995
  var GrepInput = z30.object({
@@ -17905,7 +17997,7 @@ var GrepInput = z30.object({
17905
17997
  path: z30.string().min(1).describe('Workspace-relative search path. Use JSON null, not the string "null", for the workspace root.').nullable().optional(),
17906
17998
  glob: z30.string().min(1).nullable().optional(),
17907
17999
  fileType: z30.enum(FILE_TYPES).nullable().optional(),
17908
- includeIgnored: z30.boolean().describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
18000
+ includeIgnored: BooleanInput.describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
17909
18001
  context: z30.coerce.number().int().nonnegative().max(100).nullable().optional(),
17910
18002
  limit: z30.coerce.number().int().positive().max(1e5).nullable().optional()
17911
18003
  });
@@ -19914,7 +20006,7 @@ function compileDefinition(record3, scope2, workspace) {
19914
20006
  name: record3.name,
19915
20007
  description: record3.description,
19916
20008
  inputSchema,
19917
- modelInputSchema: record3.inputSchema,
20009
+ modelInputSchema: strictJsonSchemaObject(record3.inputSchema),
19918
20010
  modelStrict: true,
19919
20011
  ...record3.agents === void 0 ? {} : { agents: record3.agents },
19920
20012
  paths: () => [],
@@ -20404,16 +20496,24 @@ var MVP_COMMANDS = [
20404
20496
  "fork",
20405
20497
  "pals",
20406
20498
  "chat",
20407
- "co-work"
20499
+ "co-work",
20500
+ "tool"
20408
20501
  ];
20409
- function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
20502
+ function parseSlashCommand(input, dynamicCommands = [], skillCommands = [], managedTools = []) {
20410
20503
  const trimmed = input.trim();
20411
20504
  if (!trimmed.startsWith("/")) return null;
20412
- const [rawName = "", ...args] = trimmed.slice(1).split(/\s+/);
20505
+ const match = /^(\S+)(?:\s+([\s\S]*))?$/u.exec(trimmed.slice(1));
20506
+ const rawName = match?.[1] ?? "";
20507
+ const rawArguments = match?.[2] ?? "";
20508
+ const args = rawArguments.length === 0 ? [] : rawArguments.split(/\s+/u);
20413
20509
  const name = rawName.toLowerCase();
20414
20510
  if (!MVP_COMMANDS.includes(name) && dynamicCommands.includes(name)) {
20415
20511
  return { name: "plugin", command: name, args };
20416
20512
  }
20513
+ if (!MVP_COMMANDS.includes(name)) {
20514
+ const managedTool = managedTools.find((tool) => tool.toLowerCase() === name);
20515
+ if (managedTool !== void 0) return { name: "managed-tool", tool: managedTool, input: rawArguments };
20516
+ }
20417
20517
  if (!MVP_COMMANDS.includes(name) && skillCommands.includes(name)) {
20418
20518
  return { name: "skill", skill: name, prompt: args.join(" ") };
20419
20519
  }
@@ -20435,6 +20535,15 @@ function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
20435
20535
  }
20436
20536
  return { name, mode: normalized };
20437
20537
  }
20538
+ if (name === "tool") {
20539
+ const [requested] = args;
20540
+ const managedTool = requested === void 0 ? void 0 : managedTools.find((candidate) => candidate.toLowerCase() === requested.toLowerCase());
20541
+ if (managedTool === void 0) {
20542
+ return { name: "invalid", command: name, message: "Use /tool <registered-tool> [JSON object]." };
20543
+ }
20544
+ const inputStart = rawArguments.search(/\s/u);
20545
+ return { name: "managed-tool", tool: managedTool, input: inputStart < 0 ? "" : rawArguments.slice(inputStart).trimStart() };
20546
+ }
20438
20547
  if (name === "audit") {
20439
20548
  const toolFilter = args.length > 0 ? args.join(" ") : void 0;
20440
20549
  return { name, toolFilter };
@@ -20662,6 +20771,7 @@ var HELP = [
20662
20771
  "/review [focus] review uncommitted changes before committing",
20663
20772
  "/evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>> self-improvement loop",
20664
20773
  "/mcp [status|tools|reconnect|enable|disable] manage MCP servers",
20774
+ "/tool <registered-tool> [JSON object] run a registered tool (or use /<tool-name>)",
20665
20775
  "/ide show VS Code connection and cursor/selection",
20666
20776
  "/pals [--verbose|rename <alias>|info <alias-or-uuid>]",
20667
20777
  "/chat <alias-or-uuid> <goal> send a task that starts on the receiving pal",
@@ -20933,7 +21043,12 @@ var FlavorSession = class {
20933
21043
  } catch {
20934
21044
  }
20935
21045
  }
20936
- const command = parseSlashCommand(prompt, this.#services.pluginCommands().map(({ name }) => name), skillNames);
21046
+ const command = parseSlashCommand(
21047
+ prompt,
21048
+ this.#services.pluginCommands().map(({ name }) => name),
21049
+ skillNames,
21050
+ this.#services.managedToolCommands().map(({ name }) => name)
21051
+ );
20937
21052
  if (command !== null) await this.#dispatch(command, controller2.signal);
20938
21053
  else for await (const event of this.#services.run(prompt, controller2.signal, {
20939
21054
  getSteeringMessages: () => this.#drainMessages("steer", true).map((item) => item.text),
@@ -21035,6 +21150,8 @@ var FlavorSession = class {
21035
21150
  this.#notice(`Main permissions set to ${command.mode}. Child approvals use bubble mode unless plan mode is active.`);
21036
21151
  } else if (command.name === "plugin") {
21037
21152
  this.#notice(format(await this.#services.runPluginCommand(command.command, command.args, signal)));
21153
+ } else if (command.name === "managed-tool") {
21154
+ this.#notice(format(await this.#services.runManagedTool(command.tool, command.input, signal)));
21038
21155
  } else if (command.name === "skill") {
21039
21156
  for await (const event of this.#services.runSkill(command.skill, command.prompt, signal)) this.#services.output(event);
21040
21157
  } else if (command.name === "loop") {
@@ -24635,6 +24752,7 @@ async function createProductionRuntime(options) {
24635
24752
  managedTools.push(tool);
24636
24753
  }
24637
24754
  if (harnessCreated) harness.replaceMainTools(tools);
24755
+ options.onToolsChange?.();
24638
24756
  };
24639
24757
  tools.push(...createManagedToolManagementTools({
24640
24758
  store: managedToolStore,
@@ -25964,6 +26082,25 @@ ${renderMemoryDocument(entries)}`;
25964
26082
  signal.throwIfAborted();
25965
26083
  return awaitWithSignal3(Promise.resolve(handler(args, { workspace, signal })), signal);
25966
26084
  },
26085
+ managedToolCommands: () => managedTools.map(({ name, description }) => ({ name, description })).sort((left, right) => left.name.localeCompare(right.name)),
26086
+ runManagedTool: async (name, rawInput, signal) => {
26087
+ const tool = managedTools.find((candidate) => sameToolName(candidate.name, name));
26088
+ if (tool === void 0) throw new Error(`Registered tool "${name}" is no longer available.`);
26089
+ signal.throwIfAborted();
26090
+ const input = normalizeToolCallInput(rawInput);
26091
+ const call = { name: tool.name, input };
26092
+ const validation = harness.main.runtime.normalize(call);
26093
+ if (!validation.ok) throw new Error(`Invalid input for registered tool "${tool.name}": ${validation.error.message}`);
26094
+ harness.main.runtime.beginTurn();
26095
+ const result = await harness.main.runtime.execute(
26096
+ { name: tool.name, input: validation.input },
26097
+ { agent: "main", ownerId: "main", signal }
26098
+ );
26099
+ if (!result.ok) {
26100
+ throw new Error(`Registered tool "${tool.name}" failed (${result.error?.code ?? "unknown"}): ${result.error?.message ?? "Unknown error"}`);
26101
+ }
26102
+ return result.output !== void 0 ? result.output : result.content;
26103
+ },
25967
26104
  output: emitOutput,
25968
26105
  questions,
25969
26106
  async login() {
@@ -26358,7 +26495,7 @@ ${expandSkillArguments(await skills.loadBody(skill), prompt)}`);
26358
26495
  ...additionalContext === void 0 ? {} : { additionalContext },
26359
26496
  ...getSteeringMessages === void 0 ? {} : { getSteeringMessages }
26360
26497
  })) {
26361
- if (event.type === "error" && /adapter|provider|api.?key|model/i.test(event.error.message)) {
26498
+ if (event.type === "error" && event.error.code !== "network" && event.error.code !== "rate_limit" && /adapter|provider|api.?key|model/i.test(event.error.message)) {
26362
26499
  yield { ...event, error: {
26363
26500
  ...event.error,
26364
26501
  message: `${event.error.message}. Configure providers and agents in .flavor/flavor.json or set OPENAI_API_KEY/ANTHROPIC_API_KEY.`
@@ -29277,7 +29414,8 @@ var DesktopRuntimeController = class {
29277
29414
  ...runtime.approvals.pending === void 0 ? {} : { approval: runtime.approvals.pending },
29278
29415
  ...runtime.services.questions.pending === void 0 ? {} : { questions: runtime.services.questions.pending },
29279
29416
  ...runtime.memoryReviews.pending.length === 0 ? {} : { memoryReviews: runtime.memoryReviews.pending },
29280
- ...(runtime.memoryReviews.autoDismissSeconds ?? 0) > 0 ? { memoryAutoDismissSeconds: runtime.memoryReviews.autoDismissSeconds } : {}
29417
+ ...(runtime.memoryReviews.autoDismissSeconds ?? 0) > 0 ? { memoryAutoDismissSeconds: runtime.memoryReviews.autoDismissSeconds } : {},
29418
+ ...(runtime.services.managedToolCommands?.().length ?? 0) === 0 ? {} : { managedTools: runtime.services.managedToolCommands() }
29281
29419
  },
29282
29420
  diagnostics: runtime?.diagnostics ?? [],
29283
29421
  models: this.#models,
@@ -29331,6 +29469,9 @@ var DesktopRuntimeController = class {
29331
29469
  },
29332
29470
  onApprovalChange: () => {
29333
29471
  if (this.#runtime !== void 0) this.#publishSnapshot();
29472
+ },
29473
+ onToolsChange: () => {
29474
+ if (this.#runtime !== void 0) this.#publishSnapshot();
29334
29475
  }
29335
29476
  });
29336
29477
  outputSessionId = runtime.sessionId;