@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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +43 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action.ts +52 -11
- package/corpus/core/src/agent/production-agent.ts +192 -28
- package/corpus/core/src/chat-threads/store.ts +42 -0
- package/corpus/core/src/client/use-chat-threads.ts +76 -16
- package/corpus/core/src/server/agent-chat-plugin.ts +16 -1
- package/corpus/templates/clips/changelog/2026-07-30-meeting-microphone-transcription-works-reliably-from-the-fir.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +13 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +2 -0
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +25 -132
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +5 -0
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +5 -3
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +23 -0
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +3 -0
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +11 -7
- package/corpus/templates/design/changelog/2026-07-30-fixed-the-keyboard-shortcuts-panel-not-opening-with-ctrl-shi.md +6 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +41 -11
- package/dist/action.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +177 -29
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/chat-threads/store.d.ts +13 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +35 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +66 -16
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +13 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
- package/src/action.ts +52 -11
- package/src/agent/production-agent.ts +192 -28
- package/src/chat-threads/store.ts +42 -0
- package/src/client/use-chat-threads.ts +76 -16
- 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
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
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
|
|
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
|
|
2582
|
-
if (!schema
|
|
2583
|
-
return {
|
|
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 (
|
|
2586
|
-
return {
|
|
2587
|
-
const
|
|
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
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
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
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
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 (
|
|
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
|
|
2604
|
-
|
|
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
|
});
|