@cat-factory/server 0.295.0 → 0.296.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/agents/repoFiles.d.ts +30 -8
  2. package/dist/agents/repoFiles.d.ts.map +1 -1
  3. package/dist/agents/repoFiles.js +43 -14
  4. package/dist/agents/repoFiles.js.map +1 -1
  5. package/dist/agents/repoOrigin.d.ts +73 -0
  6. package/dist/agents/repoOrigin.d.ts.map +1 -0
  7. package/dist/agents/repoOrigin.js +91 -0
  8. package/dist/agents/repoOrigin.js.map +1 -0
  9. package/dist/agents/toolServers.d.ts.map +1 -1
  10. package/dist/agents/toolServers.js +15 -1
  11. package/dist/agents/toolServers.js.map +1 -1
  12. package/dist/app.d.ts.map +1 -1
  13. package/dist/app.js +10 -0
  14. package/dist/app.js.map +1 -1
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/modules/agentKinds/AgentKindsController.d.ts +46 -0
  20. package/dist/modules/agentKinds/AgentKindsController.d.ts.map +1 -0
  21. package/dist/modules/agentKinds/AgentKindsController.js +56 -0
  22. package/dist/modules/agentKinds/AgentKindsController.js.map +1 -0
  23. package/dist/modules/persistence/PersistenceController.d.ts.map +1 -1
  24. package/dist/modules/persistence/PersistenceController.js +6 -161
  25. package/dist/modules/persistence/PersistenceController.js.map +1 -1
  26. package/dist/modules/persistence/dispatchScope.d.ts +16 -0
  27. package/dist/modules/persistence/dispatchScope.d.ts.map +1 -0
  28. package/dist/modules/persistence/dispatchScope.js +307 -0
  29. package/dist/modules/persistence/dispatchScope.js.map +1 -0
  30. package/dist/persistence/agentKinds.d.ts +43 -0
  31. package/dist/persistence/agentKinds.d.ts.map +1 -0
  32. package/dist/persistence/agentKinds.js +99 -0
  33. package/dist/persistence/agentKinds.js.map +1 -0
  34. package/dist/persistence/rpc-allowlist-libraries.d.ts +4 -0
  35. package/dist/persistence/rpc-allowlist-libraries.d.ts.map +1 -0
  36. package/dist/persistence/rpc-allowlist-libraries.js +209 -0
  37. package/dist/persistence/rpc-allowlist-libraries.js.map +1 -0
  38. package/dist/persistence/rpc-allowlist-vcs.d.ts +24 -0
  39. package/dist/persistence/rpc-allowlist-vcs.d.ts.map +1 -0
  40. package/dist/persistence/rpc-allowlist-vcs.js +119 -0
  41. package/dist/persistence/rpc-allowlist-vcs.js.map +1 -0
  42. package/dist/persistence/rpc-allowlist.d.ts.map +1 -1
  43. package/dist/persistence/rpc-allowlist.js +66 -249
  44. package/dist/persistence/rpc-allowlist.js.map +1 -1
  45. package/dist/persistence/rpc-scope.logic.d.ts +46 -0
  46. package/dist/persistence/rpc-scope.logic.d.ts.map +1 -1
  47. package/dist/persistence/rpc-scope.logic.js +109 -0
  48. package/dist/persistence/rpc-scope.logic.js.map +1 -1
  49. package/dist/persistence/rpc.d.ts +111 -0
  50. package/dist/persistence/rpc.d.ts.map +1 -1
  51. package/dist/persistence/rpc.js +42 -44
  52. package/dist/persistence/rpc.js.map +1 -1
  53. package/package.json +6 -6
@@ -0,0 +1,307 @@
1
+ /**
2
+ * The per-request READ MEMO, plus the one registry substitution it earns.
3
+ *
4
+ * Every entity-keyed scope rule resolves a row to bind the call, and for a handful of methods that
5
+ * row IS what the call then dispatches. Memoising each read per request keeps those to one query,
6
+ * and the `memoOverrides` table points the dispatched method at the same memo. Extracted from
7
+ * {@link buildDispatchScope} when the batched resolvers pushed it past its function-size budget:
8
+ * this half is about READING ONCE, the other about what a read MEANS for scope.
9
+ */
10
+ function buildRequestReaders(registry, request) {
11
+ const workspaceRepository = registry.workspaceRepository;
12
+ const blockRepository = registry.blockRepository;
13
+ const serviceRepository = registry.serviceRepository;
14
+ const skillSourceRepository = registry.skillSourceRepository;
15
+ // The owner-pair content-library source tables, keyed by the `LibrarySourceEntity` the rule
16
+ // names, so one resolver answers for both (and a new library is one row here). Keyed by the
17
+ // UNION rather than by `string`: a member added to `LibrarySourceEntity` must fail to compile
18
+ // here rather than resolve to nothing, which the stored-half check would otherwise be entitled
19
+ // to read as "no such row" and admit.
20
+ const librarySourceRepos = {
21
+ fragmentSource: registry.fragmentSourceRepository,
22
+ foundationalServiceSource: registry.foundationalServiceSourceRepository,
23
+ };
24
+ const resolveAccountId = (workspaceId) => workspaceRepository?.accountOf?.(workspaceId) ??
25
+ Promise.resolve(undefined);
26
+ // The BATCHED account read every list-shaped rule binds through. A registry that does not wire
27
+ // it answers an empty map, which fails every id closed rather than admitting one: the same
28
+ // disposition an unresolvable `accountOf` already has.
29
+ const readAccountIds = async (workspaceIds) => (await workspaceRepository?.accountIdsOf?.(workspaceIds)) ?? {};
30
+ // The `block`/`serviceList`/`service` scope checks resolve the owning account by reading the
31
+ // entity (`blockRepository.findById` / `serviceRepository.listByIds`). When the request ALSO
32
+ // dispatches that same read, memoise it per request so the resolver's read is reused instead
33
+ // of issuing a second identical query. `serviceRepository.get(id)` is the single-service form:
34
+ // its `service` scope resolves via `listByIds([id])`, so the dispatched `get` is routed through
35
+ // the same memo (a single-id `listByIds` yields the same row) rather than a second point read.
36
+ // (For every other `serviceList` method the dispatched method differs from the resolver's read,
37
+ // so there is nothing to dedupe.)
38
+ const memoizeRead = (fn) => {
39
+ const cache = new Map();
40
+ return (...args) => {
41
+ const key = JSON.stringify(args);
42
+ const hit = cache.get(key);
43
+ if (hit)
44
+ return hit;
45
+ const pending = Promise.resolve(fn(...args));
46
+ cache.set(key, pending);
47
+ return pending;
48
+ };
49
+ };
50
+ const installationRepository = registry.githubInstallationRepository;
51
+ const blockFindById = memoizeRead((blockId) => blockRepository?.findById?.(blockId));
52
+ // One memo per installation id, shared by the resolver and (via the override below) the
53
+ // dispatched point read, exactly as the source tables do. Built only when the repository is
54
+ // wired, so an unwired one answers `unreadable` rather than the absence an id-keyed upsert
55
+ // would be entitled to read as a create.
56
+ const installationGet = typeof installationRepository?.getByInstallationId === 'function'
57
+ ? memoizeRead((id) => installationRepository.getByInstallationId(id))
58
+ : undefined;
59
+ const blockFindByIds = memoizeRead((ids) => blockRepository?.findByIds?.(ids));
60
+ // One memo per requested SET of workspace ids, shared by the resolver and (via the override
61
+ // below) the dispatched `accountIdsOf`, exactly as the other self-keyed reads do.
62
+ const accountIdsOf = memoizeRead((ids) => readAccountIds(ids));
63
+ const installationListByIds = memoizeRead((ids) => installationRepository?.listByInstallationIds?.(ids));
64
+ const serviceListByIds = memoizeRead((ids) => serviceRepository?.listByIds?.(ids));
65
+ const skillSourceGet = memoizeRead((id) => skillSourceRepository?.get?.(id));
66
+ // One memo per source TABLE, keyed by entity: the resolver and the dispatched `get` of the same
67
+ // library then share a read, exactly as `skillSourceGet` does for skills. A table whose `get`
68
+ // this deployment does not wire gets NO reader rather than one answering `undefined`, so the
69
+ // resolver below can report `unreadable` instead of the absence that would admit an upsert.
70
+ const librarySourceReader = (entity) => {
71
+ const repo = librarySourceRepos[entity];
72
+ if (typeof repo?.get !== 'function')
73
+ return undefined;
74
+ // Called through the repository so a class-implemented `get` keeps its receiver.
75
+ return memoizeRead((id) => repo.get(id));
76
+ };
77
+ const librarySourceGets = {
78
+ fragmentSource: librarySourceReader('fragmentSource'),
79
+ foundationalServiceSource: librarySourceReader('foundationalServiceSource'),
80
+ };
81
+ // For the self-keyed reads, point the dispatcher's own call at the memo so it hits the
82
+ // resolver's already-resolved result. Only the one dispatched method is overridden; the rest
83
+ // of the registry is untouched. Keyed `repo.method` so a new self-keyed read is one row here
84
+ // rather than another rung on a ternary chain.
85
+ const serviceGetViaMemo = async (id) => (await serviceListByIds([id]))?.[0] ?? null;
86
+ const memoOverrides = {
87
+ 'blockRepository.findById': { blockRepository: { findById: blockFindById } },
88
+ 'blockRepository.findByIds': { blockRepository: { findByIds: blockFindByIds } },
89
+ 'serviceRepository.listByIds': { serviceRepository: { listByIds: serviceListByIds } },
90
+ 'serviceRepository.get': { serviceRepository: { get: serviceGetViaMemo } },
91
+ // The two batched self-keyed reads: the `workspaceList` rule resolves the accounts of exactly
92
+ // the ids `accountIdsOf` is being asked for, and `installationList` resolves the owners of
93
+ // exactly the ids `listByInstallationIds` is being asked for.
94
+ 'workspaceRepository.accountIdsOf': { workspaceRepository: { accountIdsOf } },
95
+ 'githubInstallationRepository.listByInstallationIds': {
96
+ githubInstallationRepository: { listByInstallationIds: installationListByIds },
97
+ },
98
+ // The `skillSource` scope resolves a source's account by reading the source; when the
99
+ // dispatched call IS that read (the sync service's `get`), reuse the resolver's result.
100
+ 'skillSourceRepository.get': { skillSourceRepository: { get: skillSourceGet } },
101
+ // The same self-keyed read for an installation: the `installation` rule resolves the row to
102
+ // scope the call, and the dispatched `getByInstallationId` IS that row.
103
+ ...(installationGet
104
+ ? {
105
+ 'githubInstallationRepository.getByInstallationId': {
106
+ githubInstallationRepository: { getByInstallationId: installationGet },
107
+ },
108
+ }
109
+ : {}),
110
+ // The same self-keyed read for the two owner-pair libraries' source tables. Contributed only
111
+ // where a reader exists: a table whose `get` is unwired has nothing to memoise, and the
112
+ // dispatcher's own wiring check answers that call with `... is not wired` regardless.
113
+ ...(librarySourceGets.fragmentSource
114
+ ? {
115
+ 'fragmentSourceRepository.get': {
116
+ fragmentSourceRepository: { get: librarySourceGets.fragmentSource },
117
+ },
118
+ }
119
+ : {}),
120
+ ...(librarySourceGets.foundationalServiceSource
121
+ ? {
122
+ 'foundationalServiceSourceRepository.get': {
123
+ foundationalServiceSourceRepository: {
124
+ get: librarySourceGets.foundationalServiceSource,
125
+ },
126
+ },
127
+ }
128
+ : {}),
129
+ };
130
+ // Substitute ONLY for a method the real registry actually wires. `memoizeRead` returns a
131
+ // function unconditionally — it closes over an optional-chained call — so overriding an ABSENT
132
+ // repository would satisfy the dispatcher's `typeof fn !== 'function'` wiring check and answer a
133
+ // misconfigured deployment with a scope 404 (or a bare null) instead of the `... is not wired`
134
+ // that names what to fix. The key is only ever consulted after `Object.hasOwn`, so an
135
+ // attacker-controlled `request.repo` cannot reach an inherited member through the lookup below.
136
+ const memoKey = `${request.repo}.${request.method}`;
137
+ const override = Object.hasOwn(memoOverrides, memoKey)
138
+ ? typeof registry[request.repo]?.[request.method] === 'function'
139
+ ? memoOverrides[memoKey]
140
+ : undefined
141
+ : undefined;
142
+ const registryForDispatch = override ? { ...registry, ...override } : registry;
143
+ return {
144
+ registryForDispatch,
145
+ blockRepository,
146
+ installationRepository,
147
+ resolveAccountId,
148
+ accountIdsOf,
149
+ blockFindById,
150
+ blockFindByIds,
151
+ serviceListByIds,
152
+ skillSourceGet,
153
+ librarySourceGets,
154
+ installationGet,
155
+ installationListByIds,
156
+ };
157
+ }
158
+ /**
159
+ * The per-request SCOPE RESOLUTION half of `POST /internal/persistence`: the entity readers the
160
+ * dispatcher binds a call through (block → workspace → account, service → account, a library
161
+ * source's owner pair, an installation's owner, an account's roster), over the per-request memo
162
+ * {@link buildRequestReaders} builds.
163
+ *
164
+ * Split out of `PersistenceController` when the surface completion pushed the handler past its
165
+ * function-size budget. It is the half that grows: every new entity-keyed scope rule adds a
166
+ * resolver here, while the controller's own job (gate, parse, dispatch, relay) does not change.
167
+ */
168
+ export function buildDispatchScope(registry, request, scope) {
169
+ const { registryForDispatch, blockRepository, installationRepository, resolveAccountId, accountIdsOf, blockFindById, blockFindByIds, serviceListByIds, skillSourceGet, librarySourceGets, installationGet, installationListByIds, } = buildRequestReaders(registry, request);
170
+ return {
171
+ registry: registryForDispatch,
172
+ scope,
173
+ resolveAccountId,
174
+ resolveAccountIds: async (workspaceIds) => {
175
+ const accounts = (await accountIdsOf(workspaceIds));
176
+ return new Map(Object.entries(accounts));
177
+ },
178
+ // A block is keyed only by its id; resolve its home workspace, then that workspace's account.
179
+ resolveBlockAccountId: async (blockId) => {
180
+ const found = (await blockFindById(blockId));
181
+ const workspaceId = found?.workspaceId;
182
+ return typeof workspaceId === 'string' ? resolveAccountId(workspaceId) : undefined;
183
+ },
184
+ // The batched form, in exactly two reads however long the list: one `findByIds` resolves every
185
+ // block's home workspace, then ONE `accountIdsOf` over the deduped workspaces. A block absent
186
+ // from either read is absent from the map.
187
+ resolveBlockAccountIds: async (blockIds) => {
188
+ const found = (await blockFindByIds(blockIds));
189
+ const entries = found ?? [];
190
+ const accounts = (await accountIdsOf([
191
+ ...new Set(entries.map((entry) => entry.workspaceId)),
192
+ ]));
193
+ const map = new Map();
194
+ for (const entry of entries) {
195
+ map.set(entry.block.id, Object.hasOwn(accounts, entry.workspaceId) ? accounts[entry.workspaceId] : undefined);
196
+ }
197
+ return map;
198
+ },
199
+ // Services are account-owned; resolve each requested id's `accountId` for the scope check.
200
+ resolveServiceAccountIds: async (serviceIds) => {
201
+ const services = (await serviceListByIds(serviceIds));
202
+ const map = new Map();
203
+ for (const service of services ?? [])
204
+ map.set(service.id, service.accountId);
205
+ return map;
206
+ },
207
+ // Skill sources are account-owned; the sync surface's methods carry only a source id, so
208
+ // resolve the row and project its `accountId` for the scope check. A source that does not
209
+ // exist resolves to undefined, which fails closed (404) exactly like a missing block/service.
210
+ resolveSkillSourceAccountId: async (sourceId) => {
211
+ const source = (await skillSourceGet(sourceId));
212
+ return source?.accountId;
213
+ },
214
+ // The owner-PAIR libraries' equivalent: a fragment / foundational-service source is owned by
215
+ // an `(ownerKind, ownerId)` tier rather than an account, so project the pair and let the rule
216
+ // resolve it the way it resolves a positional owner. A source that does not exist answers
217
+ // `absent`, which the sourceId-keyed rules fail closed on (404) exactly like a missing
218
+ // block/service, and which `ownerFieldUpsert` alone reads as a create.
219
+ resolveLibrarySourceOwner: async (entity, sourceId) => {
220
+ const read = librarySourceGets[entity];
221
+ // No reader for that table: nothing is known about the id, so say so. Answering `absent`
222
+ // here would let an id-keyed upsert through on its DECLARED owner alone.
223
+ if (!read)
224
+ return { status: 'unreadable' };
225
+ const source = (await read(sourceId));
226
+ return source
227
+ ? { status: 'found', owner: { ownerKind: source.ownerKind, ownerId: source.ownerId } }
228
+ : { status: 'absent' };
229
+ },
230
+ // A VCS installation binds either an ACCOUNT (a GitHub App installation, shared with every
231
+ // board of that account) or just its connector WORKSPACE (a per-workspace PAT, which stores
232
+ // no account). Resolve the effective owner so both bind the same way, and keep "no such
233
+ // binding" apart from "this deployment cannot read the table": the batched annotation read
234
+ // admits the first and must refuse the second.
235
+ resolveInstallationOwner: async (installationId) => {
236
+ if (!installationGet)
237
+ return { status: 'unreadable' };
238
+ const row = (await installationGet(installationId));
239
+ if (!row)
240
+ return { status: 'absent' };
241
+ if (typeof row.accountId === 'string')
242
+ return { status: 'found', accountId: row.accountId };
243
+ const workspaceId = row.workspaceId;
244
+ return {
245
+ status: 'found',
246
+ accountId: typeof workspaceId === 'string' ? await resolveAccountId(workspaceId) : null,
247
+ };
248
+ },
249
+ // The batched form: one `listByInstallationIds` plus one `accountIdsOf` over the connector
250
+ // workspaces of the PAT bindings that store no account. Ids with no binding are absent from
251
+ // the read, so the rule sees them as `absent` (the admission the annotation read needs) while
252
+ // an UNWIRED repository yields no map at all, which the rule fails closed on.
253
+ resolveInstallationOwners: async (installationIds) => {
254
+ const owners = new Map();
255
+ if (typeof installationRepository?.listByInstallationIds !== 'function') {
256
+ for (const id of installationIds)
257
+ owners.set(id, { status: 'unreadable' });
258
+ return owners;
259
+ }
260
+ const rows = ((await installationListByIds(installationIds)) ?? []);
261
+ const patWorkspaces = rows
262
+ .filter((row) => typeof row.accountId !== 'string' && typeof row.workspaceId === 'string')
263
+ .map((row) => row.workspaceId);
264
+ const accounts = (await accountIdsOf([...new Set(patWorkspaces)]));
265
+ for (const row of rows) {
266
+ if (typeof row.accountId === 'string') {
267
+ owners.set(row.installationId, { status: 'found', accountId: row.accountId });
268
+ continue;
269
+ }
270
+ const workspaceId = row.workspaceId;
271
+ owners.set(row.installationId, {
272
+ status: 'found',
273
+ accountId: typeof workspaceId === 'string' && Object.hasOwn(accounts, workspaceId)
274
+ ? accounts[workspaceId]
275
+ : null,
276
+ });
277
+ }
278
+ for (const id of installationIds) {
279
+ if (!owners.has(id))
280
+ owners.set(id, { status: 'absent' });
281
+ }
282
+ return owners;
283
+ },
284
+ // A service's frame block, as the three states `serviceInsert` needs: the row is written
285
+ // BEFORE its block, so `absent` is the ordinary create and only `found` binds.
286
+ resolveFrameBlockOwner: async (frameBlockId) => {
287
+ if (typeof blockRepository?.findById !== 'function')
288
+ return { status: 'unreadable' };
289
+ const found = (await blockFindById(frameBlockId));
290
+ if (!found)
291
+ return { status: 'absent' };
292
+ const workspaceId = found.workspaceId;
293
+ return {
294
+ status: 'found',
295
+ accountId: typeof workspaceId === 'string' ? await resolveAccountId(workspaceId) : null,
296
+ };
297
+ },
298
+ // The member-display scope (`user`/`userList`): a userId is in scope iff a co-member of an
299
+ // in-scope account, so resolve each in-scope account's roster to userIds. Bounded by the
300
+ // token's account scope, not the requested user list.
301
+ resolveAccountMemberIds: async (accountId) => {
302
+ const memberships = (await registry.membershipRepository?.listByAccount?.(accountId));
303
+ return (memberships ?? []).map((m) => m.userId);
304
+ },
305
+ };
306
+ }
307
+ //# sourceMappingURL=dispatchScope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatchScope.js","sourceRoot":"","sources":["../../../src/modules/persistence/dispatchScope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { AgentKindCapabilityView, AgentKindSource } from '@cat-factory/agents';
2
+ /**
3
+ * An {@link AgentKindSource} backed by the mothership's `GET /internal/agent-kinds`, presenting the
4
+ * node's machine token.
5
+ *
6
+ * **A failed read THROWS; it never degrades to an empty layer.** "The mothership is unreachable"
7
+ * and "this deployment assigns no capabilities" are the same value and opposite facts, and the
8
+ * second one is indistinguishable, at the agent, from a run that considered the org's playbook and
9
+ * chose not to follow it. That covers every way the read can fail to produce the layer: a transport
10
+ * error, a refusal, a 404 from a mothership older than this node, and a 200 whose payload this
11
+ * client cannot read.
12
+ *
13
+ * Throwing is not the same as failing the run, and the caller decides which it becomes. A skill a
14
+ * kind declared REQUIRED already fails its dispatch when it cannot be resolved, so the throw
15
+ * reaching that path is the existing contract, not a new one; the tool-server side states an
16
+ * unreachable layer to the agent the same way it states any capability it could not honour.
17
+ *
18
+ * There is no cache here, and the reason is NOT the one its three siblings give. Theirs is that a
19
+ * cache already sits in front of them (the per-workspace foundational catalog) and bounds the call
20
+ * volume; this read has nothing in front of it, so it is one round trip on EVERY dispatch. It stays
21
+ * uncached because that is the right size for what it costs: a dispatch starts a container agent
22
+ * that runs for minutes, the payload is the deployment's assignment table (bounded by the kinds a
23
+ * deployment registers capabilities for, not by tenants or runs), and the value has no invalidation
24
+ * path (it changes when the mothership is redeployed), so a TTL'd copy would be the homebrew cache
25
+ * the caching seam exists to keep out while buying a saving nothing can measure. If a caller ever
26
+ * puts this on a path that runs per STEP rather than per dispatch, it earns a real entry on the
27
+ * `AppCaches` seam, not a memo here.
28
+ */
29
+ export declare class HttpAgentKindSource implements AgentKindSource {
30
+ private readonly opts;
31
+ constructor(opts: {
32
+ baseUrl: string;
33
+ /**
34
+ * The machine token to present, as a fixed string OR a provider read PER REQUEST, the same
35
+ * shape `HttpPersistenceRpcClient` takes, so a token cached after boot by the
36
+ * `/local/mothership/connect` login is picked up without a restart.
37
+ */
38
+ token: string | (() => string | null);
39
+ fetchImpl?: typeof fetch;
40
+ });
41
+ capabilities(): Promise<AgentKindCapabilityView[]>;
42
+ }
43
+ //# sourceMappingURL=agentKinds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentKinds.d.ts","sourceRoot":"","sources":["../../src/persistence/agentKinds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAQnF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IAEvD,OAAO,CAAC,QAAQ,CAAC,IAAI;IADvB,YACmB,IAAI,EAAE;QACrB,OAAO,EAAE,MAAM,CAAA;QACf;;;;WAIG;QACH,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC,CAAA;QACrC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;KACzB,EACC;IAEE,YAAY,IAAI,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAqCvD;CACF"}
@@ -0,0 +1,99 @@
1
+ import { UnavailableError, describeError } from '@cat-factory/kernel';
2
+ // The client half of the mothership-mode agent-kind CAPABILITY read (see
3
+ // `../modules/agentKinds/AgentKindsController.ts` for why only that half of the registry crosses
4
+ // the machine API). The fourth sibling of `foundationalBuiltins.ts`, `binaryGenerators.ts` and
5
+ // `promptFragments.ts`, written to the same contract for the same reason.
6
+ /**
7
+ * An {@link AgentKindSource} backed by the mothership's `GET /internal/agent-kinds`, presenting the
8
+ * node's machine token.
9
+ *
10
+ * **A failed read THROWS; it never degrades to an empty layer.** "The mothership is unreachable"
11
+ * and "this deployment assigns no capabilities" are the same value and opposite facts, and the
12
+ * second one is indistinguishable, at the agent, from a run that considered the org's playbook and
13
+ * chose not to follow it. That covers every way the read can fail to produce the layer: a transport
14
+ * error, a refusal, a 404 from a mothership older than this node, and a 200 whose payload this
15
+ * client cannot read.
16
+ *
17
+ * Throwing is not the same as failing the run, and the caller decides which it becomes. A skill a
18
+ * kind declared REQUIRED already fails its dispatch when it cannot be resolved, so the throw
19
+ * reaching that path is the existing contract, not a new one; the tool-server side states an
20
+ * unreachable layer to the agent the same way it states any capability it could not honour.
21
+ *
22
+ * There is no cache here, and the reason is NOT the one its three siblings give. Theirs is that a
23
+ * cache already sits in front of them (the per-workspace foundational catalog) and bounds the call
24
+ * volume; this read has nothing in front of it, so it is one round trip on EVERY dispatch. It stays
25
+ * uncached because that is the right size for what it costs: a dispatch starts a container agent
26
+ * that runs for minutes, the payload is the deployment's assignment table (bounded by the kinds a
27
+ * deployment registers capabilities for, not by tenants or runs), and the value has no invalidation
28
+ * path (it changes when the mothership is redeployed), so a TTL'd copy would be the homebrew cache
29
+ * the caching seam exists to keep out while buying a saving nothing can measure. If a caller ever
30
+ * puts this on a path that runs per STEP rather than per dispatch, it earns a real entry on the
31
+ * `AppCaches` seam, not a memo here.
32
+ */
33
+ export class HttpAgentKindSource {
34
+ opts;
35
+ constructor(opts) {
36
+ this.opts = opts;
37
+ }
38
+ async capabilities() {
39
+ const fetchImpl = this.opts.fetchImpl ?? fetch;
40
+ const token = typeof this.opts.token === 'function' ? this.opts.token() : this.opts.token;
41
+ const url = `${this.opts.baseUrl.replace(/\/$/, '')}/internal/agent-kinds`;
42
+ let res;
43
+ try {
44
+ res = await fetchImpl(url, { headers: { authorization: `Bearer ${token ?? ''}` } });
45
+ }
46
+ catch (error) {
47
+ throw new UnavailableError('The agent-kind capabilities could not be read from the mothership', 'agent_kinds_unreachable',
48
+ // Scrubbed through `redactSecrets`, because a fetch failure routinely echoes the request
49
+ // URL, which here carries the mothership's address.
50
+ describeError(error));
51
+ }
52
+ if (!res.ok) {
53
+ // Includes the case that matters most: a mothership OLDER than this node, which answers 404
54
+ // for a route it does not serve. Reading that as "no assigned capabilities" is precisely the
55
+ // silent-empty-layer failure this class refuses to produce.
56
+ throw new UnavailableError('The mothership refused the agent-kind capability read', 'agent_kinds_unreachable', { status: res.status });
57
+ }
58
+ const body = (await res.json().catch(() => null));
59
+ // Shape-checked rather than cast, for the same reason every other failure here throws: a reply
60
+ // this code cannot read is a reply whose capability layer is unknown, and the one disposition
61
+ // that must never be reachable is answering an unknown layer with an empty one. An empty layer
62
+ // is spelled `[]`, which no honest server omits.
63
+ if (!body || !Array.isArray(body.kinds))
64
+ throw unreadable('kinds');
65
+ // The ELEMENTS too, and shallowly enough to matter: an entry whose `kind` is not a string can
66
+ // be matched against no dispatch, so a reply carrying one is a reply whose layer this node
67
+ // cannot apply — the very thing this class promises never to answer quietly.
68
+ if (!body.kinds.every(isCapabilityView))
69
+ throw unreadable('kinds[]');
70
+ return body.kinds;
71
+ }
72
+ }
73
+ /** The shape one entry must have for a dispatch to be able to apply it. */
74
+ function isCapabilityView(entry) {
75
+ if (!entry || typeof entry !== 'object')
76
+ return false;
77
+ const { kind, skills, toolServers } = entry;
78
+ if (typeof kind !== 'string')
79
+ return false;
80
+ const skillHalves = skills;
81
+ if (!skillHalves ||
82
+ !Array.isArray(skillHalves.bundled) ||
83
+ !Array.isArray(skillHalves.catalog) ||
84
+ !Array.isArray(skillHalves.unknown)) {
85
+ return false;
86
+ }
87
+ const tools = toolServers;
88
+ return Boolean(tools && Array.isArray(tools.servers) && Array.isArray(tools.unknown));
89
+ }
90
+ /**
91
+ * The unreadable-reply refusal. One helper rather than two literals because they are one FACT (the
92
+ * mothership answered with something this node cannot resolve a capability layer from), and the
93
+ * whole point of this class is that every route to "we do not know the org's capabilities" ends at
94
+ * a throw. `field` names which part failed.
95
+ */
96
+ function unreadable(field) {
97
+ return new UnavailableError('The mothership returned an unreadable agent-kind capability layer', 'agent_kinds_unreachable', { field });
98
+ }
99
+ //# sourceMappingURL=agentKinds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentKinds.js","sourceRoot":"","sources":["../../src/persistence/agentKinds.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { PersistenceMethodTable } from './rpc.js';
2
+ /** The content libraries' remote surface. See `rpc-allowlist.ts` for the table's contract. */
3
+ export declare const LIBRARY_PERSISTENCE_METHODS: PersistenceMethodTable;
4
+ //# sourceMappingURL=rpc-allowlist-libraries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-allowlist-libraries.d.ts","sourceRoot":"","sources":["../../src/persistence/rpc-allowlist-libraries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AAatD,8FAA8F;AAC9F,eAAO,MAAM,2BAA2B,EAAE,sBAoMzC,CAAA"}