@ai-matrx/agents 0.7.1 → 0.9.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 +225 -0
- package/dist/catalog/index.cjs +119 -6
- package/dist/catalog/index.cjs.map +1 -1
- package/dist/catalog/index.d.cts +17 -521
- package/dist/catalog/index.d.ts +17 -521
- package/dist/catalog/index.js +119 -6
- package/dist/catalog/index.js.map +1 -1
- package/dist/catalog/native/index.cjs +2806 -0
- package/dist/catalog/native/index.cjs.map +1 -0
- package/dist/catalog/native/index.d.cts +655 -0
- package/dist/catalog/native/index.d.ts +655 -0
- package/dist/catalog/native/index.js +2802 -0
- package/dist/catalog/native/index.js.map +1 -0
- package/dist/catalog/react/index.cjs +527 -384
- package/dist/catalog/react/index.cjs.map +1 -1
- package/dist/catalog/react/index.d.cts +171 -32
- package/dist/catalog/react/index.d.ts +171 -32
- package/dist/catalog/react/index.js +414 -271
- package/dist/catalog/react/index.js.map +1 -1
- package/dist/orchestra-BWJM2MWP.d.cts +643 -0
- package/dist/orchestra-BWJM2MWP.d.ts +643 -0
- package/package.json +22 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,230 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0 — 2026-09-08
|
|
4
|
+
|
|
5
|
+
**THE ONE PICKER NOW RENDERS ON REACT NATIVE — `@ai-matrx/agents/catalog/native`.**
|
|
6
|
+
|
|
7
|
+
Matrx Mobile (Expo 54 / RN 0.81) was the last client still hand-rolling its
|
|
8
|
+
agent list. Its sheet showed four HARDCODED agents plus a Supabase read of a
|
|
9
|
+
`prompts` table that **does not exist in the platform database** — a read whose
|
|
10
|
+
PostgREST error was swallowed into an empty array, so the list silently never
|
|
11
|
+
had anything but the four constants. Three of those four constants named the
|
|
12
|
+
WRONG agent: the ids behind "General Chat", "Deep Research" and "Code Helper"
|
|
13
|
+
resolve in `agent.definition` to *Agent Ledger*, *Knowledge Search Test* and
|
|
14
|
+
*Balanced News Analysis*. A label lying on screen, with nothing anywhere that
|
|
15
|
+
could notice — the same class the mandate default row was built to end.
|
|
16
|
+
|
|
17
|
+
React Native could not adopt the picker before this release for two concrete
|
|
18
|
+
reasons, and both are fixed here rather than worked around in the app.
|
|
19
|
+
|
|
20
|
+
### Consumer action (C28)
|
|
21
|
+
|
|
22
|
+
**Web hosts: none.** `@ai-matrx/agents/catalog` and `/catalog/react` keep every
|
|
23
|
+
export, every name and every behaviour. The provider, the store subscription
|
|
24
|
+
and the two list hooks moved to `catalog/shared/*` so both platforms run one
|
|
25
|
+
implementation; `catalog/react` re-exports all of them unchanged, and the
|
|
26
|
+
`"use client"` banner is still stamped on the react build and still absent from
|
|
27
|
+
the pure ones (the tarball canary now judges eight artefacts instead of four).
|
|
28
|
+
|
|
29
|
+
**React Native hosts:**
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
33
|
+
import { createAgentCatalog } from "@ai-matrx/agents/catalog";
|
|
34
|
+
import { AgentCatalogProvider, AgentListSheet } from "@ai-matrx/agents/catalog/native";
|
|
35
|
+
|
|
36
|
+
const catalog = createAgentCatalog({
|
|
37
|
+
client: supabase,
|
|
38
|
+
identity: { requireUserId: () => requireUserId() },
|
|
39
|
+
storage: AsyncStorage, // NEW — the async storage seam
|
|
40
|
+
transport, // only when a picker passes defaultMandateKey
|
|
41
|
+
errorSink: reportToDiagnostics,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
<AgentCatalogProvider catalog={catalog} navigate={(href) => router.push(href)}>
|
|
45
|
+
<AgentListSheet consumerId="chat" onSelect={setAgentId} activeAgentId={agentId} />
|
|
46
|
+
</AgentCatalogProvider>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- `react-native` and `react-native-svg` are OPTIONAL peers. Install
|
|
50
|
+
`react-native-svg` (`npx expo install react-native-svg`) for the real glyphs;
|
|
51
|
+
without it the icons fall back to text glyphs and **say so once** through the
|
|
52
|
+
`errorSink`, with the remedy. Nothing goes blank and nothing goes quiet.
|
|
53
|
+
- Bind `navigate` if your rows should be able to open an agent. Unbound, the
|
|
54
|
+
default is a no-op that REPORTS through the `errorSink` — never a control
|
|
55
|
+
that silently does nothing.
|
|
56
|
+
- `@ai-matrx/agents/catalog/styles.css` is a WEB artefact; the native entry
|
|
57
|
+
needs no stylesheet.
|
|
58
|
+
|
|
59
|
+
### What is new
|
|
60
|
+
|
|
61
|
+
- **`@ai-matrx/agents/catalog/native`** — the picker in React Native
|
|
62
|
+
primitives over the SAME headless kernel and the SAME hooks:
|
|
63
|
+
`AgentCatalogProvider`, `AgentListSheet` (the body of a bottom sheet — the
|
|
64
|
+
host keeps its own sheet chrome, so no gesture library is forced on anyone),
|
|
65
|
+
`AgentListInlinePicker`, and every part beneath them (`AgentListContent`,
|
|
66
|
+
`AgentListTabs`, `AgentFilterBar`, `AgentRow`, `AgentDetailCard`,
|
|
67
|
+
`AgentSubView`, `FavoriteAgentButton`, the five primitives, `AgentIcon`).
|
|
68
|
+
The shells take the web shells' prop surface name for name: `onSelect`,
|
|
69
|
+
`activeAgentId`, `consumerId`, `initialTab`, `includeSystemInAll`,
|
|
70
|
+
`visibleTabs`, `systemTabLabel`, `showPinnedAgent`, `excludeAgentIds`,
|
|
71
|
+
`defaultMandateKey`, `compact`.
|
|
72
|
+
- **The async storage seam** — `AgentCatalogAsyncStorage` /
|
|
73
|
+
`AgentCatalogStorageLike`, plus `readCachedDefaultRowAsync` and
|
|
74
|
+
`writeCachedDefaultRowAsync`. `createAgentCatalog({ storage })` now accepts an
|
|
75
|
+
`AsyncStorage`-shaped store verbatim. React Native has no `localStorage`, so
|
|
76
|
+
before this a native host could only hand the catalog a store that answered
|
|
77
|
+
`null` synchronously (a lie) or lose the default row's first paint entirely.
|
|
78
|
+
**The web path is unchanged**: `AgentCatalogStorage` is still synchronous,
|
|
79
|
+
`readCachedDefaultRow`/`writeCachedDefaultRow` still have their synchronous
|
|
80
|
+
signatures, `defaultStorage()` still prefers `localStorage`, and the async
|
|
81
|
+
helpers over a synchronous store produce byte-identical cache payloads
|
|
82
|
+
(proven in `catalog/__tests__/storage-seam.test.ts`).
|
|
83
|
+
- **`AgentCatalogNativeTheme`** (C26 on a platform with no CSS) — the native
|
|
84
|
+
twin of the `--matrx-agent-*` sheet: every token ships a DEFAULT VALUE so the
|
|
85
|
+
picker renders correctly unthemed, and a host re-brands by passing `theme` to
|
|
86
|
+
the provider. `DEFAULT_AGENT_CATALOG_NATIVE_THEME` (dark) and
|
|
87
|
+
`LIGHT_AGENT_CATALOG_NATIVE_THEME` ship; `resolveAgentCatalogNativeTheme`
|
|
88
|
+
merges an override and keeps every value it omits. No native component
|
|
89
|
+
contains a colour literal.
|
|
90
|
+
- **`catalog/shared/icon-paths.ts`** — the glyph geometry, once. The web icons
|
|
91
|
+
now read it too, so a shape can never differ between the two platforms; the
|
|
92
|
+
native entry draws the same paths through `react-native-svg` (C19: still no
|
|
93
|
+
icon-library dependency, on either platform).
|
|
94
|
+
- **`AgentListTabCounts` moved** to `catalog/shared/useAgentListCore` beside the
|
|
95
|
+
code that computes it. `catalog/react/AgentListTabs` re-exports it unchanged.
|
|
96
|
+
|
|
97
|
+
### Behavioural deltas on native, recorded rather than hidden
|
|
98
|
+
|
|
99
|
+
1. **No hover preview.** The web picker's side panel opens on hover; a touch
|
|
100
|
+
screen has no hover, so the detail card is reached by the row's chevron —
|
|
101
|
+
the same three-view stack (`list → detail → sort/categories/tags`) the web
|
|
102
|
+
`AgentListInlinePicker` already used on mobile widths.
|
|
103
|
+
2. **No cmd-click link.** `AgentRow`'s anchor exists on the web so a person can
|
|
104
|
+
open an agent in a new tab. React Native has neither, so the row is an
|
|
105
|
+
ordinary `Pressable`; the `LinkComponent` port still exists and still ships
|
|
106
|
+
a working native default.
|
|
107
|
+
3. **`autoFocusSearch` defaults to `false`** (the web inline picker defaults to
|
|
108
|
+
`true`): a keyboard that opens with the sheet covers the list it was meant
|
|
109
|
+
to filter.
|
|
110
|
+
|
|
111
|
+
### Proof
|
|
112
|
+
|
|
113
|
+
- **The 293-case ordering matrix, re-measured through the RENDERED native
|
|
114
|
+
list** (`catalog/native/__tests__/parity.test.tsx`): every case's ids, in
|
|
115
|
+
order, read back out of the mounted native tree — not out of the selectors a
|
|
116
|
+
second time. Sharing a kernel is an architecture claim; this is the
|
|
117
|
+
measurement. It carries a falsifiability leg.
|
|
118
|
+
- **The tarball canary imports AND requires `./catalog/native`**, checks every
|
|
119
|
+
documented export, proves the theme override merges over the PACKED build,
|
|
120
|
+
proves the icon degradation announces exactly once with its remedy, and
|
|
121
|
+
compiles a native usage (including an `AsyncStorage`-shaped `storage`) against
|
|
122
|
+
the packed types under `exactOptionalPropertyTypes` + `noUncheckedIndexedAccess`.
|
|
123
|
+
- **The banner canary now judges eight artefacts**: the `"use client"` prologue
|
|
124
|
+
must be present on both `catalog/react` builds and ABSENT from all four
|
|
125
|
+
`catalog/index.*` and all four `catalog/native/index.*` files.
|
|
126
|
+
|
|
127
|
+
### Known limits, stated plainly
|
|
128
|
+
|
|
129
|
+
There is still **no React Native canary harness** in this workspace (campaign
|
|
130
|
+
decision C7 schedules it for Phase 2), and the real `react-native` package
|
|
131
|
+
cannot be loaded by vitest — it ships Flow-typed source only Metro parses. The
|
|
132
|
+
native tests therefore render against a minimal RN stand-in
|
|
133
|
+
(`catalog/native/__tests__/react-native.stub.tsx`) that maps RN primitives onto
|
|
134
|
+
DOM nodes. That proves rows, order, labels, counts, handlers and every
|
|
135
|
+
announced degradation. It does **not** prove pixel layout, gestures or Hermes —
|
|
136
|
+
those are proven by the consuming Expo app's own build and device smoke.
|
|
137
|
+
|
|
138
|
+
## 0.8.0 — 2026-09-08
|
|
139
|
+
|
|
140
|
+
**THE ORCHESTRA BADGE IS BACK, in every client at once.**
|
|
141
|
+
|
|
142
|
+
An **Orchestra** is a conductor agent that delegates to member agents at run
|
|
143
|
+
time: choosing one for a step, a chat or a workflow node runs the whole
|
|
144
|
+
ensemble. The Workflow Studio's old hand-rolled picker showed that — a
|
|
145
|
+
conductor marker, the member count, the run mode, the tagline and the member
|
|
146
|
+
titles — because the Python route `GET /agents` stamped it from
|
|
147
|
+
`platform.associations`.
|
|
148
|
+
|
|
149
|
+
Every picker on the platform now reads the RPC `agx_get_list_full`, and that
|
|
150
|
+
RPC had no such column. So the day the pickers unified, EVERY client lost the
|
|
151
|
+
badge at once, and an Orchestra became indistinguishable from an ordinary
|
|
152
|
+
agent in the one place where the difference decides what happens next.
|
|
153
|
+
|
|
154
|
+
The fix is in the read every client shares, not in any client.
|
|
155
|
+
|
|
156
|
+
### Consumer action (C28)
|
|
157
|
+
|
|
158
|
+
**None for the badge — it appears by itself.** The column is served by the
|
|
159
|
+
platform RPC, this package parses it, and the picker renders it. Update to
|
|
160
|
+
`0.8.0` (hosts on `"latest"` get it on their next install) and any Orchestra in
|
|
161
|
+
the list wears its marker.
|
|
162
|
+
|
|
163
|
+
**One import path changed, compatibly:** `SORT_OPTIONS` now lives in the pure
|
|
164
|
+
`@ai-matrx/agents/catalog` entry. `@ai-matrx/agents/catalog/react` re-exports
|
|
165
|
+
it unchanged, so nothing breaks — but a server-reachable module that only wants
|
|
166
|
+
the five sort labels should import it from `./catalog` and stop dragging the
|
|
167
|
+
`"use client"` bundle into a server graph.
|
|
168
|
+
|
|
169
|
+
**Rendering your own summary of a chosen agent?** Use the exported `Music4Icon`
|
|
170
|
+
and `orchestraLabel` / `orchestraDelegatesLine` / `orchestraDepthLine` rather
|
|
171
|
+
than writing a second vocabulary for the same badge.
|
|
172
|
+
|
|
173
|
+
### What is new
|
|
174
|
+
|
|
175
|
+
- **`AgentSummary.orchestra: OrchestraBadge | null`** — strict, on every row.
|
|
176
|
+
`{ mode, tagline, depthBudget, memberCount, memberTitles }`, camelCased from
|
|
177
|
+
the new nullable `orchestra jsonb` column.
|
|
178
|
+
- **`AgentRow`** carries the conductor marker: the Orchestra glyph plus the
|
|
179
|
+
member count, titled with the delegates line. Absent on an ordinary agent —
|
|
180
|
+
never a dead or empty chip.
|
|
181
|
+
- **`AgentDetailCard`** leads with the Orchestra block — "Orchestra · supervisor
|
|
182
|
+
· 4 members", the tagline, "Delegates to: …" (collapsing past three into
|
|
183
|
+
"+N more") and the declared delegation depth. It sits ABOVE the description
|
|
184
|
+
because it changes what selecting this agent MEANS.
|
|
185
|
+
- **`orchestraLabel` / `orchestraModeLabel` / `orchestraDelegatesLine` /
|
|
186
|
+
`orchestraDepthLine`** — the Creator words, ported verbatim from the studio
|
|
187
|
+
picker, exported from both entries.
|
|
188
|
+
- **`Music4Icon`** — inlined SVG (C19), the same glyph the studio used.
|
|
189
|
+
- **`assertAgentCatalogSchema`** reports `orchestraColumn: boolean | null`: a
|
|
190
|
+
database that predates the column is VISIBLE, not silently badge-less.
|
|
191
|
+
|
|
192
|
+
### The platform side
|
|
193
|
+
|
|
194
|
+
Migration `matrx-frontend/migrations/agx_list_orchestra_badge_column.sql` adds
|
|
195
|
+
a trailing nullable `orchestra jsonb` to `agx_get_list`, `agx_get_list_full`
|
|
196
|
+
and `agx_search`, built by ONE new helper `public.agx_orchestra_badges()` —
|
|
197
|
+
the conductor self-edge (`source_type='agent'`, `source_id=target_id`,
|
|
198
|
+
`role='orchestra'`) for mode/tagline/depth_budget, its `role='member'` edges
|
|
199
|
+
for the count and the non-empty labels. Membership, `ORDER BY`, `SECURITY
|
|
200
|
+
DEFINER`, `search_path` and every grant are unchanged; the page is cut BEFORE
|
|
201
|
+
the badge join so `LIMIT`/`OFFSET` still bite on exactly the rows they did.
|
|
202
|
+
|
|
203
|
+
### Deliberate deltas, said out loud
|
|
204
|
+
|
|
205
|
+
- **`depthBudget` is what the composition DECLARED, and `null` when it declared
|
|
206
|
+
nothing.** The old studio badge printed the EFFECTIVE ceiling, defaulting to
|
|
207
|
+
the platform constant `PROJECTED_AGENT_MAX_RECURSION_DEPTH`. That constant
|
|
208
|
+
lives in the Python that enforces it; copying it into SQL and again into
|
|
209
|
+
TypeScript would be three places to drift. So a composition with no declared
|
|
210
|
+
budget shows no depth line at all, rather than a number nobody wrote.
|
|
211
|
+
- **The literal token "orchestra" is not searchable.** The studio's picker let
|
|
212
|
+
an author type the word and see only ensembles. The canonical scorer this
|
|
213
|
+
package ports (from matrx-frontend) never had that, and adding it would
|
|
214
|
+
change the 295-case parity matrix. Not ported; recorded here rather than lost.
|
|
215
|
+
|
|
216
|
+
### Proof
|
|
217
|
+
|
|
218
|
+
`catalog/__tests__/orchestra.test.ts` layers conductor rows onto the SAME
|
|
219
|
+
66-row parity fixture and asserts the badge survives the parser, every sort,
|
|
220
|
+
the user/builtin split and a real filtered-search selector, pinned to literal
|
|
221
|
+
expectations (comparing output to input passes trivially when both are null —
|
|
222
|
+
which is the regression it exists to catch). `catalog/react/picker.test.tsx`
|
|
223
|
+
renders a conductor beside an ordinary agent and asserts the marker, its count,
|
|
224
|
+
its hover text and its ABSENCE on the ordinary row. Both proven RED on a
|
|
225
|
+
deliberate perturbation and GREEN restored.
|
|
226
|
+
|
|
227
|
+
|
|
3
228
|
## 0.7.1 — 2026-09-08
|
|
4
229
|
|
|
5
230
|
**0.7.0's two catalog entries did not typecheck together.** `./catalog` and
|
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,9 +62,14 @@ __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,
|
|
72
|
+
readCachedDefaultRowAsync: () => readCachedDefaultRowAsync,
|
|
67
73
|
readSingleAgentRow: () => readSingleAgentRow,
|
|
68
74
|
resolveDefaultRow: () => resolveDefaultRow,
|
|
69
75
|
searchAgentsOnServer: () => searchAgentsOnServer,
|
|
@@ -82,7 +88,8 @@ __export(catalog_exports, {
|
|
|
82
88
|
sortFilteredAgents: () => sortFilteredAgents,
|
|
83
89
|
toAgentSummary: () => toAgentSummary,
|
|
84
90
|
writeAgentFavorite: () => writeAgentFavorite,
|
|
85
|
-
writeCachedDefaultRow: () => writeCachedDefaultRow
|
|
91
|
+
writeCachedDefaultRow: () => writeCachedDefaultRow,
|
|
92
|
+
writeCachedDefaultRowAsync: () => writeCachedDefaultRowAsync
|
|
86
93
|
});
|
|
87
94
|
module.exports = __toCommonJS(catalog_exports);
|
|
88
95
|
|
|
@@ -96,6 +103,13 @@ function mandateAgentId(mandateKey) {
|
|
|
96
103
|
function isMandateAgentId(id) {
|
|
97
104
|
return id.startsWith("mandate:");
|
|
98
105
|
}
|
|
106
|
+
var SORT_OPTIONS = [
|
|
107
|
+
{ value: "updated-desc", label: "Recent" },
|
|
108
|
+
{ value: "created-desc", label: "Created" },
|
|
109
|
+
{ value: "name-asc", label: "A \u2192 Z" },
|
|
110
|
+
{ value: "name-desc", label: "Z \u2192 A" },
|
|
111
|
+
{ value: "category-asc", label: "Category" }
|
|
112
|
+
];
|
|
99
113
|
var AGENT_NONE_SENTINEL = "__none__";
|
|
100
114
|
var DEFAULT_AGENT_CONSUMER_STATE = {
|
|
101
115
|
tab: "mine",
|
|
@@ -165,6 +179,18 @@ function stringArray(value) {
|
|
|
165
179
|
function agentType(value) {
|
|
166
180
|
return value === "builtin" ? "builtin" : "user";
|
|
167
181
|
}
|
|
182
|
+
function orchestraBadge(value) {
|
|
183
|
+
if (!isRecord(value)) return null;
|
|
184
|
+
const depth = value["depth_budget"];
|
|
185
|
+
const count = value["member_count"];
|
|
186
|
+
return {
|
|
187
|
+
mode: str(value["mode"]) ?? "supervisor",
|
|
188
|
+
tagline: str(value["tagline"]),
|
|
189
|
+
depthBudget: typeof depth === "number" && Number.isFinite(depth) && depth > 0 ? depth : null,
|
|
190
|
+
memberCount: typeof count === "number" && Number.isFinite(count) && count >= 0 ? count : 0,
|
|
191
|
+
memberTitles: stringArray(value["member_titles"])
|
|
192
|
+
};
|
|
193
|
+
}
|
|
168
194
|
function toAgentSummary(raw) {
|
|
169
195
|
if (!isRecord(raw)) return null;
|
|
170
196
|
const id = str(raw["id"]);
|
|
@@ -188,7 +214,8 @@ function toAgentSummary(raw) {
|
|
|
188
214
|
updatedAt: str(raw["updated_at"]),
|
|
189
215
|
isOwner: boolOrNull(raw["is_owner"]),
|
|
190
216
|
accessLevel: str(raw["access_level"]),
|
|
191
|
-
sharedByEmail: str(raw["shared_by_email"])
|
|
217
|
+
sharedByEmail: str(raw["shared_by_email"]),
|
|
218
|
+
orchestra: orchestraBadge(raw["orchestra"])
|
|
192
219
|
};
|
|
193
220
|
}
|
|
194
221
|
|
|
@@ -405,6 +432,51 @@ function writeCachedDefaultRow(storage, resolved) {
|
|
|
405
432
|
} catch {
|
|
406
433
|
}
|
|
407
434
|
}
|
|
435
|
+
function parseCachedDefaultRow(mandateKey, raw) {
|
|
436
|
+
try {
|
|
437
|
+
if (!raw) return null;
|
|
438
|
+
const parsed = JSON.parse(raw);
|
|
439
|
+
if (!isRecord2(parsed)) return null;
|
|
440
|
+
const holderId = parsed["holderId"];
|
|
441
|
+
const holderName = parsed["holderName"];
|
|
442
|
+
if (typeof holderId !== "string" || typeof holderName !== "string") {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
const description = parsed["holderDescription"];
|
|
446
|
+
const provenance = parsed["provenance"];
|
|
447
|
+
const at = parsed["at"];
|
|
448
|
+
return {
|
|
449
|
+
mandateKey,
|
|
450
|
+
holderId,
|
|
451
|
+
holderName,
|
|
452
|
+
holderDescription: typeof description === "string" ? description : null,
|
|
453
|
+
provenance: typeof provenance === "string" ? provenance : "system",
|
|
454
|
+
at: typeof at === "number" ? at : 0
|
|
455
|
+
};
|
|
456
|
+
} catch {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async function readCachedDefaultRowAsync(storage, mandateKey) {
|
|
461
|
+
let raw;
|
|
462
|
+
try {
|
|
463
|
+
raw = await storage.getItem(
|
|
464
|
+
CACHE_PREFIX + mandateKey
|
|
465
|
+
);
|
|
466
|
+
} catch {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
return parseCachedDefaultRow(mandateKey, raw);
|
|
470
|
+
}
|
|
471
|
+
async function writeCachedDefaultRowAsync(storage, resolved) {
|
|
472
|
+
try {
|
|
473
|
+
await storage.setItem(
|
|
474
|
+
CACHE_PREFIX + resolved.mandateKey,
|
|
475
|
+
JSON.stringify(resolved)
|
|
476
|
+
);
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
}
|
|
408
480
|
function defaultRowFromResolution(resolved) {
|
|
409
481
|
return {
|
|
410
482
|
id: mandateAgentId(resolved.mandateKey),
|
|
@@ -426,6 +498,9 @@ function defaultRowFromResolution(resolved) {
|
|
|
426
498
|
isOwner: null,
|
|
427
499
|
accessLevel: null,
|
|
428
500
|
sharedByEmail: null,
|
|
501
|
+
// A mandate default row is a live reference to whichever agent the mandate
|
|
502
|
+
// resolves to; the badge belongs to the RESOLVED agent's own catalog row.
|
|
503
|
+
orchestra: null,
|
|
429
504
|
mandateKey: resolved.mandateKey
|
|
430
505
|
};
|
|
431
506
|
}
|
|
@@ -441,7 +516,7 @@ function detectDefaultRowDrift(cached, live) {
|
|
|
441
516
|
}
|
|
442
517
|
async function resolveDefaultRow(args) {
|
|
443
518
|
const { mandateKey, transport, client, storage, lookupRow } = args;
|
|
444
|
-
const cached =
|
|
519
|
+
const cached = await readCachedDefaultRowAsync(storage, mandateKey);
|
|
445
520
|
const verdict = await askTheServer(transport, mandateKey);
|
|
446
521
|
if (verdict.holderType !== "agent") {
|
|
447
522
|
throw new MandateDefaultRowError({
|
|
@@ -478,7 +553,7 @@ async function resolveDefaultRow(args) {
|
|
|
478
553
|
at: Date.now()
|
|
479
554
|
};
|
|
480
555
|
const drift = detectDefaultRowDrift(cached, resolved);
|
|
481
|
-
|
|
556
|
+
await writeCachedDefaultRowAsync(storage, resolved);
|
|
482
557
|
return { resolved, row: defaultRowFromResolution(resolved), drift };
|
|
483
558
|
}
|
|
484
559
|
|
|
@@ -530,10 +605,14 @@ async function assertAgentCatalogSchema(client, options = {}) {
|
|
|
530
605
|
const missing = [];
|
|
531
606
|
const unreachable = [];
|
|
532
607
|
const answered = [];
|
|
608
|
+
let orchestraColumn = null;
|
|
533
609
|
for (const fn of names) {
|
|
534
610
|
const args = DEMANDED_RPCS[fn] ?? {};
|
|
535
611
|
try {
|
|
536
|
-
const { error } = await client.rpc(fn, args);
|
|
612
|
+
const { data, error } = await client.rpc(fn, args);
|
|
613
|
+
if (fn === "agx_get_list_full" && error == null) {
|
|
614
|
+
orchestraColumn = readsOrchestraColumn(data);
|
|
615
|
+
}
|
|
537
616
|
if (error == null) answered.push(fn);
|
|
538
617
|
else if (isMissingFunctionError(error)) missing.push(fn);
|
|
539
618
|
else answered.push(fn);
|
|
@@ -573,7 +652,8 @@ async function assertAgentCatalogSchema(client, options = {}) {
|
|
|
573
652
|
missing,
|
|
574
653
|
unreachable,
|
|
575
654
|
answered,
|
|
576
|
-
favoriteWritable
|
|
655
|
+
favoriteWritable,
|
|
656
|
+
orchestraColumn
|
|
577
657
|
};
|
|
578
658
|
if (!ok && throwOnViolation) {
|
|
579
659
|
const parts = [];
|
|
@@ -591,6 +671,12 @@ async function assertAgentCatalogSchema(client, options = {}) {
|
|
|
591
671
|
}
|
|
592
672
|
return report;
|
|
593
673
|
}
|
|
674
|
+
function readsOrchestraColumn(data) {
|
|
675
|
+
if (!Array.isArray(data) || data.length === 0) return null;
|
|
676
|
+
return data.some(
|
|
677
|
+
(row) => typeof row === "object" && row !== null && Object.prototype.hasOwnProperty.call(row, "orchestra")
|
|
678
|
+
);
|
|
679
|
+
}
|
|
594
680
|
function isPermissionRefusal(error) {
|
|
595
681
|
if (!error) return false;
|
|
596
682
|
const code = error.code;
|
|
@@ -964,6 +1050,33 @@ function getRegisteredAgentCatalog(catalogId = "default") {
|
|
|
964
1050
|
return found;
|
|
965
1051
|
}
|
|
966
1052
|
|
|
1053
|
+
// catalog/orchestra.ts
|
|
1054
|
+
var ORCHESTRA_MODE_LABEL = {
|
|
1055
|
+
supervisor: "supervisor",
|
|
1056
|
+
sequential: "in order",
|
|
1057
|
+
parallel: "together",
|
|
1058
|
+
dag: "custom flow"
|
|
1059
|
+
};
|
|
1060
|
+
function orchestraModeLabel(mode) {
|
|
1061
|
+
return ORCHESTRA_MODE_LABEL[mode] ?? mode;
|
|
1062
|
+
}
|
|
1063
|
+
function orchestraLabel(badge) {
|
|
1064
|
+
const n = badge.memberCount;
|
|
1065
|
+
return `Orchestra \xB7 ${orchestraModeLabel(badge.mode)} \xB7 ${n} ${n === 1 ? "member" : "members"}`;
|
|
1066
|
+
}
|
|
1067
|
+
function orchestraDepthLine(badge) {
|
|
1068
|
+
const n = badge.depthBudget;
|
|
1069
|
+
if (typeof n !== "number" || n <= 0) return null;
|
|
1070
|
+
return `Team members can call helpers up to ${n} ${n === 1 ? "level" : "levels"} deep`;
|
|
1071
|
+
}
|
|
1072
|
+
function orchestraDelegatesLine(badge, limit = 3) {
|
|
1073
|
+
const titles = badge.memberTitles;
|
|
1074
|
+
if (titles.length === 0) return null;
|
|
1075
|
+
const shown = titles.slice(0, limit);
|
|
1076
|
+
const extra = titles.length - shown.length;
|
|
1077
|
+
return `Delegates to: ${shown.join(", ")}${extra > 0 ? ` +${extra} more` : ""}`;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
967
1080
|
// catalog/labels.ts
|
|
968
1081
|
function agentListEmptyLabel(tab, labels) {
|
|
969
1082
|
switch (tab) {
|