flavor-code 1.3.10 → 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.
@@ -6095,9 +6095,10 @@ function withStructuredOutput(options) {
6095
6095
  throw new Error("Structured output retry delays must be non-negative integers");
6096
6096
  }
6097
6097
  }
6098
- 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 };
6099
6100
  const maxAttempts = retry.maxRetries + 1;
6100
- const jsonSchema = tool.inputSchema;
6101
+ const validationSchema = jsonSchemaFromZod(options.schema);
6101
6102
  const model = {
6102
6103
  async *stream(request) {
6103
6104
  let candidate;
@@ -6170,7 +6171,7 @@ function withStructuredOutput(options) {
6170
6171
  }
6171
6172
  }
6172
6173
  if (candidateCount === 1 && candidate !== null && typeof candidate === "object") {
6173
- candidate = coerceByJsonSchema(candidate, jsonSchema);
6174
+ candidate = coerceByJsonSchema(candidate, validationSchema);
6174
6175
  }
6175
6176
  if (candidateCount !== 1) {
6176
6177
  attemptError = {
@@ -6242,7 +6243,13 @@ function withStructuredOutput(options) {
6242
6243
  return model;
6243
6244
  }
6244
6245
  function strictJsonSchema(schema) {
6245
- 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);
6246
6253
  }
6247
6254
  function extractJsonObject(text) {
6248
6255
  const trimmed = text.trim();
@@ -6290,6 +6297,7 @@ function coerceByJsonSchema(value, schema) {
6290
6297
  const node = schema;
6291
6298
  const branches = [
6292
6299
  ...Array.isArray(node.anyOf) ? node.anyOf : [],
6300
+ ...Array.isArray(node.oneOf) ? node.oneOf : [],
6293
6301
  ...Array.isArray(node.allOf) ? node.allOf : []
6294
6302
  ].filter((branch) => branch !== null && typeof branch === "object");
6295
6303
  const types = [];
@@ -6309,40 +6317,108 @@ function coerceByJsonSchema(value, schema) {
6309
6317
  if (lowered === "true") return true;
6310
6318
  if (lowered === "false") return false;
6311
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
+ }
6312
6346
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
6313
- 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;
6314
6349
  if (properties !== void 0) {
6315
6350
  const output = {};
6351
+ const required2 = new Set(Array.isArray(objectSchema?.required) ? objectSchema.required : []);
6316
6352
  for (const [key, child] of Object.entries(value)) {
6317
- 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;
6318
6358
  }
6319
6359
  return output;
6320
6360
  }
6321
6361
  }
6322
- if (types.includes("array") && Array.isArray(value) && node.items !== void 0) {
6323
- 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
+ }
6324
6370
  }
6325
6371
  return value;
6326
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
+ }
6327
6387
  function modelToolFromZod(name, description, schema) {
6328
6388
  return { name, description, inputSchema: strictJsonSchema(schema) };
6329
6389
  }
6330
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
+ }
6331
6410
  if (schema.type !== "object" || typeof schema.properties !== "object" || schema.properties === null) {
6332
- return schema;
6411
+ return output;
6333
6412
  }
6334
- const required2 = Array.isArray(schema.required) ? [...schema.required] : [];
6335
- const requiredSet = new Set(required2);
6413
+ const requiredSet = new Set(Array.isArray(schema.required) ? schema.required : []);
6336
6414
  const properties = {};
6337
6415
  for (const [key, value] of Object.entries(schema.properties)) {
6338
6416
  const child = typeof value === "object" && value !== null ? ensureStrictSchema(value) : value;
6339
- if (requiredSet.has(key)) properties[key] = child;
6340
- else {
6341
- properties[key] = { anyOf: [child, { type: "null" }] };
6342
- requiredSet.add(key);
6343
- }
6417
+ if (requiredSet.has(key) || jsonSchemaAllowsNull(child)) properties[key] = child;
6418
+ else properties[key] = { anyOf: [child, { type: "null" }] };
6419
+ requiredSet.add(key);
6344
6420
  }
6345
- return { ...schema, additionalProperties: false, required: [...requiredSet], properties };
6421
+ return { ...output, additionalProperties: false, required: [...requiredSet], properties };
6346
6422
  }
6347
6423
  function repairMessages(messages, tool, original, candidate, error) {
6348
6424
  const details = [
@@ -6712,19 +6788,11 @@ var AgentLoop = class {
6712
6788
  });
6713
6789
  continue;
6714
6790
  }
6715
- 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 };
6716
6792
  if (validation.ok) {
6717
6793
  toolCalls.push({ id: collected.id, name: collected.name, input: validation.input });
6718
6794
  continue;
6719
6795
  }
6720
- if (collected.kind === "valid") {
6721
- const coerced = coerceByJsonSchema(collected.input, strictJsonSchema(definition.inputSchema));
6722
- const revalidation = this.#options.runtime.validate({ ...collected, input: coerced });
6723
- if (revalidation.ok) {
6724
- toolCalls.push({ id: collected.id, name: collected.name, input: revalidation.input });
6725
- continue;
6726
- }
6727
- }
6728
6796
  const repairModelId = this.#options.fallbackModelId ?? this.#options.modelId;
6729
6797
  const invalidOutput = collected.kind === "invalid" ? collected.rawInput : serializeToolInput(collected.input);
6730
6798
  const structured = withStructuredOutput({
@@ -6733,6 +6801,8 @@ var AgentLoop = class {
6733
6801
  name: definition.name,
6734
6802
  description: definition.description,
6735
6803
  schema: definition.inputSchema,
6804
+ ...definition.modelInputSchema === void 0 ? {} : { modelInputSchema: definition.modelInputSchema },
6805
+ ...definition.modelStrict === void 0 ? {} : { modelStrict: definition.modelStrict },
6736
6806
  beforeAttempt: async ({ attempt: repairAttempt, maxAttempts: repairMaxAttempts, messageCount }) => {
6737
6807
  const before = await this.#options.hooks.emit({
6738
6808
  version: 1,
@@ -7658,12 +7728,24 @@ var ToolRuntime = class {
7658
7728
  throw error;
7659
7729
  }
7660
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
+ }
7661
7743
  async #execute(call, context) {
7662
7744
  const tool = this.#tools.get(call.name);
7663
7745
  if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
7664
7746
  const signal = context.signal ?? new AbortController().signal;
7665
7747
  const toolCallId = call.id ?? randomUUID7();
7666
- const validation = this.validate(call);
7748
+ const validation = this.normalize(call);
7667
7749
  if (!validation.ok) {
7668
7750
  return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
7669
7751
  }
@@ -9414,6 +9496,7 @@ var PluginHost = class {
9414
9496
  description: contribution.description,
9415
9497
  inputSchema: z18.unknown(),
9416
9498
  ...contribution.modelInputSchema === void 0 ? {} : { modelInputSchema: contribution.modelInputSchema },
9499
+ ...contribution.modelInputSchema === void 0 ? {} : { modelStrict: false },
9417
9500
  ...contribution.readOnly === void 0 ? {} : { readOnly: contribution.readOnly },
9418
9501
  paths: () => [],
9419
9502
  execute: (input, _signal, toolContext) => session.invoke("tool", contribution.name, [
@@ -19923,7 +20006,7 @@ function compileDefinition(record3, scope2, workspace) {
19923
20006
  name: record3.name,
19924
20007
  description: record3.description,
19925
20008
  inputSchema,
19926
- modelInputSchema: record3.inputSchema,
20009
+ modelInputSchema: strictJsonSchemaObject(record3.inputSchema),
19927
20010
  modelStrict: true,
19928
20011
  ...record3.agents === void 0 ? {} : { agents: record3.agents },
19929
20012
  paths: () => [],
@@ -20413,16 +20496,24 @@ var MVP_COMMANDS = [
20413
20496
  "fork",
20414
20497
  "pals",
20415
20498
  "chat",
20416
- "co-work"
20499
+ "co-work",
20500
+ "tool"
20417
20501
  ];
20418
- function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
20502
+ function parseSlashCommand(input, dynamicCommands = [], skillCommands = [], managedTools = []) {
20419
20503
  const trimmed = input.trim();
20420
20504
  if (!trimmed.startsWith("/")) return null;
20421
- 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);
20422
20509
  const name = rawName.toLowerCase();
20423
20510
  if (!MVP_COMMANDS.includes(name) && dynamicCommands.includes(name)) {
20424
20511
  return { name: "plugin", command: name, args };
20425
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
+ }
20426
20517
  if (!MVP_COMMANDS.includes(name) && skillCommands.includes(name)) {
20427
20518
  return { name: "skill", skill: name, prompt: args.join(" ") };
20428
20519
  }
@@ -20444,6 +20535,15 @@ function parseSlashCommand(input, dynamicCommands = [], skillCommands = []) {
20444
20535
  }
20445
20536
  return { name, mode: normalized };
20446
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
+ }
20447
20547
  if (name === "audit") {
20448
20548
  const toolFilter = args.length > 0 ? args.join(" ") : void 0;
20449
20549
  return { name, toolFilter };
@@ -20671,6 +20771,7 @@ var HELP = [
20671
20771
  "/review [focus] review uncommitted changes before committing",
20672
20772
  "/evolve <signals|suggest|improve <id>|verify <name>|reload <name>|test|revert <name>|done <id>> self-improvement loop",
20673
20773
  "/mcp [status|tools|reconnect|enable|disable] manage MCP servers",
20774
+ "/tool <registered-tool> [JSON object] run a registered tool (or use /<tool-name>)",
20674
20775
  "/ide show VS Code connection and cursor/selection",
20675
20776
  "/pals [--verbose|rename <alias>|info <alias-or-uuid>]",
20676
20777
  "/chat <alias-or-uuid> <goal> send a task that starts on the receiving pal",
@@ -20942,7 +21043,12 @@ var FlavorSession = class {
20942
21043
  } catch {
20943
21044
  }
20944
21045
  }
20945
- 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
+ );
20946
21052
  if (command !== null) await this.#dispatch(command, controller2.signal);
20947
21053
  else for await (const event of this.#services.run(prompt, controller2.signal, {
20948
21054
  getSteeringMessages: () => this.#drainMessages("steer", true).map((item) => item.text),
@@ -21044,6 +21150,8 @@ var FlavorSession = class {
21044
21150
  this.#notice(`Main permissions set to ${command.mode}. Child approvals use bubble mode unless plan mode is active.`);
21045
21151
  } else if (command.name === "plugin") {
21046
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)));
21047
21155
  } else if (command.name === "skill") {
21048
21156
  for await (const event of this.#services.runSkill(command.skill, command.prompt, signal)) this.#services.output(event);
21049
21157
  } else if (command.name === "loop") {
@@ -24644,6 +24752,7 @@ async function createProductionRuntime(options) {
24644
24752
  managedTools.push(tool);
24645
24753
  }
24646
24754
  if (harnessCreated) harness.replaceMainTools(tools);
24755
+ options.onToolsChange?.();
24647
24756
  };
24648
24757
  tools.push(...createManagedToolManagementTools({
24649
24758
  store: managedToolStore,
@@ -25973,6 +26082,25 @@ ${renderMemoryDocument(entries)}`;
25973
26082
  signal.throwIfAborted();
25974
26083
  return awaitWithSignal3(Promise.resolve(handler(args, { workspace, signal })), signal);
25975
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
+ },
25976
26104
  output: emitOutput,
25977
26105
  questions,
25978
26106
  async login() {
@@ -29286,7 +29414,8 @@ var DesktopRuntimeController = class {
29286
29414
  ...runtime.approvals.pending === void 0 ? {} : { approval: runtime.approvals.pending },
29287
29415
  ...runtime.services.questions.pending === void 0 ? {} : { questions: runtime.services.questions.pending },
29288
29416
  ...runtime.memoryReviews.pending.length === 0 ? {} : { memoryReviews: runtime.memoryReviews.pending },
29289
- ...(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() }
29290
29419
  },
29291
29420
  diagnostics: runtime?.diagnostics ?? [],
29292
29421
  models: this.#models,
@@ -29340,6 +29469,9 @@ var DesktopRuntimeController = class {
29340
29469
  },
29341
29470
  onApprovalChange: () => {
29342
29471
  if (this.#runtime !== void 0) this.#publishSnapshot();
29472
+ },
29473
+ onToolsChange: () => {
29474
+ if (this.#runtime !== void 0) this.#publishSnapshot();
29343
29475
  }
29344
29476
  });
29345
29477
  outputSessionId = runtime.sessionId;