@ai-matrx/agents 0.59.256 → 0.59.258
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/CHANGELOG.md +11 -1
- package/dist/catalog/native/index.cjs +11 -9
- package/dist/catalog/native/index.d.cts +27 -44
- package/dist/catalog/native/index.d.ts +27 -44
- package/dist/catalog/native/index.js +12 -16
- package/dist/catalog/react/index.cjs +11 -10
- package/dist/catalog/react/index.d.cts +27 -44
- package/dist/catalog/react/index.d.ts +27 -44
- package/dist/catalog/react/index.js +12 -17
- package/dist/content-transfer/react/index.cjs +0 -1
- package/dist/content-transfer/react/index.js +0 -1
- package/dist/intelligence/react/index.d.cts +0 -6
- package/dist/intelligence/react/index.d.ts +0 -6
- package/dist/models/index.cjs +74 -4
- package/dist/models/index.d.cts +91 -1
- package/dist/models/index.d.ts +91 -1
- package/dist/models/index.js +74 -4
- package/dist/models/react/index.cjs +31 -1
- package/dist/models/react/index.d.cts +31 -1
- package/dist/models/react/index.d.ts +31 -1
- package/dist/models/react/index.js +37 -7
- package/dist/runtime/react/index.cjs +0 -1
- package/dist/runtime/react/index.js +0 -1
- package/dist/settings/react/index.cjs +0 -1
- package/dist/settings/react/index.js +0 -1
- package/dist/skills/react/index.cjs +0 -1
- package/dist/skills/react/index.js +0 -1
- package/dist/sources/index.d.cts +8 -2
- package/dist/sources/index.d.ts +8 -2
- package/dist/sources/react/index.d.cts +8 -17
- package/dist/sources/react/index.d.ts +8 -17
- package/package.json +7 -6
- package/scripts/check-model-catalog-reads.mjs +122 -0
|
@@ -398,6 +398,14 @@ interface ModelCatalog {
|
|
|
398
398
|
load(variant: ModelCatalogVariant): Promise<void>;
|
|
399
399
|
/** Re-read the variant now (rows stay visible while it runs). Never throws. */
|
|
400
400
|
reload(variant: ModelCatalogVariant): Promise<void>;
|
|
401
|
+
/**
|
|
402
|
+
* The catalog changed upstream (`catalog_changed`): re-read every picker variant already read and every
|
|
403
|
+
* record already read (`records.refresh()`). Called by itself on every `announceModelCatalogChanged`
|
|
404
|
+
* (catalog/changes.ts) unless `followChanges: false`. Never throws.
|
|
405
|
+
*/
|
|
406
|
+
refresh(): Promise<void>;
|
|
407
|
+
/** Stop following `catalog_changed` (a test, or a catalog a host discards). */
|
|
408
|
+
dispose(): void;
|
|
401
409
|
/**
|
|
402
410
|
* The RECORDS part (agent core B3): options list, full records with resolved controls, class
|
|
403
411
|
* configs, the class index and identities — what chat's retired `modelRegistry` slice held.
|
|
@@ -650,8 +658,30 @@ interface ModelListDropdownBaseProps {
|
|
|
650
658
|
}
|
|
651
659
|
declare function ModelListDropdown({ value: savedValue, localModels: localModelsProp, access: accessProp, onValueChange, inputModalities, outputModalities, selectionPurpose, allowedModelIds, catalogVariant, emptyOptionLabel, onClear, placeholder, priorityModelIds, pinnedOfferingId, onOfferingPinChange, modelOnly, className, triggerVariant, presentation, id, disabled, disabledTitle, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }: ModelListDropdownProps): react.JSX.Element;
|
|
652
660
|
|
|
661
|
+
/**
|
|
662
|
+
* `@ai-matrx/agents/models` — the ONE subscription to `catalog_changed`.
|
|
663
|
+
*
|
|
664
|
+
* The server publishes `catalog_changed` on the platform bus for every admin edit and direct SQL write to
|
|
665
|
+
* the model catalog. A host binds its bus client ONCE with `followModelCatalogChanges(subscribe)` (the web's
|
|
666
|
+
* directive registry, Matrx 2's IPC forward of the main-process bus); every model catalog
|
|
667
|
+
* (`createModelCatalog`: picker rows + records) re-reads itself on the notice, and any other reader
|
|
668
|
+
* subscribes here (`onModelCatalogChanged`, or `useModelCatalogRevision` / `useOnModelCatalogChanged` in
|
|
669
|
+
* `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
|
|
670
|
+
*
|
|
671
|
+
* The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
|
|
672
|
+
*/
|
|
673
|
+
/** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
|
|
674
|
+
type ModelCatalogChangePayload = Record<string, unknown>;
|
|
675
|
+
|
|
653
676
|
/** Subscribe to a slice of a model catalog's records. `select` must return a stable value for unchanged state. */
|
|
654
677
|
declare function useModelRecordsSelector<T>(records: ModelRecords, select: (state: ModelRecordsState) => T): T;
|
|
678
|
+
/**
|
|
679
|
+
* How many `catalog_changed` notices this page has seen. A reader that holds its OWN rows (the admin model
|
|
680
|
+
* editor) puts it in its effect dependencies / query key, so an edit made anywhere re-reads it.
|
|
681
|
+
*/
|
|
682
|
+
declare function useModelCatalogRevision(): number;
|
|
683
|
+
/** Run `onChange` on every model catalog change notice (the latest callback; never stale). */
|
|
684
|
+
declare function useOnModelCatalogChanged(onChange: (payload: ModelCatalogChangePayload) => void): void;
|
|
655
685
|
|
|
656
686
|
/**
|
|
657
687
|
* `@ai-matrx/agents/models` — downloadable (local) models: the catalog shape every client reads, the
|
|
@@ -1010,4 +1040,4 @@ interface DiscoveryResultViewProps {
|
|
|
1010
1040
|
/** A resolved model: its header, the source's reason when the originals will not run, and every version judged. */
|
|
1011
1041
|
declare function DiscoveryResultView({ result, device, runtimes, want, chooseLabel, mode, onChoose, renderVariantActions, renderActions }: DiscoveryResultViewProps): react.JSX.Element;
|
|
1012
1042
|
|
|
1013
|
-
export { type DiscoveryKind, type DiscoveryMode, type DiscoveryPort, DiscoveryResultView, type DiscoveryResultViewProps, type DiscoverySearchHit, type DiscoverySearchPage, type DiscoverySearchQuery, type DiscoverySort, type LocalModelEntry, type LocalModelsPort, type ModelAccessPort, type ModelCatalogLinkComponent, ModelCatalogProvider, type ModelCatalogProviderProps, type ModelCatalogUiPorts, ModelDiscovery, type ModelDiscoveryProps, ModelListDropdown, type ModelListDropdownProps, YOUR_KEY_LABEL, useLocalModels, useModelAccess, useModelCatalog, useModelCatalogStore, useModelCatalogUiPorts, useModelFavorites, useModelRecordsSelector };
|
|
1043
|
+
export { type DiscoveryKind, type DiscoveryMode, type DiscoveryPort, DiscoveryResultView, type DiscoveryResultViewProps, type DiscoverySearchHit, type DiscoverySearchPage, type DiscoverySearchQuery, type DiscoverySort, type LocalModelEntry, type LocalModelsPort, type ModelAccessPort, type ModelCatalogLinkComponent, ModelCatalogProvider, type ModelCatalogProviderProps, type ModelCatalogUiPorts, ModelDiscovery, type ModelDiscoveryProps, ModelListDropdown, type ModelListDropdownProps, YOUR_KEY_LABEL, useLocalModels, useModelAccess, useModelCatalog, useModelCatalogRevision, useModelCatalogStore, useModelCatalogUiPorts, useModelFavorites, useModelRecordsSelector, useOnModelCatalogChanged };
|
|
@@ -398,6 +398,14 @@ interface ModelCatalog {
|
|
|
398
398
|
load(variant: ModelCatalogVariant): Promise<void>;
|
|
399
399
|
/** Re-read the variant now (rows stay visible while it runs). Never throws. */
|
|
400
400
|
reload(variant: ModelCatalogVariant): Promise<void>;
|
|
401
|
+
/**
|
|
402
|
+
* The catalog changed upstream (`catalog_changed`): re-read every picker variant already read and every
|
|
403
|
+
* record already read (`records.refresh()`). Called by itself on every `announceModelCatalogChanged`
|
|
404
|
+
* (catalog/changes.ts) unless `followChanges: false`. Never throws.
|
|
405
|
+
*/
|
|
406
|
+
refresh(): Promise<void>;
|
|
407
|
+
/** Stop following `catalog_changed` (a test, or a catalog a host discards). */
|
|
408
|
+
dispose(): void;
|
|
401
409
|
/**
|
|
402
410
|
* The RECORDS part (agent core B3): options list, full records with resolved controls, class
|
|
403
411
|
* configs, the class index and identities — what chat's retired `modelRegistry` slice held.
|
|
@@ -650,8 +658,30 @@ interface ModelListDropdownBaseProps {
|
|
|
650
658
|
}
|
|
651
659
|
declare function ModelListDropdown({ value: savedValue, localModels: localModelsProp, access: accessProp, onValueChange, inputModalities, outputModalities, selectionPurpose, allowedModelIds, catalogVariant, emptyOptionLabel, onClear, placeholder, priorityModelIds, pinnedOfferingId, onOfferingPinChange, modelOnly, className, triggerVariant, presentation, id, disabled, disabledTitle, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }: ModelListDropdownProps): react.JSX.Element;
|
|
652
660
|
|
|
661
|
+
/**
|
|
662
|
+
* `@ai-matrx/agents/models` — the ONE subscription to `catalog_changed`.
|
|
663
|
+
*
|
|
664
|
+
* The server publishes `catalog_changed` on the platform bus for every admin edit and direct SQL write to
|
|
665
|
+
* the model catalog. A host binds its bus client ONCE with `followModelCatalogChanges(subscribe)` (the web's
|
|
666
|
+
* directive registry, Matrx 2's IPC forward of the main-process bus); every model catalog
|
|
667
|
+
* (`createModelCatalog`: picker rows + records) re-reads itself on the notice, and any other reader
|
|
668
|
+
* subscribes here (`onModelCatalogChanged`, or `useModelCatalogRevision` / `useOnModelCatalogChanged` in
|
|
669
|
+
* `@ai-matrx/agents/models/react`). No surface keeps a model cache that misses the event.
|
|
670
|
+
*
|
|
671
|
+
* The hub lives on `globalThis` so two bundled copies of this module share one set of listeners.
|
|
672
|
+
*/
|
|
673
|
+
/** A `catalog_changed` payload: `{ catalog?: "models" | ..., change?, model_id? }`. */
|
|
674
|
+
type ModelCatalogChangePayload = Record<string, unknown>;
|
|
675
|
+
|
|
653
676
|
/** Subscribe to a slice of a model catalog's records. `select` must return a stable value for unchanged state. */
|
|
654
677
|
declare function useModelRecordsSelector<T>(records: ModelRecords, select: (state: ModelRecordsState) => T): T;
|
|
678
|
+
/**
|
|
679
|
+
* How many `catalog_changed` notices this page has seen. A reader that holds its OWN rows (the admin model
|
|
680
|
+
* editor) puts it in its effect dependencies / query key, so an edit made anywhere re-reads it.
|
|
681
|
+
*/
|
|
682
|
+
declare function useModelCatalogRevision(): number;
|
|
683
|
+
/** Run `onChange` on every model catalog change notice (the latest callback; never stale). */
|
|
684
|
+
declare function useOnModelCatalogChanged(onChange: (payload: ModelCatalogChangePayload) => void): void;
|
|
655
685
|
|
|
656
686
|
/**
|
|
657
687
|
* `@ai-matrx/agents/models` — downloadable (local) models: the catalog shape every client reads, the
|
|
@@ -1010,4 +1040,4 @@ interface DiscoveryResultViewProps {
|
|
|
1010
1040
|
/** A resolved model: its header, the source's reason when the originals will not run, and every version judged. */
|
|
1011
1041
|
declare function DiscoveryResultView({ result, device, runtimes, want, chooseLabel, mode, onChoose, renderVariantActions, renderActions }: DiscoveryResultViewProps): react.JSX.Element;
|
|
1012
1042
|
|
|
1013
|
-
export { type DiscoveryKind, type DiscoveryMode, type DiscoveryPort, DiscoveryResultView, type DiscoveryResultViewProps, type DiscoverySearchHit, type DiscoverySearchPage, type DiscoverySearchQuery, type DiscoverySort, type LocalModelEntry, type LocalModelsPort, type ModelAccessPort, type ModelCatalogLinkComponent, ModelCatalogProvider, type ModelCatalogProviderProps, type ModelCatalogUiPorts, ModelDiscovery, type ModelDiscoveryProps, ModelListDropdown, type ModelListDropdownProps, YOUR_KEY_LABEL, useLocalModels, useModelAccess, useModelCatalog, useModelCatalogStore, useModelCatalogUiPorts, useModelFavorites, useModelRecordsSelector };
|
|
1043
|
+
export { type DiscoveryKind, type DiscoveryMode, type DiscoveryPort, DiscoveryResultView, type DiscoveryResultViewProps, type DiscoverySearchHit, type DiscoverySearchPage, type DiscoverySearchQuery, type DiscoverySort, type LocalModelEntry, type LocalModelsPort, type ModelAccessPort, type ModelCatalogLinkComponent, ModelCatalogProvider, type ModelCatalogProviderProps, type ModelCatalogUiPorts, ModelDiscovery, type ModelDiscoveryProps, ModelListDropdown, type ModelListDropdownProps, YOUR_KEY_LABEL, useLocalModels, useModelAccess, useModelCatalog, useModelCatalogRevision, useModelCatalogStore, useModelCatalogUiPorts, useModelFavorites, useModelRecordsSelector, useOnModelCatalogChanged };
|
|
@@ -2280,7 +2280,26 @@ function ModelListDropdown({
|
|
|
2280
2280
|
}
|
|
2281
2281
|
|
|
2282
2282
|
// models/react/records.ts
|
|
2283
|
-
import { useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
2283
|
+
import { useEffect as useEffect3, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
2284
|
+
|
|
2285
|
+
// models/catalog/changes.ts
|
|
2286
|
+
var KEY = /* @__PURE__ */ Symbol.for("ai-matrx.agents.model-catalog-changes");
|
|
2287
|
+
function hub() {
|
|
2288
|
+
const g = globalThis;
|
|
2289
|
+
return g[KEY] ??= { listeners: /* @__PURE__ */ new Set(), revision: 0 };
|
|
2290
|
+
}
|
|
2291
|
+
function onModelCatalogChanged(listener) {
|
|
2292
|
+
const h = hub();
|
|
2293
|
+
h.listeners.add(listener);
|
|
2294
|
+
return () => {
|
|
2295
|
+
h.listeners.delete(listener);
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
function modelCatalogRevision() {
|
|
2299
|
+
return hub().revision;
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
// models/react/records.ts
|
|
2284
2303
|
function useModelRecordsSelector(records, select) {
|
|
2285
2304
|
return useSyncExternalStore2(
|
|
2286
2305
|
records.subscribe,
|
|
@@ -2288,10 +2307,19 @@ function useModelRecordsSelector(records, select) {
|
|
|
2288
2307
|
() => select(records.getState())
|
|
2289
2308
|
);
|
|
2290
2309
|
}
|
|
2310
|
+
var subscribeRevision = (listener) => onModelCatalogChanged(() => listener());
|
|
2311
|
+
function useModelCatalogRevision() {
|
|
2312
|
+
return useSyncExternalStore2(subscribeRevision, modelCatalogRevision, modelCatalogRevision);
|
|
2313
|
+
}
|
|
2314
|
+
function useOnModelCatalogChanged(onChange) {
|
|
2315
|
+
const latest = useRef2(onChange);
|
|
2316
|
+
latest.current = onChange;
|
|
2317
|
+
useEffect3(() => onModelCatalogChanged((payload) => latest.current(payload)), []);
|
|
2318
|
+
}
|
|
2291
2319
|
|
|
2292
2320
|
// models/react/ModelDiscovery.tsx
|
|
2293
2321
|
import { formatCount as formatCount2, formatFileSize } from "@ai-matrx/kit/format";
|
|
2294
|
-
import { useEffect as
|
|
2322
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
|
|
2295
2323
|
import { Avatar, AvatarFallback, AvatarImage, Collapsible, CollapsibleContent, CollapsibleTrigger, ErrorNotice } from "@ai-matrx/design-system";
|
|
2296
2324
|
import { Badge, Button, Field, SearchField, Select, Tabs } from "@ai-matrx/design-system/controls";
|
|
2297
2325
|
|
|
@@ -2570,8 +2598,8 @@ function ModelDiscovery({
|
|
|
2570
2598
|
const result = controlled !== void 0 ? controlled : own;
|
|
2571
2599
|
const [resolving, setResolving] = useState2(null);
|
|
2572
2600
|
const [resolveError, setResolveError] = useState2(null);
|
|
2573
|
-
const abortRef =
|
|
2574
|
-
|
|
2601
|
+
const abortRef = useRef3(null);
|
|
2602
|
+
useEffect4(() => () => abortRef.current?.abort(), []);
|
|
2575
2603
|
const resolve = async (link) => {
|
|
2576
2604
|
const trimmed = link.trim();
|
|
2577
2605
|
if (!trimmed) return;
|
|
@@ -2661,7 +2689,7 @@ function SearchTab({ port, onPick }) {
|
|
|
2661
2689
|
const [page, setPage] = useState2(null);
|
|
2662
2690
|
const [busy, setBusy] = useState2(false);
|
|
2663
2691
|
const [error, setError] = useState2(null);
|
|
2664
|
-
const seq =
|
|
2692
|
+
const seq = useRef3(0);
|
|
2665
2693
|
const run = async (cursor) => {
|
|
2666
2694
|
if (!port.search) return;
|
|
2667
2695
|
const mine = ++seq.current;
|
|
@@ -2677,7 +2705,7 @@ function SearchTab({ port, onPick }) {
|
|
|
2677
2705
|
if (mine === seq.current) setBusy(false);
|
|
2678
2706
|
}
|
|
2679
2707
|
};
|
|
2680
|
-
|
|
2708
|
+
useEffect4(() => {
|
|
2681
2709
|
if (port.search) void run(null);
|
|
2682
2710
|
}, [kind, sort]);
|
|
2683
2711
|
if (!port.search) return /* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground", children: "Search is not available here yet. Paste a link instead." });
|
|
@@ -2814,8 +2842,10 @@ export {
|
|
|
2814
2842
|
useLocalModels,
|
|
2815
2843
|
useModelAccess,
|
|
2816
2844
|
useModelCatalog,
|
|
2845
|
+
useModelCatalogRevision,
|
|
2817
2846
|
useModelCatalogStore,
|
|
2818
2847
|
useModelCatalogUiPorts,
|
|
2819
2848
|
useModelFavorites,
|
|
2820
|
-
useModelRecordsSelector
|
|
2849
|
+
useModelRecordsSelector,
|
|
2850
|
+
useOnModelCatalogChanged
|
|
2821
2851
|
};
|
package/dist/sources/index.d.cts
CHANGED
|
@@ -47,7 +47,13 @@ interface SourceRef {
|
|
|
47
47
|
promote?: ResourcePromotion | ResourcePromotion[];
|
|
48
48
|
exclude?: string[];
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* A TYPE, not an interface (2026-10-23): it IS a JSON object, so it is assignable wherever the server
|
|
52
|
+
* contract types the field as a JSON object (@ai-matrx/api's request bodies carry `source_set` as
|
|
53
|
+
* `{ [key: string]: JsonValue }` — the podcast generate request). An interface has no implicit index
|
|
54
|
+
* signature and would need a cast at every send.
|
|
55
|
+
*/
|
|
56
|
+
type SourceSet = {
|
|
51
57
|
__kind: typeof SOURCE_SET_KIND;
|
|
52
58
|
version: typeof SOURCE_SET_VERSION;
|
|
53
59
|
sources: SourceRef[];
|
|
@@ -59,7 +65,7 @@ interface SourceSet {
|
|
|
59
65
|
retrieve_query?: string;
|
|
60
66
|
/** The model whose context window sets the budget. */
|
|
61
67
|
target_model_id?: string;
|
|
62
|
-
}
|
|
68
|
+
};
|
|
63
69
|
type SourceState = "ready" | "processing" | "failed" | "unavailable";
|
|
64
70
|
interface SourceManifestForm {
|
|
65
71
|
form: string;
|
package/dist/sources/index.d.ts
CHANGED
|
@@ -47,7 +47,13 @@ interface SourceRef {
|
|
|
47
47
|
promote?: ResourcePromotion | ResourcePromotion[];
|
|
48
48
|
exclude?: string[];
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* A TYPE, not an interface (2026-10-23): it IS a JSON object, so it is assignable wherever the server
|
|
52
|
+
* contract types the field as a JSON object (@ai-matrx/api's request bodies carry `source_set` as
|
|
53
|
+
* `{ [key: string]: JsonValue }` — the podcast generate request). An interface has no implicit index
|
|
54
|
+
* signature and would need a cast at every send.
|
|
55
|
+
*/
|
|
56
|
+
type SourceSet = {
|
|
51
57
|
__kind: typeof SOURCE_SET_KIND;
|
|
52
58
|
version: typeof SOURCE_SET_VERSION;
|
|
53
59
|
sources: SourceRef[];
|
|
@@ -59,7 +65,7 @@ interface SourceSet {
|
|
|
59
65
|
retrieve_query?: string;
|
|
60
66
|
/** The model whose context window sets the budget. */
|
|
61
67
|
target_model_id?: string;
|
|
62
|
-
}
|
|
68
|
+
};
|
|
63
69
|
type SourceState = "ready" | "processing" | "failed" | "unavailable";
|
|
64
70
|
interface SourceManifestForm {
|
|
65
71
|
form: string;
|
|
@@ -47,7 +47,13 @@ interface SourceRef {
|
|
|
47
47
|
promote?: ResourcePromotion | ResourcePromotion[];
|
|
48
48
|
exclude?: string[];
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* A TYPE, not an interface (2026-10-23): it IS a JSON object, so it is assignable wherever the server
|
|
52
|
+
* contract types the field as a JSON object (@ai-matrx/api's request bodies carry `source_set` as
|
|
53
|
+
* `{ [key: string]: JsonValue }` — the podcast generate request). An interface has no implicit index
|
|
54
|
+
* signature and would need a cast at every send.
|
|
55
|
+
*/
|
|
56
|
+
type SourceSet = {
|
|
51
57
|
__kind: typeof SOURCE_SET_KIND;
|
|
52
58
|
version: typeof SOURCE_SET_VERSION;
|
|
53
59
|
sources: SourceRef[];
|
|
@@ -59,7 +65,7 @@ interface SourceSet {
|
|
|
59
65
|
retrieve_query?: string;
|
|
60
66
|
/** The model whose context window sets the budget. */
|
|
61
67
|
target_model_id?: string;
|
|
62
|
-
}
|
|
68
|
+
};
|
|
63
69
|
type SourceState = "ready" | "processing" | "failed" | "unavailable";
|
|
64
70
|
interface SourceManifestForm {
|
|
65
71
|
form: string;
|
|
@@ -705,21 +711,6 @@ interface FileRecoveryDeps {
|
|
|
705
711
|
fileOrganizationId?: ((fileId: string) => string | null | undefined) | undefined;
|
|
706
712
|
}
|
|
707
713
|
|
|
708
|
-
/**
|
|
709
|
-
* `@ai-matrx/agents/sources/react` — thin React hooks over the Source runtime
|
|
710
|
-
* (`@ai-matrx/agents/sources/runtime`). No logic lives here that a non-React
|
|
711
|
-
* client could not reach through the runtime itself: the hooks subscribe,
|
|
712
|
-
* schedule, and hand back the same controller.
|
|
713
|
-
*
|
|
714
|
-
* const set = useSourceSet(adapter, { persistenceReady });
|
|
715
|
-
* const intake = useSourceIntake(set, deps);
|
|
716
|
-
* useSourceRecovery(set, intake, runner, { organizationId, readFileState });
|
|
717
|
-
* const search = useSourcePartsSearch(client, ref, query);
|
|
718
|
-
*
|
|
719
|
-
* A host binds the adapter to its own store once (memoized per surface) —
|
|
720
|
-
* the web app binds Redux; another client binds its own.
|
|
721
|
-
*/
|
|
722
|
-
|
|
723
714
|
interface UseSourceSetOptions {
|
|
724
715
|
/**
|
|
725
716
|
* The draft store has been read back (a device store loads a moment after
|
|
@@ -47,7 +47,13 @@ interface SourceRef {
|
|
|
47
47
|
promote?: ResourcePromotion | ResourcePromotion[];
|
|
48
48
|
exclude?: string[];
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* A TYPE, not an interface (2026-10-23): it IS a JSON object, so it is assignable wherever the server
|
|
52
|
+
* contract types the field as a JSON object (@ai-matrx/api's request bodies carry `source_set` as
|
|
53
|
+
* `{ [key: string]: JsonValue }` — the podcast generate request). An interface has no implicit index
|
|
54
|
+
* signature and would need a cast at every send.
|
|
55
|
+
*/
|
|
56
|
+
type SourceSet = {
|
|
51
57
|
__kind: typeof SOURCE_SET_KIND;
|
|
52
58
|
version: typeof SOURCE_SET_VERSION;
|
|
53
59
|
sources: SourceRef[];
|
|
@@ -59,7 +65,7 @@ interface SourceSet {
|
|
|
59
65
|
retrieve_query?: string;
|
|
60
66
|
/** The model whose context window sets the budget. */
|
|
61
67
|
target_model_id?: string;
|
|
62
|
-
}
|
|
68
|
+
};
|
|
63
69
|
type SourceState = "ready" | "processing" | "failed" | "unavailable";
|
|
64
70
|
interface SourceManifestForm {
|
|
65
71
|
form: string;
|
|
@@ -705,21 +711,6 @@ interface FileRecoveryDeps {
|
|
|
705
711
|
fileOrganizationId?: ((fileId: string) => string | null | undefined) | undefined;
|
|
706
712
|
}
|
|
707
713
|
|
|
708
|
-
/**
|
|
709
|
-
* `@ai-matrx/agents/sources/react` — thin React hooks over the Source runtime
|
|
710
|
-
* (`@ai-matrx/agents/sources/runtime`). No logic lives here that a non-React
|
|
711
|
-
* client could not reach through the runtime itself: the hooks subscribe,
|
|
712
|
-
* schedule, and hand back the same controller.
|
|
713
|
-
*
|
|
714
|
-
* const set = useSourceSet(adapter, { persistenceReady });
|
|
715
|
-
* const intake = useSourceIntake(set, deps);
|
|
716
|
-
* useSourceRecovery(set, intake, runner, { organizationId, readFileState });
|
|
717
|
-
* const search = useSourcePartsSearch(client, ref, query);
|
|
718
|
-
*
|
|
719
|
-
* A host binds the adapter to its own store once (memoized per surface) —
|
|
720
|
-
* the web app binds Redux; another client binds its own.
|
|
721
|
-
*/
|
|
722
|
-
|
|
723
714
|
interface UseSourceSetOptions {
|
|
724
715
|
/**
|
|
725
716
|
* The draft store has been read back (a device store loads a moment after
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/agents",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.258",
|
|
4
4
|
"description": "Portable AI Matrx agent stream protocol, event projection, and safe result-presentation primitives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
|
+
"scripts/check-model-catalog-reads.mjs",
|
|
27
28
|
"README.md",
|
|
28
29
|
"LICENSE",
|
|
29
30
|
"CHANGELOG.md"
|
|
@@ -508,12 +509,12 @@
|
|
|
508
509
|
},
|
|
509
510
|
"sideEffects": false,
|
|
510
511
|
"dependencies": {
|
|
511
|
-
"@ai-matrx/api": "latest",
|
|
512
|
-
"@ai-matrx/data": "latest",
|
|
513
512
|
"@ai-matrx/alchemy": "latest",
|
|
513
|
+
"@ai-matrx/api": "latest",
|
|
514
|
+
"@ai-matrx/associations": "latest",
|
|
514
515
|
"@ai-matrx/content-ir": "latest",
|
|
516
|
+
"@ai-matrx/data": "latest",
|
|
515
517
|
"@ai-matrx/design-system": "latest",
|
|
516
|
-
"@ai-matrx/associations": "latest",
|
|
517
518
|
"@ai-matrx/diff": "latest",
|
|
518
519
|
"@ai-matrx/icons": "latest",
|
|
519
520
|
"@ai-matrx/kit": "latest"
|
|
@@ -555,11 +556,11 @@
|
|
|
555
556
|
"scripts": {
|
|
556
557
|
"build": "pnpm clean && node ./scripts/build-parts.mjs && node ../scripts/guard-exit.mjs ../scripts/check-node-env-not-inlined.mjs",
|
|
557
558
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
558
|
-
"check:package": "node ./scripts/check-catalog-reader-columns.mjs && node ./scripts/check-catalog-reader-columns.mjs --self-test && node ../scripts/check-no-model-literals.mjs && node ../scripts/check-no-model-literals.mjs --self-test && node ../scripts/check-agents-below-chat.mjs && node ../scripts/check-agents-below-chat.mjs --self-test && pnpm build && node ../scripts/check-exports-agreement.mjs && publint && pnpm verify:tarball && node ../scripts/check-tarball-relative-imports.mjs --self-test && node ../scripts/check-tarball-relative-imports.mjs --pack . && node ../scripts/check-package-rules.mjs --self-test && node ../scripts/check-package-rules.mjs .",
|
|
559
|
+
"check:package": "node ./scripts/check-model-catalog-reads.mjs && node ./scripts/check-model-catalog-reads.mjs --self-test && node ./scripts/check-catalog-reader-columns.mjs && node ./scripts/check-catalog-reader-columns.mjs --self-test && node ../scripts/check-no-model-literals.mjs && node ../scripts/check-no-model-literals.mjs --self-test && node ../scripts/check-agents-below-chat.mjs && node ../scripts/check-agents-below-chat.mjs --self-test && pnpm build && node ../scripts/check-exports-agreement.mjs && publint && pnpm verify:tarball && node ../scripts/check-tarball-relative-imports.mjs --self-test && node ../scripts/check-tarball-relative-imports.mjs --pack . && node ../scripts/check-package-rules.mjs --self-test && node ../scripts/check-package-rules.mjs .",
|
|
559
560
|
"typecheck": "tsc --noEmit",
|
|
560
561
|
"test": "vitest run",
|
|
561
562
|
"verify:tarball": "node ./scripts/verify-tarball.mjs",
|
|
562
563
|
"typegen:self-test": "node scripts/typegen/drop-guard.self-test.mjs && node scripts/typegen/check-api-types-fresh.mjs --self-test"
|
|
563
564
|
},
|
|
564
|
-
"gitHead": "
|
|
565
|
+
"gitHead": "48dc0c53e766ad507fa37f41893b2b358be229f4"
|
|
565
566
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ONE MODEL CATALOG READER (owner ruling 2026-10-23). Every surface that names or lists a model reads the
|
|
4
|
+
* model catalog through `@ai-matrx/agents/models` (records / readModelCatalog, subscribed to
|
|
5
|
+
* `catalog_changed` in models/catalog/changes.ts) and computes the name with `modelDisplayLabel`
|
|
6
|
+
* (models/label.ts), so an admin edit reaches every open app within seconds.
|
|
7
|
+
*
|
|
8
|
+
* Fails when a file outside apps/shared/matrx-agents/models/:
|
|
9
|
+
* R1 reads ai.model_definition / model_public / model_offering / model_config / model_variant /
|
|
10
|
+
* model_presentation (`.from("…")`) or the catalog RPCs (`admin_model_catalog`, `resolve_model_config`);
|
|
11
|
+
* R2 computes a model label by hand (`x.common_name || x.name`, `?? …name`) instead of `modelDisplayLabel`.
|
|
12
|
+
* ALLOWED lists the few readers that keep their own read on purpose, each with why and how it follows the
|
|
13
|
+
* event. Test files are fixtures and are not scanned.
|
|
14
|
+
*
|
|
15
|
+
* node apps/shared/matrx-agents/scripts/check-model-catalog-reads.mjs [--root <repo>] [--extra <dir>]... [--self-test]
|
|
16
|
+
* `--extra` adds a scan root (the red proof plants an untracked scratch file there). `--scan` replaces the
|
|
17
|
+
* default roots (Matrx 2: `node node_modules/@ai-matrx/agents/scripts/check-model-catalog-reads.mjs --root . --scan app`).
|
|
18
|
+
*/
|
|
19
|
+
import { mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
import { join, relative, resolve } from "node:path";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
|
+
|
|
24
|
+
const here = fileURLToPath(new URL(".", import.meta.url));
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
const flag = (name) => {
|
|
27
|
+
const out = [];
|
|
28
|
+
for (let i = 0; i < args.length; i++) if (args[i] === name && args[i + 1]) out.push(args[++i]);
|
|
29
|
+
return out;
|
|
30
|
+
};
|
|
31
|
+
const ROOT = resolve(flag("--root")[0] ?? join(here, "../../../.."));
|
|
32
|
+
// `--scan <dir>` replaces the aidream roots: Matrx 2 runs this file from its installed
|
|
33
|
+
// node_modules/@ai-matrx/agents with `--root . --scan app` (one guard, two repos).
|
|
34
|
+
const ROOTS = [...(flag("--scan").length ? flag("--scan") : ["apps/shared", "apps/matrx-frontend"]), ...flag("--extra")];
|
|
35
|
+
|
|
36
|
+
/** The reader module: the only place the catalog relations are read and the label is computed. */
|
|
37
|
+
const HOME = "apps/shared/matrx-agents/models/";
|
|
38
|
+
|
|
39
|
+
/** Files that keep their own catalog read, on purpose. Each says why and how it follows catalog_changed. */
|
|
40
|
+
export const ALLOWED = {
|
|
41
|
+
// The admin model EDITOR: reads and writes every column of the rows it edits. Every container that shows
|
|
42
|
+
// its rows re-reads on the event (useOnModelCatalogChanged / useModelCatalogRevision).
|
|
43
|
+
"apps/matrx-frontend/features/ai-models/service.ts": "admin editor CRUD; containers follow catalog_changed",
|
|
44
|
+
"apps/matrx-frontend/features/ai-models/discovery/api.ts": "admin discovery CRUD; UnifiedModelKindTable follows catalog_changed",
|
|
45
|
+
"apps/matrx-frontend/features/ai-models/translation/data.ts": "admin translation grid; settings-translation keys its query on the revision",
|
|
46
|
+
// cx-dashboard names models on the server per request; CxDashboardLayoutClient router.refresh()es on the event.
|
|
47
|
+
"apps/matrx-frontend/features/cx-dashboard/service.ts": "server-rendered admin page; layout refreshes on catalog_changed",
|
|
48
|
+
// The public JSON API for external callers (anon key, CDN cached by design) — not a screen.
|
|
49
|
+
"apps/matrx-frontend/app/api/ai-models/route.ts": "public CDN-cached JSON API for external callers, not a screen",
|
|
50
|
+
// A security probe that reads the relations to prove their grants.
|
|
51
|
+
"apps/matrx-frontend/scripts/check-rls-on.ts": "RLS probe, not a reader",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const SKIP_DIR = new Set(["node_modules", "dist", ".next", ".turbo", "coverage", "generated", "__tests__", "__fixtures__"]);
|
|
55
|
+
const isTest = (p) => /\.(test|spec)\.[cm]?[jt]sx?$/.test(p) || /\.d\.ts$/.test(p) || /\.generated\./.test(p);
|
|
56
|
+
|
|
57
|
+
const R1 = /\.from\(\s*["'`](model_definition|model_public|model_offering|model_config|model_variant|model_presentation)["'`]|\.rpc\(\s*["'`](admin_model_catalog|resolve_model_config)["'`]/g;
|
|
58
|
+
const R2 = /\bcommon_name\s*(\|\||\?\?)\s*[\w$?.]*\bname\b/g;
|
|
59
|
+
|
|
60
|
+
function* walk(dir) {
|
|
61
|
+
let names;
|
|
62
|
+
try {
|
|
63
|
+
names = readdirSync(dir);
|
|
64
|
+
} catch {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
for (const n of names) {
|
|
68
|
+
if (SKIP_DIR.has(n)) continue;
|
|
69
|
+
const p = join(dir, n);
|
|
70
|
+
const st = statSync(p);
|
|
71
|
+
if (st.isDirectory()) yield* walk(p);
|
|
72
|
+
else if (/\.(ts|tsx|mts|cts|js|jsx|mjs)$/.test(n) && !isTest(n)) yield p;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const lineOf = (src, index) => src.slice(0, index).split("\n").length;
|
|
77
|
+
function scan(roots) {
|
|
78
|
+
const findings = [];
|
|
79
|
+
for (const r of roots) {
|
|
80
|
+
for (const file of walk(resolve(ROOT, r))) {
|
|
81
|
+
const rel = relative(ROOT, file).split("\\").join("/");
|
|
82
|
+
if (rel.startsWith(HOME) || rel === "apps/shared/matrx-agents/scripts/check-model-catalog-reads.mjs") continue;
|
|
83
|
+
if (ALLOWED[rel]) continue;
|
|
84
|
+
const src = readFileSync(file, "utf8");
|
|
85
|
+
for (const [rule, re, what] of [
|
|
86
|
+
["R1", R1, "reads the model catalog directly — use @ai-matrx/agents/models (records / chat's useModelLabel / readModelIdentities)"],
|
|
87
|
+
["R2", R2, "computes a model label by hand — use modelDisplayLabel(row) from @ai-matrx/agents/models"],
|
|
88
|
+
]) {
|
|
89
|
+
re.lastIndex = 0;
|
|
90
|
+
for (let m; (m = re.exec(src)); ) findings.push(`${rel}:${lineOf(src, m.index)} ${rule} ${what} [${m[0].replace(/\s+/g, " ")}]`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return findings;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (args.includes("--self-test")) {
|
|
98
|
+
// Falsifiability: a planted direct read and a hand-made label must each fail; the lawful form must pass.
|
|
99
|
+
const dir = mkdtempSync(join(tmpdir(), "model-catalog-reads-"));
|
|
100
|
+
try {
|
|
101
|
+
writeFileSync(join(dir, "bad.ts"), 'export const r = (db: any) => db.schema("ai").from("model_definition").select("id");\nexport const l = (m: any) =>\n m?.common_name ??\n m?.name;\n');
|
|
102
|
+
writeFileSync(join(dir, "good.ts"), 'import { modelDisplayLabel } from "@ai-matrx/agents/models";\nexport const l = (m: any) => modelDisplayLabel(m) ?? m.common_name ?? null;\n');
|
|
103
|
+
const got = scan([dir]);
|
|
104
|
+
const ok = got.length === 2 && got.some((f) => f.includes("bad.ts:1 R1")) && got.some((f) => f.includes("bad.ts:3 R2"));
|
|
105
|
+
if (!ok) {
|
|
106
|
+
console.error(`check-model-catalog-reads --self-test: FAILED — expected R1+R2 on bad.ts only, got:\n ${got.join("\n ") || "(nothing)"}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
console.log("check-model-catalog-reads --self-test: OK — a planted read and a hand-made label both go red; modelDisplayLabel passes.");
|
|
110
|
+
process.exit(0);
|
|
111
|
+
} finally {
|
|
112
|
+
rmSync(dir, { recursive: true, force: true });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const findings = scan(ROOTS);
|
|
117
|
+
if (findings.length) {
|
|
118
|
+
console.error(`check-model-catalog-reads: ${findings.length} finding(s) — every model name and list goes through @ai-matrx/agents/models so catalog_changed reaches it:`);
|
|
119
|
+
for (const f of findings) console.error(` ${f}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
console.log(`check-model-catalog-reads: OK — no model catalog read or hand-made model label outside ${HOME} (${Object.keys(ALLOWED).length} allowed readers).`);
|