@gobing-ai/ts-ai-decision 0.5.30 → 0.5.33
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/README.md +208 -42
- package/dist/agent-driver.d.ts +59 -0
- package/dist/agent-driver.d.ts.map +1 -0
- package/dist/agent-driver.js +104 -0
- package/dist/catalog.js +1 -0
- package/dist/hub.d.ts +72 -10
- package/dist/hub.d.ts.map +1 -1
- package/dist/hub.js +163 -28
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/registry.d.ts +43 -6
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +52 -6
- package/dist/schema.d.ts +12 -10
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +20 -2
- package/dist/types.d.ts +11 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/typesafe-driver.d.ts +24 -0
- package/dist/typesafe-driver.d.ts.map +1 -0
- package/dist/typesafe-driver.js +160 -0
- package/package.json +9 -5
- package/schemas/decision-catalog.schema.json +26 -2
- package/src/agent-driver.ts +164 -0
- package/src/catalog.ts +1 -0
- package/src/hub.ts +293 -45
- package/src/index.ts +2 -0
- package/src/registry.ts +111 -10
- package/src/schema.ts +21 -2
- package/src/types.ts +12 -3
- package/src/typesafe-driver.ts +212 -0
package/README.md
CHANGED
|
@@ -1,15 +1,53 @@
|
|
|
1
1
|
# @gobing-ai/ts-ai-decision
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
has a concrete answer.
|
|
3
|
+
The home of the **decision family** in ts-libs: declarative YAML **decision catalogs**, the
|
|
4
|
+
**`DecisionMakerRegistry`** that names every decision maker, and the fallback-guaranteed
|
|
5
|
+
**`DecisionHub`** that serves them (ADR-033). Applications depend on this package; the concrete
|
|
6
|
+
decision backends stay in their own packages and are bundled or registered here.
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
A catalog declares named, static decision points whose shape follows TypeSafe AI's Jev question
|
|
9
|
+
spec (`type` / `instructions` / `criteria`), with a required `fallback`, so every decision always
|
|
10
|
+
has a concrete answer no matter which backend is configured or available.
|
|
11
|
+
|
|
12
|
+
## The decision family at a glance
|
|
13
|
+
|
|
14
|
+
| Layer | Package | Owns |
|
|
15
|
+
|-------|---------|------|
|
|
16
|
+
| Contract | `@gobing-ai/ts-ai-runner` | Neutral `Question` / `Answer` types, `DecisionDriver`, `createDecisionMaker`, `validateQuestions` / `validateAnswers`, the `Decision*Error` taxonomy |
|
|
17
|
+
| Hub (this package) | `@gobing-ai/ts-ai-decision` | Catalog schema + loader, parameter rendering, `DecisionMakerRegistry`, `DecisionHub`, `createDecisionHub`, the TypeSafe and `llm-agent` drivers |
|
|
18
|
+
| Backends | `ts-decision-fm`, `ts-laya-mlx`, `ts-decision-clef` | One `DecisionDriver` each; they depend only on the contract, never on this package |
|
|
19
|
+
|
|
20
|
+
The dependency direction is fixed: backends → contract, hub → contract + bundled backends. That is
|
|
21
|
+
why backends live outside this package (ADR-033, ADR-037) — a backend that imported the hub would
|
|
22
|
+
create a cycle with the bundled built-ins.
|
|
23
|
+
|
|
24
|
+
### Decision makers
|
|
25
|
+
|
|
26
|
+
| Maker name | Backend | Package | Registered | Needs | Probabilities |
|
|
27
|
+
|------------|---------|---------|------------|-------|---------------|
|
|
28
|
+
| `typesafe` | TypeSafe Jev (hosted) | this package (`@typesafe-ai/sdk` 0.7.0) | built-in, lazy; hub default | `TYPESAFE_API_KEY` | from the API |
|
|
29
|
+
| `fm-local` | Apple Foundation Models via `fm` (ADR-029/030) | `ts-decision-fm` | built-in, lazy | macOS on Apple silicon, `fm` CLI | k-sample frequencies |
|
|
30
|
+
| `laya-local` | Laya on MLX, warm Python worker (ADR-027/028) | `ts-laya-mlx` (optional; installed on macOS only, ADR-041) | built-in, lazy | Apple silicon, Laya model + Python env. Other hosts fall back (`no-backend`) | from the local model |
|
|
31
|
+
| `llm-agent` | Any `ts-ai-runner` coding agent / model (ADR-038) | this package (`ts-ai-runner` surface) | built-in, lazy | an installed agent CLI | k-sample frequencies (accepted = all samples agreed) |
|
|
32
|
+
| `clef` | Cloudflare Workers AI Clef (ADR-037) | `ts-decision-clef` | consumer-registered | Cloudflare account ID + API token | from the API |
|
|
33
|
+
|
|
34
|
+
Built-ins are registered lazily: naming, listing and describing makers never constructs a driver,
|
|
35
|
+
so a host without a backend's prerequisites only notices when a decision actually routes to it —
|
|
36
|
+
and then gets the declared fallback (`reason: 'no-backend'` or `'error'`), not an exception.
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { createDecisionHub } from '@gobing-ai/ts-ai-decision';
|
|
42
|
+
|
|
43
|
+
const hub = await createDecisionHub({ catalogs: ['decisions/support.yaml'] });
|
|
44
|
+
|
|
45
|
+
const r = await hub.decide('category', { instructions: ticketText, channel: 'chat' });
|
|
46
|
+
// { id, type, value, confidence, source, reason, maker, durationMs }
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`createDecisionHub` composes registry + catalogs in one call and never returns a partially built
|
|
50
|
+
hub. See `examples/decisions.yaml` for a complete catalog.
|
|
13
51
|
|
|
14
52
|
## Catalog format
|
|
15
53
|
|
|
@@ -56,9 +94,6 @@ answer vocabulary stays static. `${env.*}` and any other namespace are rejected
|
|
|
56
94
|
|
|
57
95
|
## DecisionHub
|
|
58
96
|
|
|
59
|
-
`DecisionHub` loads catalogs and serves `decide(id, input)` with a guaranteed concrete answer —
|
|
60
|
-
see `examples/decisions.yaml` for a complete catalog:
|
|
61
|
-
|
|
62
97
|
```ts
|
|
63
98
|
import { DecisionHub } from '@gobing-ai/ts-ai-decision';
|
|
64
99
|
|
|
@@ -68,25 +103,69 @@ await hub.loadFile('decisions/support.yaml'); // or hub.load(loadedCatalog)
|
|
|
68
103
|
hub.list(); // [{ id, type, description, source }] — pure discovery
|
|
69
104
|
hub.describe('category'); // parameters (incl. instructions), criteria, fallback,
|
|
70
105
|
// effective minConfidence, maker and model
|
|
71
|
-
const r = await hub.decide('category', { instructions: ticketText,
|
|
72
|
-
// { id, type, value, confidence, source, reason, maker, durationMs }
|
|
73
|
-
const r2 = await hub.decide('category', { instructions: ticketText }, { maker: 'fm-local' });
|
|
106
|
+
const r = await hub.decide('category', { instructions: ticketText }, { maker: 'fm-local' });
|
|
74
107
|
```
|
|
75
108
|
|
|
76
109
|
`load` is all-or-nothing per catalog: a duplicate decision id across catalogs, or a `maker`
|
|
77
110
|
name (decision or `defaults.maker`) that is not registered, throws `DecisionCatalogError` and
|
|
78
|
-
registers nothing from the offending catalog.
|
|
111
|
+
registers nothing from the offending catalog.
|
|
112
|
+
|
|
113
|
+
### Maker chains
|
|
114
|
+
|
|
115
|
+
`maker` accepts a name or a non-empty ordered list — the first maker that does not fail as a
|
|
116
|
+
backend answers, which is how "local first, hosted when the local backend is unavailable" is spelled:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
decisions:
|
|
120
|
+
triage:
|
|
121
|
+
maker: [fm-local, typesafe] # ask fm-local, advance to typesafe only if it fails
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The chain advances only on backend failure (`no-backend`, `timeout`, `error`). A `low-confidence`
|
|
125
|
+
answer is an answer: it is returned from the maker that produced it and later makers stay
|
|
126
|
+
unresolved, so their lazy factories never run. When every maker fails, the result is the declared
|
|
127
|
+
fallback carrying the last failure's `reason`, `maker` and `error`, plus `attempts` — one
|
|
128
|
+
`{ maker, reason, durationMs, error? }` per maker tried, present only when more than one ran.
|
|
129
|
+
`describe` reports the effective chain as `makers`, with `maker` still the first name; a
|
|
130
|
+
single-name decision's result carries no `attempts`, so nothing changes for existing catalogs.
|
|
131
|
+
|
|
132
|
+
### Shadow makers
|
|
133
|
+
|
|
134
|
+
`shadow` names a second maker to ask **beside** the primary on live traffic — the same rendered
|
|
135
|
+
request, started at the same time — so a candidate can be evaluated without risking the decision:
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
decisions:
|
|
139
|
+
triage:
|
|
140
|
+
maker: typesafe
|
|
141
|
+
shadow: fm-local # asked on every call, reported, never served
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Every served field (`value`, `source`, `reason`, `maker`, `confidence`, `error`, `attempts`) still
|
|
145
|
+
comes from the primary chain; the shadow's own outcome rides beside it as `result.shadow` — its
|
|
146
|
+
`maker`, the `value` it actually produced (absent when it fell back), `confidence`, `reason`,
|
|
147
|
+
`durationMs` and `error?`. A failing or unregistered-at-call shadow never rejects `decide`.
|
|
148
|
+
|
|
149
|
+
**Cost:** a shadow doubles the backend calls for that decision and the call's latency becomes the
|
|
150
|
+
slower of the two, so it is a per-decision or per-call decision, not a catalog default (there is no
|
|
151
|
+
`defaults.shadow`). Pass `shadow` per call to sample rather than shadow every request. `list` and `describe` never construct a maker or
|
|
79
152
|
driver.
|
|
80
153
|
|
|
154
|
+
The hub picks a maker purely by name — per-call `options.maker` → the decision's `maker` → the
|
|
155
|
+
catalog's `defaults.maker` → the hub's `defaultMaker` (default `typesafe`). Catalog names are
|
|
156
|
+
checked at `load`; a misspelled per-call name fails loudly instead of falling back.
|
|
157
|
+
|
|
81
158
|
### The fallback contract
|
|
82
159
|
|
|
83
160
|
`decide` never rejects for backend outcomes — only caller mistakes throw (`UnknownDecisionError`
|
|
84
161
|
for an unknown id, `DecisionInputError` for input breaking the parameter contract,
|
|
85
|
-
`UnknownDecisionMakerError` for an unregistered per-call maker
|
|
86
|
-
unregistered
|
|
162
|
+
`UnknownDecisionMakerError` for an unregistered, empty or repeating per-call maker chain or an
|
|
163
|
+
unregistered per-call shadow; the constructor throws for an unregistered `defaultMaker`). Every
|
|
164
|
+
throw happens before any backend call.
|
|
87
165
|
|
|
88
|
-
Exactly one Jev question keyed by the decision id goes through
|
|
89
|
-
`maker.ask({ state, questions, model })
|
|
166
|
+
Exactly one Jev question keyed by the decision id goes through
|
|
167
|
+
`maker.ask({ state, questions, model })` — once per maker a chain tries, plus once for a shadow;
|
|
168
|
+
`model` resolves decision → catalog default → omitted.
|
|
90
169
|
Backend outcomes map to the declared fallback with `source: 'default'`:
|
|
91
170
|
|
|
92
171
|
| Outcome | `reason` |
|
|
@@ -99,50 +178,137 @@ Backend outcomes map to the declared fallback with `source: 'default'`:
|
|
|
99
178
|
|
|
100
179
|
Values: choice returns a string label, score the (possibly fractional) expected score, and noul
|
|
101
180
|
`p >= 0.5` with `probability` and confidence `max(p, 1 - p)`. Every result names the selected
|
|
102
|
-
`maker`.
|
|
181
|
+
`maker`. Every failure reason (`no-backend`, `timeout`, `error`) also carries the caught cause as
|
|
182
|
+
`error: { name, message }`, with the message truncated to 500 characters — persist it verbatim
|
|
183
|
+
(Spur writes it to `decision_logs.error`); accepted and `low-confidence` results never carry one.
|
|
184
|
+
|
|
185
|
+
Confidence is only as graded as the maker. Probability-native makers (`typesafe`, `laya-local`,
|
|
186
|
+
`clef`) return a model distribution; **sampled** makers (`fm-local`, `llm-agent`)
|
|
187
|
+
return entropy over k sample frequencies, so with k ≤ 5 any disagreement lands far below a 0.7
|
|
188
|
+
floor (two options: 2-of-3 → 0.08, 4-of-5 → 0.28). For a sampled maker, `accepted` effectively
|
|
189
|
+
means "all samples agreed", and a constant answer at confidence 1.0 is unanimity, not calibration.
|
|
190
|
+
Choose a probability-native maker where the floor must discriminate.
|
|
103
191
|
|
|
104
192
|
## Maker registry
|
|
105
193
|
|
|
106
|
-
`DecisionMakerRegistry` maps plain names to `DecisionMaker`s — the
|
|
107
|
-
|
|
194
|
+
`DecisionMakerRegistry` maps plain names to `DecisionMaker`s — the built-ins above plus anything
|
|
195
|
+
consumers register:
|
|
108
196
|
|
|
109
197
|
```ts
|
|
110
198
|
import { createDecisionMaker } from '@gobing-ai/ts-ai-runner';
|
|
111
199
|
import { DecisionMakerRegistry } from '@gobing-ai/ts-ai-decision';
|
|
200
|
+
import { createClefDriver } from '@gobing-ai/ts-decision-clef';
|
|
112
201
|
|
|
113
202
|
const registry = new DecisionMakerRegistry(); // pass `builtins: false` to start empty
|
|
114
|
-
registry.register('
|
|
203
|
+
registry.register('clef', () =>
|
|
204
|
+
createDecisionMaker({ driver: createClefDriver({ accountId, apiToken }) }),
|
|
205
|
+
);
|
|
115
206
|
|
|
116
207
|
registry.has('fm-local'); // true — nothing was constructed yet
|
|
117
208
|
const maker = await registry.resolve('fm-local'); // factory runs here, once, and is memoised
|
|
118
|
-
registry.names(); // ['typesafe', 'fm-local', 'laya-local', '
|
|
209
|
+
registry.names(); // ['typesafe', 'fm-local', 'laya-local', 'llm-agent', 'clef']
|
|
119
210
|
```
|
|
120
211
|
|
|
121
212
|
Registering builds nothing; the first `resolve` runs a factory and memoises the maker, and a
|
|
122
213
|
factory that throws is not memoised, so a later `resolve` retries. Invalid or duplicate names
|
|
123
214
|
throw `DecisionRegistryError`; unknown names throw `UnknownDecisionMakerError { name }`; factory
|
|
124
|
-
errors propagate unchanged. Pass `builtins: { makerOptions, driverOptions }` to tune the
|
|
125
|
-
(env/keys/timeouts
|
|
215
|
+
errors propagate unchanged. Pass `builtins: { makerOptions, driverOptions }` to tune the
|
|
216
|
+
built-ins (env/keys/timeouts via `makerOptions`; executor/client injection via
|
|
217
|
+
`driverOptions['fm-local']` / `driverOptions['laya-local']` / `driverOptions['llm-agent']`).
|
|
126
218
|
|
|
127
|
-
|
|
128
|
-
warm Python worker whose open pipes keep the calling process alive until it is closed (reachable from
|
|
129
|
-
a hub as `hub.registry.dispose()`). It never runs an unresolved factory just to close it, ignores
|
|
130
|
-
unknown names, and returns each entry to its unresolved state, so a later `resolve` builds a fresh
|
|
131
|
-
maker instead of handing back a closed one.
|
|
219
|
+
### Configuring `llm-agent`
|
|
132
220
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
name fails loudly instead of falling back. `createDecisionHub` composes the whole thing in one
|
|
137
|
-
call and never returns a partially built hub:
|
|
221
|
+
`llm-agent` turns any installed coding agent or model into a decision model. It samples the agent
|
|
222
|
+
`k` times per ask, enum-checks every sample against the question's declared options, and reports
|
|
223
|
+
empirical sample frequencies — the agent is never asked for a confidence (ADR-030).
|
|
138
224
|
|
|
139
225
|
```ts
|
|
140
|
-
import {
|
|
226
|
+
import { createAgentDriver } from '@gobing-ai/ts-ai-decision';
|
|
227
|
+
import { createDecisionMaker } from '@gobing-ai/ts-ai-runner';
|
|
141
228
|
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
229
|
+
const registry = new DecisionMakerRegistry({
|
|
230
|
+
builtins: {
|
|
231
|
+
driverOptions: { 'llm-agent': { agent: 'claude', model: 'haiku', samples: 3 } },
|
|
232
|
+
},
|
|
145
233
|
});
|
|
234
|
+
// Extra instances under their own names, e.g. a cheap second opinion:
|
|
235
|
+
registry.register('llm-haiku', () =>
|
|
236
|
+
createDecisionMaker({ driver: createAgentDriver({ agent: 'claude', model: 'haiku' }) }),
|
|
237
|
+
);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
| Option | Default | Meaning |
|
|
241
|
+
|--------|---------|---------|
|
|
242
|
+
| `agent` | first installed agent in `TIER1_PRIORITY` | Agent to sample. A configured agent is used as-is with no probe; a missing CLI then fails the sample. |
|
|
243
|
+
| `model` | agent default | Model id forwarded to the agent; a per-ask `model` overrides it. |
|
|
244
|
+
| `samples` | `3` | Agent invocations per ask — **cost is k runs per ask**, and answer resolution is 1/k. |
|
|
245
|
+
| `deterministic` | `false` | One greedy sample; probabilities are one-hot. |
|
|
246
|
+
| `concurrency` | `1` | Max samples in flight (sequential by default). |
|
|
247
|
+
| `timeoutMs` | `120000` | Budget for one sample; expiry raises `DecisionTimeoutError`. |
|
|
248
|
+
|
|
249
|
+
**Isolation.** Every ask samples in a fresh scratch directory under the runtime temp dir, removed
|
|
250
|
+
when the ask ends — success or failure. This is load-bearing, not tidiness: the claude one-shot shim
|
|
251
|
+
grants `--permission-mode acceptEdits --allowedTools Write Edit`, so the scratch directory is what
|
|
252
|
+
stops a decision from editing the caller's workspace. The driver never passes `continue`, `sessionId`
|
|
253
|
+
or `sessionDir`.
|
|
254
|
+
|
|
255
|
+
**Availability.** Detection runs on the first `ask` (never at registration), so an agent installed
|
|
256
|
+
later is picked up without rebuilding the maker; no agent at all raises `DecisionConfigError` with
|
|
257
|
+
code `AGENT`, which the hub reports as `reason: 'no-backend'` so the decision uses its fallback.
|
|
258
|
+
|
|
259
|
+
`dispose(name?)` releases the resources of already-`resolve`d makers — a `laya-local` maker holds
|
|
260
|
+
a warm Python worker whose open pipes keep the calling process alive until it is closed
|
|
261
|
+
(reachable from a hub as `hub.registry.dispose()`). It never runs an unresolved factory just to
|
|
262
|
+
close it, ignores unknown names, and returns each entry to its unresolved state, so a later
|
|
263
|
+
`resolve` builds a fresh maker instead of handing back a closed one.
|
|
264
|
+
|
|
265
|
+
The same registry can be handed to `createDecisionHub({ registry })` or extended through
|
|
266
|
+
`createDecisionHub({ makers: { name: factory } })`.
|
|
267
|
+
|
|
268
|
+
## Adding a decision backend
|
|
269
|
+
|
|
270
|
+
New backends follow the existing pattern rather than landing inside this package:
|
|
271
|
+
|
|
272
|
+
1. Implement `DecisionDriver` from `@gobing-ai/ts-ai-runner` in its own package
|
|
273
|
+
(`packages/decision-<name>`, published as `@gobing-ai/ts-decision-<name>`). Depend only on the
|
|
274
|
+
contract (and ts-runtime / ts-infra seams for platform or HTTP access); never import
|
|
275
|
+
`@gobing-ai/ts-ai-decision`.
|
|
276
|
+
2. Return answers that pass `validateAnswers`, translate every transport failure into the
|
|
277
|
+
`Decision*Error` taxonomy, and never invent a confidence the backend does not produce
|
|
278
|
+
(ADR-030: sampled backends report empirical frequencies only).
|
|
279
|
+
3. Make it reachable by name: consumers register it (`clef` pattern), or — when it should be
|
|
280
|
+
available out of the box — add a lazy built-in to `DecisionMakerRegistry` with its options
|
|
281
|
+
under `builtins.driverOptions`.
|
|
282
|
+
4. Add the boundary to `.spur/rules/typescript/decision-boundaries.yaml` and an ADR entry in
|
|
283
|
+
`docs/00_ADR.md`.
|
|
284
|
+
|
|
285
|
+
Capabilities every maker benefits from (routing, fallback, catalog features, registry
|
|
286
|
+
lifecycle) belong in this package.
|
|
287
|
+
|
|
288
|
+
## TypeSafe driver
|
|
289
|
+
|
|
290
|
+
`createTypesafeDriver(config)` ships here since task 0116 (ADR-039): `@gobing-ai/ts-ai-runner` keeps
|
|
291
|
+
only the neutral contract, so the driver and the pinned `@typesafe-ai/sdk` (0.7.0) live in this
|
|
292
|
+
package.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
import { createTypesafeDriver } from '@gobing-ai/ts-ai-decision';
|
|
296
|
+
import { createDecisionMaker } from '@gobing-ai/ts-ai-runner';
|
|
297
|
+
|
|
298
|
+
const decisions = createDecisionMaker({ driver: createTypesafeDriver({ apiKey }) });
|
|
146
299
|
```
|
|
147
300
|
|
|
148
|
-
|
|
301
|
+
`createDecisionMaker({ backend: 'typesafe' })` and the implicit default still resolve the same driver
|
|
302
|
+
through a deprecated dynamic-import bridge in `ts-ai-runner` for one release; that path requires this
|
|
303
|
+
package to be installed and reports `DecisionConfigError` (`TYPESAFE_BACKEND`) with an install hint
|
|
304
|
+
when it is not. `resolveApiKey` remains in `ts-ai-runner` and is what both paths use, so
|
|
305
|
+
`TYPESAFE_API_KEY` precedence is unchanged.
|
|
306
|
+
|
|
307
|
+
`@typesafe-ai/sdk` 0.7.0 raises its Node floor to 22 and compresses request bodies with zstd on
|
|
308
|
+
runtimes that support it (Bun does).
|
|
309
|
+
|
|
310
|
+
## References
|
|
311
|
+
|
|
312
|
+
- `docs/design/ai-decision-catalog.md` — full catalog field reference, hub and registry design.
|
|
313
|
+
- `docs/00_ADR.md` — ADR-027/028 (Laya), ADR-029/030/031 (fm), ADR-033 (catalog + hub),
|
|
314
|
+
ADR-037 (Clef), ADR-038/039 (llm-agent, TypeSafe relocation).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `llm-agent` decision driver (task 0115, ADR-038/ADR-039): any installed
|
|
3
|
+
* `ts-ai-runner` coding agent or model answering through the shared sampled-driver
|
|
4
|
+
* core. Each sample is one isolated one-shot prompt run; probabilities are sample
|
|
5
|
+
* frequencies (ADR-030) — the agent is never asked for a confidence.
|
|
6
|
+
*
|
|
7
|
+
* Every sample runs in a per-ask scratch directory. That is load-bearing, not
|
|
8
|
+
* tidiness: the claude one-shot shim grants `--permission-mode acceptEdits
|
|
9
|
+
* --allowedTools Write Edit`, so a decision must never be able to write into the
|
|
10
|
+
* caller's workspace (R6).
|
|
11
|
+
*/
|
|
12
|
+
import { AgentDetector, type AgentName, AiRunner, type Question, type SampledDecisionDriver } from '@gobing-ai/ts-ai-runner';
|
|
13
|
+
import { type FileSystem } from '@gobing-ai/ts-runtime';
|
|
14
|
+
/** The one runner method a sampled decision needs; a real `AiRunner` satisfies it. */
|
|
15
|
+
export type AgentPromptRunner = Pick<AiRunner, 'runPromptCommand'>;
|
|
16
|
+
/** The one detector method the driver needs; a real `AgentDetector` satisfies it. */
|
|
17
|
+
export type AgentDetectorPort = Pick<AgentDetector, 'detectOne'>;
|
|
18
|
+
/** Configuration options for {@link createAgentDriver}. */
|
|
19
|
+
export interface AgentDriverOptions {
|
|
20
|
+
/** Agent to sample. Default: the first installed agent in `TIER1_PRIORITY` (fm is never auto-picked). */
|
|
21
|
+
readonly agent?: AgentName;
|
|
22
|
+
/** Model id forwarded as `PromptOptions.model`; a per-ask `model` overrides it. */
|
|
23
|
+
readonly model?: string;
|
|
24
|
+
/** Samples per ask. Default: 3. Ignored when `deterministic`. */
|
|
25
|
+
readonly samples?: number;
|
|
26
|
+
/** One greedy sample; probabilities are one-hot. Default: false. */
|
|
27
|
+
readonly deterministic?: boolean;
|
|
28
|
+
/** Max samples in flight. Default: 1 (sequential). */
|
|
29
|
+
readonly concurrency?: number;
|
|
30
|
+
/** Budget for one sample. Default: 120_000. */
|
|
31
|
+
readonly timeoutMs?: number;
|
|
32
|
+
/** Injected runner. Default: a lazy `new AiRunner()`. */
|
|
33
|
+
readonly runner?: AgentPromptRunner;
|
|
34
|
+
/** Injected detector. Default: a lazy `new AgentDetector({ runner })`. */
|
|
35
|
+
readonly detector?: AgentDetectorPort;
|
|
36
|
+
/** Scratch-directory lifecycle. Default: ts-runtime's node FileSystem. */
|
|
37
|
+
readonly fs?: Pick<FileSystem, 'ensureDir' | 'deleteFile'>;
|
|
38
|
+
}
|
|
39
|
+
/** The agent driver: a sampled decision driver named `llm-agent`. */
|
|
40
|
+
export interface AgentDecisionDriver extends SampledDecisionDriver {
|
|
41
|
+
readonly name: 'llm-agent';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Instructions for a *generic* agent. fm's text mentions "the schema" because fm
|
|
45
|
+
* receives `--schema`; agents get no such flag, so this text never does.
|
|
46
|
+
*/
|
|
47
|
+
export declare const AGENT_INSTRUCTIONS: string;
|
|
48
|
+
/**
|
|
49
|
+
* The per-ask reply contract appended to the prompt: every key with its allowed
|
|
50
|
+
* option strings, plus one example object. It never asks for probabilities or
|
|
51
|
+
* confidence (ADR-030).
|
|
52
|
+
*/
|
|
53
|
+
export declare function JSON_REPLY_SUFFIX(questions: Record<string, Question>): string;
|
|
54
|
+
/**
|
|
55
|
+
* Build the `llm-agent` decision driver. Construction spawns nothing: the runner
|
|
56
|
+
* and detector are created lazily and the agent is resolved on the first `ask`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createAgentDriver(options?: AgentDriverOptions): AgentDecisionDriver;
|
|
59
|
+
//# sourceMappingURL=agent-driver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-driver.d.ts","sourceRoot":"","sources":["../src/agent-driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACH,aAAa,EACb,KAAK,SAAS,EACd,QAAQ,EAQR,KAAK,QAAQ,EACb,KAAK,qBAAqB,EAG7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAwB,KAAK,UAAU,EAA2B,MAAM,uBAAuB,CAAC;AAEvG,sFAAsF;AACtF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;AAEnE,qFAAqF;AACrF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;AAEjE,2DAA2D;AAC3D,MAAM,WAAW,kBAAkB;IAC/B,yGAAyG;IACzG,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,mFAAmF;IACnF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,sDAAsD;IACtD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IACtC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,GAAG,YAAY,CAAC,CAAC;CAC9D;AAED,qEAAqE;AACrE,MAAM,WAAW,mBAAoB,SAAQ,qBAAqB;IAC9D,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC9B;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAGM,CAAC;AAEtC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAc7E;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,mBAAmB,CAgEvF"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `llm-agent` decision driver (task 0115, ADR-038/ADR-039): any installed
|
|
3
|
+
* `ts-ai-runner` coding agent or model answering through the shared sampled-driver
|
|
4
|
+
* core. Each sample is one isolated one-shot prompt run; probabilities are sample
|
|
5
|
+
* frequencies (ADR-030) — the agent is never asked for a confidence.
|
|
6
|
+
*
|
|
7
|
+
* Every sample runs in a per-ask scratch directory. That is load-bearing, not
|
|
8
|
+
* tidiness: the claude one-shot shim grants `--permission-mode acceptEdits
|
|
9
|
+
* --allowedTools Write Edit`, so a decision must never be able to write into the
|
|
10
|
+
* caller's workspace (R6).
|
|
11
|
+
*/
|
|
12
|
+
import { AgentDetector, AiRunner, createSampledDriver, DecisionBackendError, DecisionConfigError, DecisionTimeoutError, declaredOptions, extractJsonAnswer, TIER1_PRIORITY, } from '@gobing-ai/ts-ai-runner';
|
|
13
|
+
import { createNodeFileSystem, getProcessEnv, joinPath } from '@gobing-ai/ts-runtime';
|
|
14
|
+
/**
|
|
15
|
+
* Instructions for a *generic* agent. fm's text mentions "the schema" because fm
|
|
16
|
+
* receives `--schema`; agents get no such flag, so this text never does.
|
|
17
|
+
*/
|
|
18
|
+
export const AGENT_INSTRUCTIONS = 'You are a decision function. Read the context and questions, then reply with exactly one JSON ' +
|
|
19
|
+
'object mapping each question key to one of its allowed option strings. No prose, no code, no ' +
|
|
20
|
+
'tool use, no confidence values.';
|
|
21
|
+
/**
|
|
22
|
+
* The per-ask reply contract appended to the prompt: every key with its allowed
|
|
23
|
+
* option strings, plus one example object. It never asks for probabilities or
|
|
24
|
+
* confidence (ADR-030).
|
|
25
|
+
*/
|
|
26
|
+
export function JSON_REPLY_SUFFIX(questions) {
|
|
27
|
+
const lines = ['', 'Reply with exactly one JSON object and nothing else.'];
|
|
28
|
+
for (const [key, question] of Object.entries(questions)) {
|
|
29
|
+
const options = declaredOptions(question)
|
|
30
|
+
.map((option) => `"${option}"`)
|
|
31
|
+
.join(', ');
|
|
32
|
+
lines.push(`- "${key}": one of ${options}`);
|
|
33
|
+
}
|
|
34
|
+
const example = {};
|
|
35
|
+
for (const [key, question] of Object.entries(questions)) {
|
|
36
|
+
example[key] = declaredOptions(question)[0] ?? '';
|
|
37
|
+
}
|
|
38
|
+
lines.push('', 'Example:', JSON.stringify(example));
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
}
|
|
41
|
+
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
42
|
+
/**
|
|
43
|
+
* Build the `llm-agent` decision driver. Construction spawns nothing: the runner
|
|
44
|
+
* and detector are created lazily and the agent is resolved on the first `ask`.
|
|
45
|
+
*/
|
|
46
|
+
export function createAgentDriver(options = {}) {
|
|
47
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
48
|
+
const fs = options.fs ?? createNodeFileSystem();
|
|
49
|
+
// Created inside the factory, and only when not injected, so construction never spawns.
|
|
50
|
+
const runner = options.runner ?? new AiRunner();
|
|
51
|
+
// A real AiRunner satisfies AgentPromptRunner, so this cast only widens a fake back
|
|
52
|
+
// to the injected seam; a fake runner is always paired with its own fake detector.
|
|
53
|
+
const detector = options.detector ?? new AgentDetector({ runner: runner });
|
|
54
|
+
// Memoised on success only: a later ask retries an unresolved agent after an install.
|
|
55
|
+
let resolved;
|
|
56
|
+
async function resolveAgent() {
|
|
57
|
+
if (options.agent !== undefined)
|
|
58
|
+
return options.agent;
|
|
59
|
+
if (resolved !== undefined)
|
|
60
|
+
return resolved;
|
|
61
|
+
for (const candidate of TIER1_PRIORITY) {
|
|
62
|
+
const detected = await detector.detectOne(candidate);
|
|
63
|
+
if (detected.installed) {
|
|
64
|
+
resolved = detected.name;
|
|
65
|
+
return resolved;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
throw new DecisionConfigError(`no coding agent available for llm-agent; install one of: ${TIER1_PRIORITY.join(', ')}`, 'AGENT');
|
|
69
|
+
}
|
|
70
|
+
return createSampledDriver({
|
|
71
|
+
name: 'llm-agent',
|
|
72
|
+
samples: options.samples ?? 3,
|
|
73
|
+
deterministic: options.deterministic ?? false,
|
|
74
|
+
concurrency: options.concurrency ?? 1,
|
|
75
|
+
instructions: AGENT_INSTRUCTIONS,
|
|
76
|
+
async open({ instructions, prompt, questions, model }) {
|
|
77
|
+
const agent = await resolveAgent();
|
|
78
|
+
const scratch = joinPath(getProcessEnv().TMPDIR ?? '/tmp', `llm-agent-${crypto.randomUUID()}`);
|
|
79
|
+
await fs.ensureDir(scratch);
|
|
80
|
+
const input = `${instructions}\n\n${prompt}${JSON_REPLY_SUFFIX(questions)}`;
|
|
81
|
+
const promptOptions = { input, mode: 'text' };
|
|
82
|
+
const effectiveModel = model ?? options.model;
|
|
83
|
+
if (effectiveModel !== undefined)
|
|
84
|
+
promptOptions.model = effectiveModel;
|
|
85
|
+
return {
|
|
86
|
+
async sample() {
|
|
87
|
+
const result = await runner.runPromptCommand(agent, promptOptions, {
|
|
88
|
+
cwd: scratch,
|
|
89
|
+
timeout: timeoutMs,
|
|
90
|
+
});
|
|
91
|
+
if (result.exitCode === null) {
|
|
92
|
+
throw new DecisionTimeoutError(`llm-agent sample timed out after ${timeoutMs}ms`, timeoutMs);
|
|
93
|
+
}
|
|
94
|
+
if (result.exitCode !== 0) {
|
|
95
|
+
const firstLine = result.stderr.split('\n')[0]?.trim() ?? '';
|
|
96
|
+
throw new DecisionBackendError(`llm-agent sample failed (${agent} exit ${result.exitCode}): ${firstLine}`, undefined);
|
|
97
|
+
}
|
|
98
|
+
return extractJsonAnswer(result.stdout);
|
|
99
|
+
},
|
|
100
|
+
close: () => fs.deleteFile(scratch),
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
package/dist/catalog.js
CHANGED
|
@@ -248,6 +248,7 @@ function checkDecision(id, raw, source) {
|
|
|
248
248
|
fallback: raw.fallback,
|
|
249
249
|
...(raw.minConfidence !== undefined ? { minConfidence: raw.minConfidence } : {}),
|
|
250
250
|
...(raw.maker !== undefined ? { maker: raw.maker } : {}),
|
|
251
|
+
...(raw.shadow !== undefined ? { shadow: raw.shadow } : {}),
|
|
251
252
|
...(raw.model !== undefined ? { model: raw.model } : {}),
|
|
252
253
|
};
|
|
253
254
|
}
|
package/dist/hub.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Json } from '@gobing-ai/ts-ai-runner';
|
|
2
2
|
import { type CatalogLoadOptions } from './catalog';
|
|
3
3
|
import { type BuiltinMakerOptions, DecisionMakerRegistry, type MakerSource } from './registry';
|
|
4
|
-
import type { DecisionCatalog, DecisionCriteria, DecisionParam, DecisionType } from './types';
|
|
4
|
+
import type { DecisionCatalog, DecisionCriteria, DecisionParam, DecisionType, MakerRef } from './types';
|
|
5
5
|
/**
|
|
6
6
|
* DecisionHub (task 0090, ADR-033 § API / § decide algorithm): loads catalogs
|
|
7
7
|
* over the 0089 loader and serves `decide` through makers resolved by name
|
|
@@ -16,6 +16,40 @@ import type { DecisionCatalog, DecisionCriteria, DecisionParam, DecisionType } f
|
|
|
16
16
|
export declare const DEFAULT_MIN_CONFIDENCE = 0.7;
|
|
17
17
|
/** Hub default when neither the per-call options, the decision nor the catalog names a maker. */
|
|
18
18
|
export declare const DEFAULT_MAKER = "typesafe";
|
|
19
|
+
/** Upper bound on a carried error message, in characters (ADR-040). */
|
|
20
|
+
export declare const DECISION_ERROR_MESSAGE_MAX = 500;
|
|
21
|
+
/**
|
|
22
|
+
* The caught failure, carried on a fallback result so a consumer can persist it (Spur stores
|
|
23
|
+
* `decision_logs.error`). Only `name` and `message`: a stack, a `cause` chain or the failing
|
|
24
|
+
* request would leak prompt text and internals into logs.
|
|
25
|
+
*/
|
|
26
|
+
export interface DecisionErrorInfo {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly message: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One maker's attempt inside a chain: what it was, how it ended, and how long it took. Present only
|
|
32
|
+
* when more than one maker was tried, so a single-name decision's result is unchanged.
|
|
33
|
+
*/
|
|
34
|
+
export interface DecisionAttempt {
|
|
35
|
+
readonly maker: string;
|
|
36
|
+
readonly reason: DecisionResultBase['reason'];
|
|
37
|
+
readonly durationMs: number;
|
|
38
|
+
readonly error?: DecisionErrorInfo;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* What a shadow maker did with the same request. Observed only: it rides beside the served answer and
|
|
42
|
+
* never changes it (ADR-040). `value` is the answer the shadow itself produced, so it is absent when
|
|
43
|
+
* the shadow fell back.
|
|
44
|
+
*/
|
|
45
|
+
export interface ShadowOutcome {
|
|
46
|
+
readonly maker: string;
|
|
47
|
+
readonly value?: string | number | boolean;
|
|
48
|
+
readonly confidence: number | null;
|
|
49
|
+
readonly reason: DecisionResultBase['reason'];
|
|
50
|
+
readonly durationMs: number;
|
|
51
|
+
readonly error?: DecisionErrorInfo;
|
|
52
|
+
}
|
|
19
53
|
/** Shared envelope of every {@link DecisionResult} variant. */
|
|
20
54
|
export interface DecisionResultBase {
|
|
21
55
|
readonly id: string;
|
|
@@ -27,6 +61,12 @@ export interface DecisionResultBase {
|
|
|
27
61
|
/** Registry name of the maker that was selected. */
|
|
28
62
|
readonly maker: string;
|
|
29
63
|
readonly durationMs: number;
|
|
64
|
+
/** The caught failure. Present only for `no-backend`, `timeout` and `error`. */
|
|
65
|
+
readonly error?: DecisionErrorInfo;
|
|
66
|
+
/** Every maker tried, in order. Present only when a chain tried more than one. */
|
|
67
|
+
readonly attempts?: readonly DecisionAttempt[];
|
|
68
|
+
/** The shadow's outcome. Present only when the decision or the call named a shadow. */
|
|
69
|
+
readonly shadow?: ShadowOutcome;
|
|
30
70
|
}
|
|
31
71
|
/** The fallback-guaranteed result of one `decide` call, shaped by the decision type. */
|
|
32
72
|
export type DecisionResult = (DecisionResultBase & {
|
|
@@ -59,8 +99,12 @@ export interface DecisionDescriptor {
|
|
|
59
99
|
readonly fallback: string | number | boolean;
|
|
60
100
|
/** decision → catalog defaults → {@link DEFAULT_MIN_CONFIDENCE}. */
|
|
61
101
|
readonly minConfidence: number;
|
|
62
|
-
/** decision → catalog defaults → hub defaultMaker. */
|
|
102
|
+
/** First name of the effective chain (decision → catalog defaults → hub defaultMaker). */
|
|
63
103
|
readonly maker: string;
|
|
104
|
+
/** The full effective chain; a single name yields a one-element list. */
|
|
105
|
+
readonly makers: readonly string[];
|
|
106
|
+
/** The decision's configured shadow; a per-call shadow is not visible to `describe`. */
|
|
107
|
+
readonly shadow?: string;
|
|
64
108
|
/** decision → catalog defaults; absent when neither declares one. */
|
|
65
109
|
readonly model?: string;
|
|
66
110
|
}
|
|
@@ -75,8 +119,13 @@ export interface DecisionHubOptions {
|
|
|
75
119
|
}
|
|
76
120
|
/** Per-call options for {@link DecisionHub.decide}. */
|
|
77
121
|
export interface DecideOptions {
|
|
78
|
-
/**
|
|
79
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Per-call maker name or ordered fallback chain (ADR-040); wins over the decision's, the
|
|
124
|
+
* catalog's and the hub default. Validated before any backend call.
|
|
125
|
+
*/
|
|
126
|
+
readonly maker?: MakerRef;
|
|
127
|
+
/** Per-call shadow maker (ADR-040); wins over the decision's. Validated before any backend call. */
|
|
128
|
+
readonly shadow?: string;
|
|
80
129
|
}
|
|
81
130
|
/**
|
|
82
131
|
* Hub over loaded catalogs and named makers (task 0090, ADR-033 § API):
|
|
@@ -93,8 +142,9 @@ export declare class DecisionHub {
|
|
|
93
142
|
constructor(options?: DecisionHubOptions);
|
|
94
143
|
/**
|
|
95
144
|
* Register every decision of an already-loaded catalog. All-or-nothing: a
|
|
96
|
-
* duplicate decision id or an unregistered maker name (
|
|
97
|
-
* `defaults.maker
|
|
145
|
+
* duplicate decision id or an unregistered maker name (any chain name of a
|
|
146
|
+
* decision or `defaults.maker`, or a decision `shadow`) throws
|
|
147
|
+
* {@link DecisionCatalogError} and registers
|
|
98
148
|
* nothing from the offending catalog.
|
|
99
149
|
*/
|
|
100
150
|
load(catalog: DecisionCatalog): void;
|
|
@@ -107,14 +157,26 @@ export declare class DecisionHub {
|
|
|
107
157
|
/**
|
|
108
158
|
* Serve one decision with a fallback-guaranteed answer. Resolves the input,
|
|
109
159
|
* renders exactly one Jev question keyed by the decision id, selects the
|
|
110
|
-
* maker
|
|
111
|
-
*
|
|
160
|
+
* maker chain (per call → decision → catalog defaults → hub default) and
|
|
161
|
+
* asks its makers in order until one answers, with an optional shadow asked
|
|
162
|
+
* beside it (ADR-040). Backend construction, transport, timeout,
|
|
112
163
|
* invalid answers and low confidence all resolve to the declared fallback;
|
|
113
164
|
* only caller mistakes throw, before any backend call.
|
|
114
165
|
*/
|
|
115
166
|
decide(id: string, input?: Record<string, Json>, options?: DecideOptions): Promise<DecisionResult>;
|
|
116
|
-
/**
|
|
117
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Project a shadow's own result onto {@link ShadowOutcome}. `value` is carried only when the shadow
|
|
169
|
+
* actually answered — a served-model answer or a low-confidence one — never its declared fallback.
|
|
170
|
+
*/
|
|
171
|
+
private toShadowOutcome;
|
|
172
|
+
/**
|
|
173
|
+
* Ask one maker and classify its outcome. `failed` is true exactly for `no-backend`, `timeout`
|
|
174
|
+
* and `error` — the reasons a chain advances past (ADR-040); `accepted` and `low-confidence`
|
|
175
|
+
* answer, so the chain stops there.
|
|
176
|
+
*/
|
|
177
|
+
private askOne;
|
|
178
|
+
/** Maker-chain precedence for discovery results: decision → catalog defaults → hub default. */
|
|
179
|
+
private effectiveChain;
|
|
118
180
|
private mustGet;
|
|
119
181
|
/**
|
|
120
182
|
* Build the declared-fallback envelope: `source: 'default'`, the observed
|
package/dist/hub.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../src/hub.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,IAAI,EAIZ,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,kBAAkB,EAAuB,MAAM,WAAW,CAAC;AAGzE,OAAO,EAAE,KAAK,mBAAmB,EAAE,qBAAqB,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,KAAK,EAER,eAAe,EACf,gBAAgB,EAEhB,aAAa,EACb,YAAY,
|
|
1
|
+
{"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../src/hub.ts"],"names":[],"mappings":"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"}
|