@ai-matrx/agents 0.7.0 → 0.8.0
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 +130 -0
- package/dist/catalog/index.cjs +69 -3
- package/dist/catalog/index.cjs.map +1 -1
- package/dist/catalog/index.d.cts +114 -3
- package/dist/catalog/index.d.ts +114 -3
- package/dist/catalog/index.js +69 -3
- package/dist/catalog/index.js.map +1 -1
- package/dist/catalog/react/index.cjs +74 -7
- package/dist/catalog/react/index.cjs.map +1 -1
- package/dist/catalog/react/index.d.cts +120 -8
- package/dist/catalog/react/index.d.ts +120 -8
- package/dist/catalog/react/index.js +74 -7
- package/dist/catalog/react/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,135 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0 — 2026-09-08
|
|
4
|
+
|
|
5
|
+
**THE ORCHESTRA BADGE IS BACK, in every client at once.**
|
|
6
|
+
|
|
7
|
+
An **Orchestra** is a conductor agent that delegates to member agents at run
|
|
8
|
+
time: choosing one for a step, a chat or a workflow node runs the whole
|
|
9
|
+
ensemble. The Workflow Studio's old hand-rolled picker showed that — a
|
|
10
|
+
conductor marker, the member count, the run mode, the tagline and the member
|
|
11
|
+
titles — because the Python route `GET /agents` stamped it from
|
|
12
|
+
`platform.associations`.
|
|
13
|
+
|
|
14
|
+
Every picker on the platform now reads the RPC `agx_get_list_full`, and that
|
|
15
|
+
RPC had no such column. So the day the pickers unified, EVERY client lost the
|
|
16
|
+
badge at once, and an Orchestra became indistinguishable from an ordinary
|
|
17
|
+
agent in the one place where the difference decides what happens next.
|
|
18
|
+
|
|
19
|
+
The fix is in the read every client shares, not in any client.
|
|
20
|
+
|
|
21
|
+
### Consumer action (C28)
|
|
22
|
+
|
|
23
|
+
**None for the badge — it appears by itself.** The column is served by the
|
|
24
|
+
platform RPC, this package parses it, and the picker renders it. Update to
|
|
25
|
+
`0.8.0` (hosts on `"latest"` get it on their next install) and any Orchestra in
|
|
26
|
+
the list wears its marker.
|
|
27
|
+
|
|
28
|
+
**One import path changed, compatibly:** `SORT_OPTIONS` now lives in the pure
|
|
29
|
+
`@ai-matrx/agents/catalog` entry. `@ai-matrx/agents/catalog/react` re-exports
|
|
30
|
+
it unchanged, so nothing breaks — but a server-reachable module that only wants
|
|
31
|
+
the five sort labels should import it from `./catalog` and stop dragging the
|
|
32
|
+
`"use client"` bundle into a server graph.
|
|
33
|
+
|
|
34
|
+
**Rendering your own summary of a chosen agent?** Use the exported `Music4Icon`
|
|
35
|
+
and `orchestraLabel` / `orchestraDelegatesLine` / `orchestraDepthLine` rather
|
|
36
|
+
than writing a second vocabulary for the same badge.
|
|
37
|
+
|
|
38
|
+
### What is new
|
|
39
|
+
|
|
40
|
+
- **`AgentSummary.orchestra: OrchestraBadge | null`** — strict, on every row.
|
|
41
|
+
`{ mode, tagline, depthBudget, memberCount, memberTitles }`, camelCased from
|
|
42
|
+
the new nullable `orchestra jsonb` column.
|
|
43
|
+
- **`AgentRow`** carries the conductor marker: the Orchestra glyph plus the
|
|
44
|
+
member count, titled with the delegates line. Absent on an ordinary agent —
|
|
45
|
+
never a dead or empty chip.
|
|
46
|
+
- **`AgentDetailCard`** leads with the Orchestra block — "Orchestra · supervisor
|
|
47
|
+
· 4 members", the tagline, "Delegates to: …" (collapsing past three into
|
|
48
|
+
"+N more") and the declared delegation depth. It sits ABOVE the description
|
|
49
|
+
because it changes what selecting this agent MEANS.
|
|
50
|
+
- **`orchestraLabel` / `orchestraModeLabel` / `orchestraDelegatesLine` /
|
|
51
|
+
`orchestraDepthLine`** — the Creator words, ported verbatim from the studio
|
|
52
|
+
picker, exported from both entries.
|
|
53
|
+
- **`Music4Icon`** — inlined SVG (C19), the same glyph the studio used.
|
|
54
|
+
- **`assertAgentCatalogSchema`** reports `orchestraColumn: boolean | null`: a
|
|
55
|
+
database that predates the column is VISIBLE, not silently badge-less.
|
|
56
|
+
|
|
57
|
+
### The platform side
|
|
58
|
+
|
|
59
|
+
Migration `matrx-frontend/migrations/agx_list_orchestra_badge_column.sql` adds
|
|
60
|
+
a trailing nullable `orchestra jsonb` to `agx_get_list`, `agx_get_list_full`
|
|
61
|
+
and `agx_search`, built by ONE new helper `public.agx_orchestra_badges()` —
|
|
62
|
+
the conductor self-edge (`source_type='agent'`, `source_id=target_id`,
|
|
63
|
+
`role='orchestra'`) for mode/tagline/depth_budget, its `role='member'` edges
|
|
64
|
+
for the count and the non-empty labels. Membership, `ORDER BY`, `SECURITY
|
|
65
|
+
DEFINER`, `search_path` and every grant are unchanged; the page is cut BEFORE
|
|
66
|
+
the badge join so `LIMIT`/`OFFSET` still bite on exactly the rows they did.
|
|
67
|
+
|
|
68
|
+
### Deliberate deltas, said out loud
|
|
69
|
+
|
|
70
|
+
- **`depthBudget` is what the composition DECLARED, and `null` when it declared
|
|
71
|
+
nothing.** The old studio badge printed the EFFECTIVE ceiling, defaulting to
|
|
72
|
+
the platform constant `PROJECTED_AGENT_MAX_RECURSION_DEPTH`. That constant
|
|
73
|
+
lives in the Python that enforces it; copying it into SQL and again into
|
|
74
|
+
TypeScript would be three places to drift. So a composition with no declared
|
|
75
|
+
budget shows no depth line at all, rather than a number nobody wrote.
|
|
76
|
+
- **The literal token "orchestra" is not searchable.** The studio's picker let
|
|
77
|
+
an author type the word and see only ensembles. The canonical scorer this
|
|
78
|
+
package ports (from matrx-frontend) never had that, and adding it would
|
|
79
|
+
change the 295-case parity matrix. Not ported; recorded here rather than lost.
|
|
80
|
+
|
|
81
|
+
### Proof
|
|
82
|
+
|
|
83
|
+
`catalog/__tests__/orchestra.test.ts` layers conductor rows onto the SAME
|
|
84
|
+
66-row parity fixture and asserts the badge survives the parser, every sort,
|
|
85
|
+
the user/builtin split and a real filtered-search selector, pinned to literal
|
|
86
|
+
expectations (comparing output to input passes trivially when both are null —
|
|
87
|
+
which is the regression it exists to catch). `catalog/react/picker.test.tsx`
|
|
88
|
+
renders a conductor beside an ordinary agent and asserts the marker, its count,
|
|
89
|
+
its hover text and its ABSENCE on the ordinary row. Both proven RED on a
|
|
90
|
+
deliberate perturbation and GREEN restored.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## 0.7.1 — 2026-09-08
|
|
94
|
+
|
|
95
|
+
**0.7.0's two catalog entries did not typecheck together.** `./catalog` and
|
|
96
|
+
`./catalog/react` are emitted by two separate tsup passes (only the react group
|
|
97
|
+
carries the `"use client"` banner), so each `.d.ts` holds its own copy of the
|
|
98
|
+
shared declarations. `AgentId` was branded with a `unique symbol`, which is
|
|
99
|
+
NOMINAL — the two copies were unrelated types, and the wiring the 0.7.0
|
|
100
|
+
`Consumer action` prescribes:
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<AgentCatalogProvider catalog={createAgentCatalog({ client, identity })}>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
failed with *"Two different types with this name exist, but they are
|
|
107
|
+
unrelated"* in every consumer. Every runtime canary was green; only a `tsc` in
|
|
108
|
+
a real host caught it (matrx-extend, on its first compile of the adoption).
|
|
109
|
+
|
|
110
|
+
### Consumer action (C28)
|
|
111
|
+
|
|
112
|
+
Update to 0.7.1 and the documented wiring compiles. No source change is needed
|
|
113
|
+
in a host — if you worked around this with a cast, DELETE the cast.
|
|
114
|
+
|
|
115
|
+
### The fix, and the guard
|
|
116
|
+
|
|
117
|
+
- `AgentId` is now branded with a string literal
|
|
118
|
+
(`string & { readonly __agentId: "ai-matrx.agent-id" }`). A literal brand is
|
|
119
|
+
structural, so the two emitted copies unify; a bare `string` still cannot be
|
|
120
|
+
passed where an `AgentId` is required, so the branding is not weakened. Any
|
|
121
|
+
future brand in this package follows the same rule — the reason is recorded
|
|
122
|
+
at the declaration.
|
|
123
|
+
- `scripts/verify-tarball.mjs` grew THE TWO-ENTRY TYPE-IDENTITY CANARY: it
|
|
124
|
+
installs the packed tarball into a scratch consumer with `strict`,
|
|
125
|
+
`exactOptionalPropertyTypes`, `noUncheckedIndexedAccess` and
|
|
126
|
+
`skipLibCheck: false`, then compiles the real provider wiring plus reads that
|
|
127
|
+
cross the seam in both directions. Proven RED against the 0.7.0 brand
|
|
128
|
+
(the same "two different types" error, from the packed types) and GREEN
|
|
129
|
+
after the fix. It runs inside `pnpm check:package`, which the publish
|
|
130
|
+
workflow gates on.
|
|
131
|
+
|
|
132
|
+
|
|
3
133
|
## 0.7.0 — 2026-09-08
|
|
4
134
|
|
|
5
135
|
**THE ONE AGENT PICKER moves into the package.** Two new entries — `./catalog`
|
package/dist/catalog/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ __export(catalog_exports, {
|
|
|
30
30
|
DEFAULT_AGENT_CATALOG_LABELS: () => DEFAULT_AGENT_CATALOG_LABELS,
|
|
31
31
|
DEFAULT_AGENT_CONSUMER_STATE: () => DEFAULT_AGENT_CONSUMER_STATE,
|
|
32
32
|
MandateDefaultRowError: () => MandateDefaultRowError,
|
|
33
|
+
SORT_OPTIONS: () => SORT_OPTIONS,
|
|
33
34
|
_resetCatalogGlobalState: () => _resetCatalogGlobalState,
|
|
34
35
|
agentConsumerHasActiveFilters: () => agentConsumerHasActiveFilters,
|
|
35
36
|
agentListEmptyLabel: () => agentListEmptyLabel,
|
|
@@ -61,6 +62,10 @@ __export(catalog_exports, {
|
|
|
61
62
|
memoize1: () => memoize1,
|
|
62
63
|
memoize2: () => memoize2,
|
|
63
64
|
memoize3: () => memoize3,
|
|
65
|
+
orchestraDelegatesLine: () => orchestraDelegatesLine,
|
|
66
|
+
orchestraDepthLine: () => orchestraDepthLine,
|
|
67
|
+
orchestraLabel: () => orchestraLabel,
|
|
68
|
+
orchestraModeLabel: () => orchestraModeLabel,
|
|
64
69
|
parseMandateResolution: () => parseMandateResolution,
|
|
65
70
|
readAgentCatalogRows: () => readAgentCatalogRows,
|
|
66
71
|
readCachedDefaultRow: () => readCachedDefaultRow,
|
|
@@ -96,6 +101,13 @@ function mandateAgentId(mandateKey) {
|
|
|
96
101
|
function isMandateAgentId(id) {
|
|
97
102
|
return id.startsWith("mandate:");
|
|
98
103
|
}
|
|
104
|
+
var SORT_OPTIONS = [
|
|
105
|
+
{ value: "updated-desc", label: "Recent" },
|
|
106
|
+
{ value: "created-desc", label: "Created" },
|
|
107
|
+
{ value: "name-asc", label: "A \u2192 Z" },
|
|
108
|
+
{ value: "name-desc", label: "Z \u2192 A" },
|
|
109
|
+
{ value: "category-asc", label: "Category" }
|
|
110
|
+
];
|
|
99
111
|
var AGENT_NONE_SENTINEL = "__none__";
|
|
100
112
|
var DEFAULT_AGENT_CONSUMER_STATE = {
|
|
101
113
|
tab: "mine",
|
|
@@ -165,6 +177,18 @@ function stringArray(value) {
|
|
|
165
177
|
function agentType(value) {
|
|
166
178
|
return value === "builtin" ? "builtin" : "user";
|
|
167
179
|
}
|
|
180
|
+
function orchestraBadge(value) {
|
|
181
|
+
if (!isRecord(value)) return null;
|
|
182
|
+
const depth = value["depth_budget"];
|
|
183
|
+
const count = value["member_count"];
|
|
184
|
+
return {
|
|
185
|
+
mode: str(value["mode"]) ?? "supervisor",
|
|
186
|
+
tagline: str(value["tagline"]),
|
|
187
|
+
depthBudget: typeof depth === "number" && Number.isFinite(depth) && depth > 0 ? depth : null,
|
|
188
|
+
memberCount: typeof count === "number" && Number.isFinite(count) && count >= 0 ? count : 0,
|
|
189
|
+
memberTitles: stringArray(value["member_titles"])
|
|
190
|
+
};
|
|
191
|
+
}
|
|
168
192
|
function toAgentSummary(raw) {
|
|
169
193
|
if (!isRecord(raw)) return null;
|
|
170
194
|
const id = str(raw["id"]);
|
|
@@ -188,7 +212,8 @@ function toAgentSummary(raw) {
|
|
|
188
212
|
updatedAt: str(raw["updated_at"]),
|
|
189
213
|
isOwner: boolOrNull(raw["is_owner"]),
|
|
190
214
|
accessLevel: str(raw["access_level"]),
|
|
191
|
-
sharedByEmail: str(raw["shared_by_email"])
|
|
215
|
+
sharedByEmail: str(raw["shared_by_email"]),
|
|
216
|
+
orchestra: orchestraBadge(raw["orchestra"])
|
|
192
217
|
};
|
|
193
218
|
}
|
|
194
219
|
|
|
@@ -426,6 +451,9 @@ function defaultRowFromResolution(resolved) {
|
|
|
426
451
|
isOwner: null,
|
|
427
452
|
accessLevel: null,
|
|
428
453
|
sharedByEmail: null,
|
|
454
|
+
// A mandate default row is a live reference to whichever agent the mandate
|
|
455
|
+
// resolves to; the badge belongs to the RESOLVED agent's own catalog row.
|
|
456
|
+
orchestra: null,
|
|
429
457
|
mandateKey: resolved.mandateKey
|
|
430
458
|
};
|
|
431
459
|
}
|
|
@@ -530,10 +558,14 @@ async function assertAgentCatalogSchema(client, options = {}) {
|
|
|
530
558
|
const missing = [];
|
|
531
559
|
const unreachable = [];
|
|
532
560
|
const answered = [];
|
|
561
|
+
let orchestraColumn = null;
|
|
533
562
|
for (const fn of names) {
|
|
534
563
|
const args = DEMANDED_RPCS[fn] ?? {};
|
|
535
564
|
try {
|
|
536
|
-
const { error } = await client.rpc(fn, args);
|
|
565
|
+
const { data, error } = await client.rpc(fn, args);
|
|
566
|
+
if (fn === "agx_get_list_full" && error == null) {
|
|
567
|
+
orchestraColumn = readsOrchestraColumn(data);
|
|
568
|
+
}
|
|
537
569
|
if (error == null) answered.push(fn);
|
|
538
570
|
else if (isMissingFunctionError(error)) missing.push(fn);
|
|
539
571
|
else answered.push(fn);
|
|
@@ -573,7 +605,8 @@ async function assertAgentCatalogSchema(client, options = {}) {
|
|
|
573
605
|
missing,
|
|
574
606
|
unreachable,
|
|
575
607
|
answered,
|
|
576
|
-
favoriteWritable
|
|
608
|
+
favoriteWritable,
|
|
609
|
+
orchestraColumn
|
|
577
610
|
};
|
|
578
611
|
if (!ok && throwOnViolation) {
|
|
579
612
|
const parts = [];
|
|
@@ -591,6 +624,12 @@ async function assertAgentCatalogSchema(client, options = {}) {
|
|
|
591
624
|
}
|
|
592
625
|
return report;
|
|
593
626
|
}
|
|
627
|
+
function readsOrchestraColumn(data) {
|
|
628
|
+
if (!Array.isArray(data) || data.length === 0) return null;
|
|
629
|
+
return data.some(
|
|
630
|
+
(row) => typeof row === "object" && row !== null && Object.prototype.hasOwnProperty.call(row, "orchestra")
|
|
631
|
+
);
|
|
632
|
+
}
|
|
594
633
|
function isPermissionRefusal(error) {
|
|
595
634
|
if (!error) return false;
|
|
596
635
|
const code = error.code;
|
|
@@ -964,6 +1003,33 @@ function getRegisteredAgentCatalog(catalogId = "default") {
|
|
|
964
1003
|
return found;
|
|
965
1004
|
}
|
|
966
1005
|
|
|
1006
|
+
// catalog/orchestra.ts
|
|
1007
|
+
var ORCHESTRA_MODE_LABEL = {
|
|
1008
|
+
supervisor: "supervisor",
|
|
1009
|
+
sequential: "in order",
|
|
1010
|
+
parallel: "together",
|
|
1011
|
+
dag: "custom flow"
|
|
1012
|
+
};
|
|
1013
|
+
function orchestraModeLabel(mode) {
|
|
1014
|
+
return ORCHESTRA_MODE_LABEL[mode] ?? mode;
|
|
1015
|
+
}
|
|
1016
|
+
function orchestraLabel(badge) {
|
|
1017
|
+
const n = badge.memberCount;
|
|
1018
|
+
return `Orchestra \xB7 ${orchestraModeLabel(badge.mode)} \xB7 ${n} ${n === 1 ? "member" : "members"}`;
|
|
1019
|
+
}
|
|
1020
|
+
function orchestraDepthLine(badge) {
|
|
1021
|
+
const n = badge.depthBudget;
|
|
1022
|
+
if (typeof n !== "number" || n <= 0) return null;
|
|
1023
|
+
return `Team members can call helpers up to ${n} ${n === 1 ? "level" : "levels"} deep`;
|
|
1024
|
+
}
|
|
1025
|
+
function orchestraDelegatesLine(badge, limit = 3) {
|
|
1026
|
+
const titles = badge.memberTitles;
|
|
1027
|
+
if (titles.length === 0) return null;
|
|
1028
|
+
const shown = titles.slice(0, limit);
|
|
1029
|
+
const extra = titles.length - shown.length;
|
|
1030
|
+
return `Delegates to: ${shown.join(", ")}${extra > 0 ? ` +${extra} more` : ""}`;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
967
1033
|
// catalog/labels.ts
|
|
968
1034
|
function agentListEmptyLabel(tab, labels) {
|
|
969
1035
|
switch (tab) {
|