@gobing-ai/ts-ai-decision 0.5.30 → 0.5.32

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 CHANGED
@@ -1,15 +1,53 @@
1
1
  # @gobing-ai/ts-ai-decision
2
2
 
3
- Declarative YAML **decision catalogs** over the `DecisionMaker` surface of
4
- `@gobing-ai/ts-ai-runner`, served through a fallback-guaranteed **`DecisionHub`** (ADR-033). A
5
- catalog declares named, static decision points whose shape follows TypeSafe AI's Jev question
6
- spec (`type` / `instructions` / `criteria`), with a required `fallback` so every decision always
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
- The package owns three layers: the strict catalog schema and loader (JSON Schema + zod,
10
- `${params.*}` template validation/rendering, load-time consistency checks), the
11
- `DecisionMakerRegistry` (makers picked by name), and the `DecisionHub` (one Jev question per
12
- `decide`, declared fallback on every backend outcome).
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` | built-in, lazy | Apple silicon, Laya model + Python env | 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, channel: 'chat' });
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. `list` and `describe` never construct a maker or
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; the constructor throws for an
86
- unregistered `defaultMaker`). Every throw happens before any backend call.
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 a single
89
- `maker.ask({ state, questions, model })`; `model` resolves decision → catalog default → omitted.
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 three built-ins (`typesafe`,
107
- `fm-local`, `laya-local`) pre-registered lazily, plus anything consumers register (ADR-033):
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('scripted-judge', () => createDecisionMaker({ driver: myDriver }));
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', 'scripted-judge']
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 built-ins
125
- (env/keys/timeouts, or executor/client injection for the local drivers).
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
- `dispose(name?)` releases the resources of already-`resolve`d makers — a `laya-local` maker holds a
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
- The hub picks a maker purely by name — no new driver code per routing:
134
- per-call `options.maker` → the decision's `maker` → the catalog's `defaults.maker` → the hub's
135
- `defaultMaker` (default `typesafe`). Catalog names are checked at `load`; a misspelled per-call
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 { createDecisionHub } from '@gobing-ai/ts-ai-decision';
226
+ import { createAgentDriver } from '@gobing-ai/ts-ai-decision';
227
+ import { createDecisionMaker } from '@gobing-ai/ts-ai-runner';
141
228
 
142
- const hub = await createDecisionHub({
143
- catalogs: ['decisions/support.yaml'],
144
- makers: { 'scripted-judge': () => createDecisionMaker({ driver: myDriver }) },
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
- See `docs/design/ai-decision-catalog.md` for the full field reference.
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
- /** Per-call maker name; wins over the decision's, the catalog's and the hub default. */
79
- readonly maker?: string;
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 (decision or
97
- * `defaults.maker`) throws {@link DecisionCatalogError} and registers
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 by name (per call → decision → catalog defaults → hub default) and
111
- * issues a single `maker.ask`. Backend construction, transport, timeout,
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
- /** Maker-name precedence for discovery results: decision → catalog defaults → hub default. */
117
- private effectiveMakerName;
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":"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"}
1
+ {"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../src/hub.ts"],"names":[],"mappings":"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"}