@agent-native/core 0.132.0 → 0.132.2

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 (51) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +43 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/action.ts +52 -11
  5. package/corpus/core/src/agent/production-agent.ts +192 -28
  6. package/corpus/core/src/chat-threads/store.ts +42 -0
  7. package/corpus/core/src/client/use-chat-threads.ts +76 -16
  8. package/corpus/core/src/server/agent-chat-plugin.ts +16 -1
  9. package/corpus/templates/clips/changelog/2026-07-30-meeting-microphone-transcription-works-reliably-from-the-fir.md +6 -0
  10. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +13 -0
  11. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +2 -0
  12. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +25 -132
  13. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +5 -0
  14. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +5 -3
  15. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +23 -0
  16. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +3 -0
  17. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +11 -7
  18. package/corpus/templates/design/changelog/2026-07-30-fixed-the-keyboard-shortcuts-panel-not-opening-with-ctrl-shi.md +6 -0
  19. package/dist/action.d.ts.map +1 -1
  20. package/dist/action.js +41 -11
  21. package/dist/action.js.map +1 -1
  22. package/dist/agent/production-agent.d.ts.map +1 -1
  23. package/dist/agent/production-agent.js +177 -29
  24. package/dist/agent/production-agent.js.map +1 -1
  25. package/dist/chat-threads/store.d.ts +13 -0
  26. package/dist/chat-threads/store.d.ts.map +1 -1
  27. package/dist/chat-threads/store.js +35 -0
  28. package/dist/chat-threads/store.js.map +1 -1
  29. package/dist/client/use-chat-threads.d.ts.map +1 -1
  30. package/dist/client/use-chat-threads.js +66 -16
  31. package/dist/client/use-chat-threads.js.map +1 -1
  32. package/dist/collab/awareness.d.ts +2 -2
  33. package/dist/collab/awareness.d.ts.map +1 -1
  34. package/dist/collab/routes.d.ts +1 -1
  35. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  36. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  37. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  38. package/dist/notifications/routes.d.ts +6 -6
  39. package/dist/observability/routes.d.ts +3 -3
  40. package/dist/progress/routes.d.ts +1 -1
  41. package/dist/resources/handlers.d.ts +1 -1
  42. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  43. package/dist/server/agent-chat-plugin.js +13 -2
  44. package/dist/server/agent-chat-plugin.js.map +1 -1
  45. package/dist/server/transcribe-voice.d.ts +1 -1
  46. package/package.json +1 -1
  47. package/src/action.ts +52 -11
  48. package/src/agent/production-agent.ts +192 -28
  49. package/src/chat-threads/store.ts +42 -0
  50. package/src/client/use-chat-threads.ts +76 -16
  51. package/src/server/agent-chat-plugin.ts +16 -1
@@ -2531,6 +2531,9 @@ const rawToolInputAjv = new Ajv({
2531
2531
  coerceTypes: true,
2532
2532
  useDefaults: false,
2533
2533
  removeAdditional: false,
2534
+ // `parentSchema`/`data` on each error are what let us drop the branches of a
2535
+ // `oneOf` the caller never meant to match.
2536
+ verbose: true,
2534
2537
  });
2535
2538
  const rawToolInputValidatorCache = new WeakMap();
2536
2539
  const optionalPlaceholderAjv = new Ajv({
@@ -2559,51 +2562,148 @@ function isStructurallyEmptyToolValue(value) {
2559
2562
  value !== null &&
2560
2563
  Object.keys(value).length === 0);
2561
2564
  }
2565
+ function compileToolValueValidator(schema) {
2566
+ const cached = optionalPlaceholderValidatorCache.get(schema);
2567
+ if (cached)
2568
+ return cached;
2569
+ try {
2570
+ const validator = optionalPlaceholderAjv.compile(schema);
2571
+ optionalPlaceholderValidatorCache.set(schema, validator);
2572
+ return validator;
2573
+ }
2574
+ catch {
2575
+ return null;
2576
+ }
2577
+ }
2562
2578
  function schemaAcceptsToolValue(schema, value) {
2563
- let validator = optionalPlaceholderValidatorCache.get(schema);
2564
- if (!validator) {
2565
- try {
2566
- validator = optionalPlaceholderAjv.compile(schema);
2567
- optionalPlaceholderValidatorCache.set(schema, validator);
2568
- }
2569
- catch {
2570
- return false;
2571
- }
2579
+ const validator = compileToolValueValidator(schema);
2580
+ return validator ? Boolean(validator(value)) : false;
2581
+ }
2582
+ /**
2583
+ * True only when the sub-schema compiled AND rejected the value. A sub-schema
2584
+ * that cannot be compiled standalone — a `$ref` into the root `$defs`, say — is
2585
+ * unknown, not invalid; counting it as invalid would let one unreadable
2586
+ * sub-schema delete the caller's intentional empty values everywhere else in
2587
+ * the same object.
2588
+ */
2589
+ function schemaRejectsToolValue(schema, value) {
2590
+ const validator = compileToolValueValidator(schema);
2591
+ return validator ? !validator(value) : false;
2592
+ }
2593
+ /** The `const`/single-value-`enum` a discriminated-union branch pins a key to. */
2594
+ function schemaDiscriminatorValue(schema) {
2595
+ if (!schema)
2596
+ return undefined;
2597
+ if (schema.const !== undefined)
2598
+ return schema.const;
2599
+ if (Array.isArray(schema.enum) && schema.enum.length === 1) {
2600
+ return schema.enum[0];
2572
2601
  }
2573
- return Boolean(validator(value));
2602
+ return undefined;
2603
+ }
2604
+ /**
2605
+ * Index of the `oneOf`/`anyOf` branch whose discriminator constants all match
2606
+ * `value`, or -1 when the union is not discriminated or nothing matches.
2607
+ */
2608
+ function discriminatedBranchIndex(branches, value) {
2609
+ if (!value || typeof value !== "object" || Array.isArray(value))
2610
+ return -1;
2611
+ const record = value;
2612
+ return branches.findIndex((branch) => {
2613
+ const properties = branch.properties;
2614
+ if (!properties)
2615
+ return false;
2616
+ const discriminators = Object.entries(properties).filter(([, spec]) => schemaDiscriminatorValue(spec) !== undefined);
2617
+ if (discriminators.length === 0)
2618
+ return false;
2619
+ return discriminators.every(([key, spec]) => record[key] === schemaDiscriminatorValue(spec));
2620
+ });
2621
+ }
2622
+ /**
2623
+ * The object schema that actually governs `value` — the union branch its
2624
+ * discriminator selects, or the schema itself. Undefined when a union cannot be
2625
+ * resolved, since guessing a branch would strip the wrong keys.
2626
+ */
2627
+ function resolveObjectSchemaBranch(schema, value) {
2628
+ const branches = schema.oneOf ?? schema.anyOf;
2629
+ if (!branches?.length)
2630
+ return schema;
2631
+ const index = discriminatedBranchIndex(branches, value);
2632
+ return index >= 0 ? branches[index] : undefined;
2574
2633
  }
2575
2634
  /**
2576
2635
  * Some model gateways fill every optional tool field with an empty sentinel
2577
2636
  * instead of omitting it. One schema-invalid empty value proves that pattern;
2578
2637
  * only then strip the other optional empty/default sentinels from the call.
2579
2638
  * Calls whose empty values are all schema-valid keep their intentional clears.
2639
+ *
2640
+ * The proof is evaluated per object, and the walk descends through array items
2641
+ * and discriminated-union branches: gateways pre-fill the leaf object they are
2642
+ * building (`operations[0].fields`), which a top-level-only sweep never reaches.
2580
2643
  */
2581
- function normalizeOptionalToolPlaceholders(schema, input) {
2582
- if (!schema?.properties || !input || typeof input !== "object") {
2583
- return { input, changed: false };
2644
+ function stripOptionalToolPlaceholders(schema, value) {
2645
+ if (!schema)
2646
+ return { value, changed: false };
2647
+ if (Array.isArray(value)) {
2648
+ const itemSchema = schema.items;
2649
+ if (!itemSchema)
2650
+ return { value, changed: false };
2651
+ let changed = false;
2652
+ const items = value.map((item) => {
2653
+ const result = stripOptionalToolPlaceholders(itemSchema, item);
2654
+ if (result.changed)
2655
+ changed = true;
2656
+ return result.value;
2657
+ });
2658
+ return changed
2659
+ ? { value: items, changed: true }
2660
+ : { value, changed: false };
2584
2661
  }
2585
- if (Array.isArray(input))
2586
- return { input, changed: false };
2587
- const required = new Set(Array.isArray(schema.required) ? schema.required : []);
2662
+ if (!value || typeof value !== "object")
2663
+ return { value, changed: false };
2664
+ const objectSchema = resolveObjectSchemaBranch(schema, value);
2665
+ const properties = objectSchema?.properties;
2666
+ if (!properties)
2667
+ return { value, changed: false };
2668
+ const required = new Set(Array.isArray(objectSchema.required) ? objectSchema.required : []);
2669
+ const record = value;
2588
2670
  const placeholders = [];
2589
2671
  let hasSchemaInvalidPlaceholder = false;
2590
- for (const [key, value] of Object.entries(input)) {
2591
- if (required.has(key) || !isStructurallyEmptyToolValue(value))
2592
- continue;
2593
- const propertySchema = schema.properties[key];
2672
+ let normalized = null;
2673
+ for (const [key, entry] of Object.entries(record)) {
2674
+ const propertySchema = properties[key];
2594
2675
  if (!propertySchema || typeof propertySchema !== "object")
2595
2676
  continue;
2596
- placeholders.push(key);
2597
- if (!schemaAcceptsToolValue(propertySchema, value)) {
2598
- hasSchemaInvalidPlaceholder = true;
2677
+ if (!required.has(key) && isStructurallyEmptyToolValue(entry)) {
2678
+ placeholders.push(key);
2679
+ if (schemaRejectsToolValue(propertySchema, entry)) {
2680
+ hasSchemaInvalidPlaceholder = true;
2681
+ }
2682
+ continue;
2683
+ }
2684
+ const nested = stripOptionalToolPlaceholders(propertySchema, entry);
2685
+ if (nested.changed) {
2686
+ normalized ??= { ...record };
2687
+ normalized[key] = nested.value;
2599
2688
  }
2600
2689
  }
2601
- if (!hasSchemaInvalidPlaceholder)
2690
+ if (hasSchemaInvalidPlaceholder) {
2691
+ normalized ??= { ...record };
2692
+ for (const key of placeholders)
2693
+ delete normalized[key];
2694
+ }
2695
+ return normalized
2696
+ ? { value: normalized, changed: true }
2697
+ : { value, changed: false };
2698
+ }
2699
+ function normalizeOptionalToolPlaceholders(schema, input) {
2700
+ if (!schema?.properties || !input || typeof input !== "object") {
2701
+ return { input, changed: false };
2702
+ }
2703
+ if (Array.isArray(input))
2602
2704
  return { input, changed: false };
2603
- const normalized = { ...input };
2604
- for (const key of placeholders)
2605
- delete normalized[key];
2606
- return { input: normalized, changed: true };
2705
+ const result = stripOptionalToolPlaceholders(schema, input);
2706
+ return { input: result.value, changed: result.changed };
2607
2707
  }
2608
2708
  /**
2609
2709
  * Models routinely JSON-encode a field's value into a string when the tool
@@ -2672,6 +2772,54 @@ function shouldValidateRawToolParameters(entry) {
2672
2772
  const maybeSchema = entry.schema;
2673
2773
  return !maybeSchema?.["~standard"] && Boolean(entry.tool.parameters);
2674
2774
  }
2775
+ /**
2776
+ * With `allErrors`, a failing union reports every branch, so a five-branch
2777
+ * union answers "your `patch-deck-fields` op has a bad enum" with eight
2778
+ * complaints about `slideId`/`orderedIds` from the four branches the caller
2779
+ * never meant. The model reads the loudest, wrong advice and re-sends the same
2780
+ * arguments until the identical-error breaker ends the turn. When the
2781
+ * discriminator names a branch, report only that branch's errors.
2782
+ *
2783
+ * Both union keywords are load-bearing for the same Zod schema: Zod v4's own
2784
+ * `toJSONSchema` emits `oneOf` for a discriminated union, while the manual
2785
+ * fallback converter in `action.ts` emits `anyOf`.
2786
+ *
2787
+ * Scoped by `instancePath` as well as `schemaPath`: every element of an array
2788
+ * shares one `items` schema, so `operations[0]` and `operations[1]` produce
2789
+ * branch errors under the same `schemaPath` and only the instance path says
2790
+ * which element they belong to.
2791
+ */
2792
+ function isWithinInstancePath(candidate, root) {
2793
+ return candidate === root || candidate.startsWith(`${root}/`);
2794
+ }
2795
+ function narrowUnionBranchErrors(errors) {
2796
+ if (!errors?.length)
2797
+ return errors;
2798
+ let kept = errors;
2799
+ for (const error of errors) {
2800
+ const keyword = error.keyword;
2801
+ if (keyword !== "oneOf" && keyword !== "anyOf")
2802
+ continue;
2803
+ const branches = error.parentSchema?.[keyword];
2804
+ if (!branches?.length)
2805
+ continue;
2806
+ const index = discriminatedBranchIndex(branches, error.data);
2807
+ if (index < 0)
2808
+ continue;
2809
+ const branchPrefix = `${error.schemaPath}/`;
2810
+ kept = kept.filter((candidate) => {
2811
+ if (candidate === error)
2812
+ return false;
2813
+ if (!isWithinInstancePath(candidate.instancePath, error.instancePath)) {
2814
+ return true;
2815
+ }
2816
+ if (!candidate.schemaPath.startsWith(branchPrefix))
2817
+ return true;
2818
+ return candidate.schemaPath.startsWith(`${branchPrefix}${index}/`);
2819
+ });
2820
+ }
2821
+ return kept.length ? kept : errors;
2822
+ }
2675
2823
  function validateRawToolInput(entry, input) {
2676
2824
  if (!shouldValidateRawToolParameters(entry))
2677
2825
  return null;
@@ -2687,7 +2835,7 @@ function validateRawToolInput(entry, input) {
2687
2835
  }
2688
2836
  if (validator(input === undefined ? {} : input))
2689
2837
  return null;
2690
- return rawToolInputAjv.errorsText(validator.errors, {
2838
+ return rawToolInputAjv.errorsText(narrowUnionBranchErrors(validator.errors), {
2691
2839
  separator: "; ",
2692
2840
  dataVar: "input",
2693
2841
  });