ai-capability-framework 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +38 -30
  3. package/ROADMAP.md +14 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +32 -1
  6. package/dist/discovery/bindings.d.ts +7 -0
  7. package/dist/discovery/bindings.d.ts.map +1 -0
  8. package/dist/discovery/bindings.js +24 -0
  9. package/dist/discovery/budget.d.ts +17 -0
  10. package/dist/discovery/budget.d.ts.map +1 -0
  11. package/dist/discovery/budget.js +77 -0
  12. package/dist/discovery/configuration.d.ts +10 -0
  13. package/dist/discovery/configuration.d.ts.map +1 -0
  14. package/dist/discovery/configuration.js +63 -0
  15. package/dist/discovery/corpus.d.ts +8 -0
  16. package/dist/discovery/corpus.d.ts.map +1 -0
  17. package/dist/discovery/corpus.js +64 -0
  18. package/dist/discovery/diagnostics.d.ts +15 -0
  19. package/dist/discovery/diagnostics.d.ts.map +1 -0
  20. package/dist/discovery/diagnostics.js +11 -0
  21. package/dist/discovery/discoverer.d.ts +23 -0
  22. package/dist/discovery/discoverer.d.ts.map +1 -0
  23. package/dist/discovery/discoverer.js +261 -0
  24. package/dist/discovery/embedding-configuration.d.ts +12 -0
  25. package/dist/discovery/embedding-configuration.d.ts.map +1 -0
  26. package/dist/discovery/embedding-configuration.js +81 -0
  27. package/dist/discovery/embedding-types.d.ts +75 -0
  28. package/dist/discovery/embedding-types.d.ts.map +1 -0
  29. package/dist/discovery/embedding-types.js +1 -0
  30. package/dist/discovery/embeddings.d.ts +38 -0
  31. package/dist/discovery/embeddings.d.ts.map +1 -0
  32. package/dist/discovery/embeddings.js +71 -0
  33. package/dist/discovery/evaluation-validation.d.ts +8 -0
  34. package/dist/discovery/evaluation-validation.d.ts.map +1 -0
  35. package/dist/discovery/evaluation-validation.js +19 -0
  36. package/dist/discovery/evaluation.d.ts +84 -0
  37. package/dist/discovery/evaluation.d.ts.map +1 -0
  38. package/dist/discovery/evaluation.js +310 -0
  39. package/dist/discovery/export.d.ts +14 -0
  40. package/dist/discovery/export.d.ts.map +1 -0
  41. package/dist/discovery/export.js +81 -0
  42. package/dist/discovery/facade.d.ts +82 -0
  43. package/dist/discovery/facade.d.ts.map +1 -0
  44. package/dist/discovery/facade.js +266 -0
  45. package/dist/discovery/fingerprint.d.ts +71 -0
  46. package/dist/discovery/fingerprint.d.ts.map +1 -0
  47. package/dist/discovery/fingerprint.js +67 -0
  48. package/dist/discovery/index-builder.d.ts +3 -0
  49. package/dist/discovery/index-builder.d.ts.map +1 -0
  50. package/dist/discovery/index-builder.js +129 -0
  51. package/dist/discovery/index-validation.d.ts +12 -0
  52. package/dist/discovery/index-validation.d.ts.map +1 -0
  53. package/dist/discovery/index-validation.js +117 -0
  54. package/dist/discovery/index.d.ts +32 -0
  55. package/dist/discovery/index.d.ts.map +1 -0
  56. package/dist/discovery/index.js +18 -0
  57. package/dist/discovery/json-snapshot.d.ts +3 -0
  58. package/dist/discovery/json-snapshot.d.ts.map +1 -0
  59. package/dist/discovery/json-snapshot.js +11 -0
  60. package/dist/discovery/lexical.d.ts +12 -0
  61. package/dist/discovery/lexical.d.ts.map +1 -0
  62. package/dist/discovery/lexical.js +26 -0
  63. package/dist/discovery/metadata.d.ts +4 -0
  64. package/dist/discovery/metadata.d.ts.map +1 -0
  65. package/dist/discovery/metadata.js +33 -0
  66. package/dist/discovery/ranking.d.ts +7 -0
  67. package/dist/discovery/ranking.d.ts.map +1 -0
  68. package/dist/discovery/ranking.js +68 -0
  69. package/dist/discovery/replay.d.ts +50 -0
  70. package/dist/discovery/replay.d.ts.map +1 -0
  71. package/dist/discovery/replay.js +175 -0
  72. package/dist/discovery/retrieval-policy.d.ts +5 -0
  73. package/dist/discovery/retrieval-policy.d.ts.map +1 -0
  74. package/dist/discovery/retrieval-policy.js +10 -0
  75. package/dist/discovery/router.d.ts +39 -0
  76. package/dist/discovery/router.d.ts.map +1 -0
  77. package/dist/discovery/router.js +98 -0
  78. package/dist/discovery/selection-attempt.d.ts +20 -0
  79. package/dist/discovery/selection-attempt.d.ts.map +1 -0
  80. package/dist/discovery/selection-attempt.js +39 -0
  81. package/dist/discovery/selection-cards.d.ts +12 -0
  82. package/dist/discovery/selection-cards.d.ts.map +1 -0
  83. package/dist/discovery/selection-cards.js +79 -0
  84. package/dist/discovery/selection-configuration.d.ts +18 -0
  85. package/dist/discovery/selection-configuration.d.ts.map +1 -0
  86. package/dist/discovery/selection-configuration.js +71 -0
  87. package/dist/discovery/selection-response.d.ts +13 -0
  88. package/dist/discovery/selection-response.d.ts.map +1 -0
  89. package/dist/discovery/selection-response.js +47 -0
  90. package/dist/discovery/selection-types.d.ts +18 -0
  91. package/dist/discovery/selection-types.d.ts.map +1 -0
  92. package/dist/discovery/selection-types.js +1 -0
  93. package/dist/discovery/selection.d.ts +6 -0
  94. package/dist/discovery/selection.d.ts.map +1 -0
  95. package/dist/discovery/selection.js +145 -0
  96. package/dist/discovery/types.d.ts +291 -0
  97. package/dist/discovery/types.d.ts.map +1 -0
  98. package/dist/discovery/types.js +1 -0
  99. package/dist/discovery/usage.d.ts +2 -0
  100. package/dist/discovery/usage.d.ts.map +1 -0
  101. package/dist/discovery/usage.js +1 -0
  102. package/dist/discovery/vectors.d.ts +5 -0
  103. package/dist/discovery/vectors.d.ts.map +1 -0
  104. package/dist/discovery/vectors.js +43 -0
  105. package/dist/discovery/versions.d.ts +37 -0
  106. package/dist/discovery/versions.d.ts.map +1 -0
  107. package/dist/discovery/versions.js +36 -0
  108. package/dist/evals-live/live-eval-runner.d.ts +1 -0
  109. package/dist/evals-live/live-eval-runner.d.ts.map +1 -1
  110. package/dist/evals-live/live-eval-runner.js +12 -1
  111. package/dist/evals-live/types.d.ts +3 -0
  112. package/dist/evals-live/types.d.ts.map +1 -1
  113. package/dist/generated/manifest-types.d.ts +3655 -0
  114. package/dist/generated/manifest-types.d.ts.map +1 -1
  115. package/dist/loader.d.ts.map +1 -1
  116. package/dist/loader.js +8 -0
  117. package/dist/mcp-server/server.d.ts.map +1 -1
  118. package/dist/mcp-server/server.js +11 -2
  119. package/dist/mcp-server/types.d.ts +4 -0
  120. package/dist/mcp-server/types.d.ts.map +1 -1
  121. package/dist/observability/redaction.d.ts.map +1 -1
  122. package/dist/observability/redaction.js +6 -2
  123. package/dist/observability/types.d.ts +1 -1
  124. package/dist/observability/types.d.ts.map +1 -1
  125. package/dist/openai/index.d.ts +2 -0
  126. package/dist/openai/index.d.ts.map +1 -1
  127. package/dist/openai/index.js +1 -0
  128. package/dist/openai/responses-runner.d.ts +1 -1
  129. package/dist/openai/responses-runner.d.ts.map +1 -1
  130. package/dist/openai/responses-runner.js +134 -7
  131. package/dist/openai/selection-client.d.ts +28 -0
  132. package/dist/openai/selection-client.d.ts.map +1 -0
  133. package/dist/openai/selection-client.js +95 -0
  134. package/dist/openai/tool-loop.js +1 -1
  135. package/dist/openai/types.d.ts +19 -2
  136. package/dist/openai/types.d.ts.map +1 -1
  137. package/dist/replay/discovery-snapshot.d.ts +10 -0
  138. package/dist/replay/discovery-snapshot.d.ts.map +1 -0
  139. package/dist/replay/discovery-snapshot.js +17 -0
  140. package/dist/replay/recorder.d.ts.map +1 -1
  141. package/dist/replay/recorder.js +4 -0
  142. package/dist/replay/redaction.js +5 -1
  143. package/dist/replay/replayer.d.ts.map +1 -1
  144. package/dist/replay/replayer.js +14 -1
  145. package/dist/replay/types.d.ts +3 -0
  146. package/dist/replay/types.d.ts.map +1 -1
  147. package/dist/replay/validation.d.ts.map +1 -1
  148. package/dist/replay/validation.js +6 -0
  149. package/dist/runtime/bounded-json.d.ts +4 -0
  150. package/dist/runtime/bounded-json.d.ts.map +1 -0
  151. package/dist/runtime/bounded-json.js +74 -0
  152. package/dist/runtime/eligibility.d.ts +4 -0
  153. package/dist/runtime/eligibility.d.ts.map +1 -0
  154. package/dist/runtime/eligibility.js +128 -0
  155. package/dist/runtime/index.d.ts +5 -1
  156. package/dist/runtime/index.d.ts.map +1 -1
  157. package/dist/runtime/index.js +2 -0
  158. package/dist/runtime/provider-attempt.d.ts +11 -0
  159. package/dist/runtime/provider-attempt.d.ts.map +1 -0
  160. package/dist/runtime/provider-attempt.js +221 -0
  161. package/dist/runtime/provider-run.d.ts +10 -0
  162. package/dist/runtime/provider-run.d.ts.map +1 -0
  163. package/dist/runtime/provider-run.js +123 -0
  164. package/dist/runtime/provider-stop.d.ts +9 -0
  165. package/dist/runtime/provider-stop.d.ts.map +1 -0
  166. package/dist/runtime/provider-stop.js +28 -0
  167. package/dist/runtime/provider-types.d.ts +189 -0
  168. package/dist/runtime/provider-types.d.ts.map +1 -0
  169. package/dist/runtime/provider-types.js +1 -0
  170. package/dist/runtime/provider-usage.d.ts +9 -0
  171. package/dist/runtime/provider-usage.d.ts.map +1 -0
  172. package/dist/runtime/provider-usage.js +69 -0
  173. package/dist/runtime/router.d.ts.map +1 -1
  174. package/dist/runtime/router.js +3 -118
  175. package/dist/runtime/types.d.ts +9 -0
  176. package/dist/runtime/types.d.ts.map +1 -1
  177. package/dist/types.d.ts +1 -1
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/validator.d.ts +3 -0
  180. package/dist/validator.d.ts.map +1 -1
  181. package/dist/validator.js +32 -0
  182. package/docs/api/public-api-policy.md +10 -0
  183. package/docs/api/stable-workflow.md +58 -0
  184. package/docs/api.md +7 -0
  185. package/docs/capability-manifests.md +24 -0
  186. package/docs/development.md +22 -0
  187. package/docs/discovery.md +186 -0
  188. package/docs/evals/discovery-live.md +199 -0
  189. package/docs/evals/discovery-qualification.md +158 -0
  190. package/docs/evals/overview.md +9 -0
  191. package/docs/getting-started/installation.md +11 -12
  192. package/docs/getting-started/openai-quickstart.md +16 -5
  193. package/docs/getting-started/provider-neutral-quickstart.md +247 -88
  194. package/docs/getting-started/quickstart.md +8 -30
  195. package/docs/index.md +10 -0
  196. package/docs/release.md +29 -5
  197. package/docs/runtime.md +27 -0
  198. package/docs/start-here.md +23 -134
  199. package/examples/12-capability-discovery-selection/README.md +346 -0
  200. package/examples/12-capability-discovery-selection/customer-cases.mjs +52 -0
  201. package/examples/12-capability-discovery-selection/customer-harness.mjs +91 -0
  202. package/examples/12-capability-discovery-selection/customer-request-example.mjs +320 -0
  203. package/examples/12-capability-discovery-selection/discovery-example.mjs +175 -0
  204. package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.suite.json +7887 -0
  205. package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v2.suite.json +8167 -0
  206. package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v3.suite.json +8342 -0
  207. package/examples/12-capability-discovery-selection/evaluation/application.mjs +305 -0
  208. package/examples/12-capability-discovery-selection/evaluation/campaign.mjs +248 -0
  209. package/examples/12-capability-discovery-selection/evaluation/clients.mjs +433 -0
  210. package/examples/12-capability-discovery-selection/evaluation/continuation.mjs +208 -0
  211. package/examples/12-capability-discovery-selection/evaluation/durable-budget.mjs +420 -0
  212. package/examples/12-capability-discovery-selection/evaluation/freeze.mjs +9 -0
  213. package/examples/12-capability-discovery-selection/evaluation/harness.mjs +618 -0
  214. package/examples/12-capability-discovery-selection/evaluation/mock-host.mjs +260 -0
  215. package/examples/12-capability-discovery-selection/evaluation/preflight.mjs +115 -0
  216. package/examples/12-capability-discovery-selection/evaluation/qualification-workflows.mjs +87 -0
  217. package/examples/12-capability-discovery-selection/evaluation/regrade.mjs +281 -0
  218. package/examples/12-capability-discovery-selection/evaluation/report.mjs +517 -0
  219. package/examples/12-capability-discovery-selection/evaluation/runner.mjs +613 -0
  220. package/examples/12-capability-discovery-selection/evaluation/saved-campaign.mjs +385 -0
  221. package/examples/12-capability-discovery-selection/evaluation/wording.mjs +115 -0
  222. package/examples/12-capability-discovery-selection/evaluation/workflow-application.mjs +378 -0
  223. package/examples/12-capability-discovery-selection/evaluation/workflow-cases.mjs +90 -0
  224. package/examples/12-capability-discovery-selection/evaluation-example.mjs +154 -0
  225. package/examples/12-capability-discovery-selection/hybrid-example.mjs +174 -0
  226. package/examples/12-capability-discovery-selection/mock-embeddings.mjs +101 -0
  227. package/examples/12-capability-discovery-selection/mock-selection.mjs +78 -0
  228. package/examples/12-capability-discovery-selection/pipeline-example.mjs +496 -0
  229. package/examples/12-capability-discovery-selection/retrieval-cases.mjs +31 -0
  230. package/examples/12-capability-discovery-selection/run-campaign-mock.mjs +40 -0
  231. package/examples/12-capability-discovery-selection/run-customer-mock.mjs +15 -0
  232. package/examples/12-capability-discovery-selection/run-evaluation-live.mjs +10 -0
  233. package/examples/12-capability-discovery-selection/run-evaluation-mock.mjs +3 -0
  234. package/examples/12-capability-discovery-selection/run-evaluation-regrade.mjs +71 -0
  235. package/examples/12-capability-discovery-selection/run-hybrid-mock.mjs +3 -0
  236. package/examples/12-capability-discovery-selection/run-mock.mjs +3 -0
  237. package/examples/12-capability-discovery-selection/run-pipeline-mock.mjs +2 -0
  238. package/examples/12-capability-discovery-selection/run-selector-mock.mjs +3 -0
  239. package/examples/12-capability-discovery-selection/selection-example.mjs +88 -0
  240. package/package.json +17 -4
  241. package/schemas/discovery/embedding-index.schema.json +71 -0
  242. package/schemas/discovery/eval-observations.schema.json +409 -0
  243. package/schemas/discovery/eval-suite.schema.json +375 -0
  244. package/schemas/discovery/metadata.schema.json +30 -0
  245. package/schemas/discovery/selection-response.schema.json +47 -0
  246. package/schemas/replay/discovery-snapshot.schema.json +335 -0
@@ -0,0 +1,346 @@
1
+ # From a customer request to a tool
2
+
3
+ Install `ai-capability-framework` (stable 1.1.0 or later). Lexical/native is the
4
+ recommended default. Hybrid is experimental and opt-in; the
5
+ [qualification report](../../docs/evals/discovery-qualification.md) explains its limits.
6
+
7
+ The application AI produces tasks. Application code searches for relevant capabilities,
8
+ then its existing model chooses a tool from the shortlist. The runtime validates the
9
+ call and enforces execution and approval policy.
10
+
11
+ Run the complete no-key example from the repository root:
12
+
13
+ ```sh
14
+ npm run build
15
+ node examples/12-capability-discovery-selection/run-customer-mock.mjs
16
+ ```
17
+
18
+ The customer asks to read a ticket and prepare a refund for its order. A mocked planning
19
+ call returns two dependent tasks. Application code searches each task when ready, passes
20
+ the authorized read result into the next task's context, and uses ordinary model tool
21
+ calls to read the ticket and prepare a refund preview. The example's host policy
22
+ requires operator review. It stops with approval pending and never exposes or executes
23
+ refund commit.
24
+
25
+ The discovery integration needs only the registry, tasks, and existing runtime context:
26
+
27
+ ```js
28
+ import { createCapabilityDiscovery } from "ai-capability-framework/discovery";
29
+
30
+ const discovery = createCapabilityDiscovery({ registry });
31
+ const [result] = await discovery.search({
32
+ tasks: [{ id: "read-ticket", text: "Read ticket TCK-100." }],
33
+ runtimeContext
34
+ });
35
+ const router = discovery.routerFor(result);
36
+ // Pass router, the same runtimeContext, and the exact task text to your ordinary runner.
37
+ ```
38
+
39
+ The default profile uses lexical retrieval, up to eight candidates, no alias bonus or
40
+ diversity, and a 30-second discovery deadline. An earlier caller deadline wins. Search
41
+ accepts controls, existing eligibility filters and permission overrides, cancellation,
42
+ and a custom context builder. Batch results retain task IDs and input order. Use the
43
+ low-level discoverer for multiple fragments or advanced ranking.
44
+
45
+ `routerFor` reuses the issued shortlist without ranking or embedding again. Results are
46
+ immutable and bound to their issuing instance, original task text, runtime context, and
47
+ source. Keep the result in the same request; reconstructed or foreign results are
48
+ rejected. Changed source, identity, permissions, controls, cancellation, or expiry
49
+ cannot export a stale shortlist. Prepare dependent context before searching. Provider
50
+ export and execution retain their ordinary checks.
51
+
52
+ The example also runs no-match clarification, permission denial, and an explicitly
53
+ invoked mock hybrid companion. Planning and ordinary model usage are reported
54
+ separately: one planning turn and four ordinary turns, all synthetic. Lexical discovery
55
+ uses no provider calls. The hybrid companion uses one explicit index-build batch and one
56
+ query attempt; invalid-index rejection purchases no query embedding. No dedicated
57
+ selector runs.
58
+
59
+ ## Optional semantic search
60
+
61
+ Enable hybrid mode explicitly, using your configured embedding client and an index built
62
+ in a separate administrative operation:
63
+
64
+ ```js
65
+ import { createInMemoryDiscoveryBudget } from "ai-capability-framework/discovery";
66
+
67
+ const budget = createInMemoryDiscoveryBudget({
68
+ runId: runtimeContext.runId,
69
+ maxProviderCalls: 8,
70
+ maxInputTokens: 32000
71
+ });
72
+ const discovery = createCapabilityDiscovery({
73
+ registry,
74
+ mode: "hybrid",
75
+ embedding: { client: embeddingClient, index, maxInputTokens: 4000 }
76
+ });
77
+ const results = await discovery.search({ tasks, runtimeContext, budget });
78
+ ```
79
+
80
+ The standard hybrid starting weights are 0.75 semantic and 0.25 lexical. Failures return
81
+ unavailable unless lexical fallback is explicitly configured. Override `weights`,
82
+ `failurePolicy`, and `limits` on the facade as needed. Embedding options allow
83
+ overriding artifact bounds (16 MiB, 10,000 entries, 4,096 dimensions) and provider
84
+ character bounds (16,384 per text, 65,536 per call). The caller supplies the token
85
+ ceiling. Token enforcement and retry disabling remain embedding-client responsibilities.
86
+ Indexes must still match the exact source and embedding configuration; serving never
87
+ builds or repairs them.
88
+
89
+ The in-memory helper enforces a finite single-run budget in one process. A supplied
90
+ `maxEstimatedCost` also requires `embedding.maxEstimatedCostPerCall`; the framework
91
+ assumes no prices. Unknown consumption retains reserved allowance. Late completion
92
+ settles its reservation without restoring a cancelled result. An unresolved attempt
93
+ retains its reservation for the lifetime of the helper.
94
+
95
+ For distributed or durable enforcement, pass `budget: { ledger, policy }` with your own
96
+ ledger and existing budget policy. Reuse the scope across discovery calls in a run. Use
97
+ the shared-run companion below to account for managed planning and downstream model
98
+ calls; this example reports those stages separately. No default credentials, model
99
+ service, or live transport is supplied.
100
+
101
+ These mocks demonstrate contracts and integration. They do not establish real semantic
102
+ quality or guarantee that the first candidate is the correct tool. Applications own
103
+ decomposition, task dependencies, argument completion, and customer responses. A model
104
+ may choose a candidate or request clarification; scores never authorize execution.
105
+
106
+ ## Optional dedicated selection
107
+
108
+ Use the ordinary model/tool loop for the standard flow above. If your application needs
109
+ a separate model decision, explicitly call the same discovery instance's `select`
110
+ method:
111
+
112
+ ```js
113
+ const batch = await discovery.select({
114
+ results,
115
+ client: selectionClient,
116
+ budget,
117
+ maxInputTokens: 4000,
118
+ maxOutputTokens: 1000
119
+ });
120
+ ```
121
+
122
+ The framework recovers task text and context bindings from its own search results and
123
+ constructs candidate cards. Supply a configured `SelectionModelClient` with an explicit
124
+ `mock` marker and `generateStructured` method; input/output token ceilings are passed to
125
+ that client. Use the existing budget helper or your own `{ ledger, policy }` scope. Cost
126
+ caps require `maxEstimatedCostPerCall`, with optional host cost estimation. There is no
127
+ default model, account, price, or credential lookup.
128
+
129
+ `batch.outcomes` contains ordered selected/abstained decisions or safe failure outcomes
130
+ for matched tasks. Non-matched tasks stay in `batch.discovery` and are never represented
131
+ as model abstentions. `batch.usage` owns selection and repair usage once for the whole
132
+ batch; discovery usage remains in the original discovery results. Individual outcomes
133
+ carry no duplicate provider-usage receipt.
134
+
135
+ Cards preserve complete model descriptions, usage/exclusion guidance, input-field names,
136
+ and read/prepare distinctions. Scores, full schemas, runtime identities, hidden policy,
137
+ and paths are omitted. Optional `contextByQueryId` explicitly projects application data
138
+ for the model. Foreign/reconstructed results and incompatible invocation scopes are
139
+ rejected. Current source, eligibility, identity, controls, cancellation, and original
140
+ deadlines are rechecked; a new deadline can shorten the original bound.
141
+
142
+ Defaults are eight tasks, 32,000 serialized input characters, and one attempt. Override
143
+ `limits: { maxTasks, maxInputCharacters, maxAttempts }` up to 32 tasks, 128,000
144
+ characters, and two attempts. The same character ceiling bounds response parsing. With
145
+ two attempts, one repair may use safe issue codes and the original cards. Oversized
146
+ batches/cards are rejected without clipping or paid splitting. Provider, accounting,
147
+ stale-binding, budget, control, cancellation, and deadline failures cause no repair or
148
+ automatic candidate choice.
149
+
150
+ Advanced callers can pass the same low-level request to `buildCapabilitySelectionTasks`
151
+ and `ModelCapabilitySelector.select`; the helper derives its hashes from that request's
152
+ configured client and limits, without a provider call or manual fingerprint
153
+ construction.
154
+
155
+ The selector returns decisions; application argument completion and runtime execution
156
+ remain separate. Native search and `routerFor` never call the dedicated selector.
157
+ Selected-result routers, the optional Responses adapter, and managed full-run accounting
158
+ are available; see the shared-run integration below. Imports use the public discovery
159
+ subpath; the [named lexical/native workflow](../../docs/api/stable-workflow.md) is
160
+ stable.
161
+
162
+ Run the explicit selector companion:
163
+
164
+ ```sh
165
+ npm run build
166
+ node examples/12-capability-discovery-selection/run-selector-mock.mjs
167
+ ```
168
+
169
+ It demonstrates competition, single-candidate abstention, a skipped no-match task, and
170
+ one enabled repair. Across two explicit invocations it reports three mock provider
171
+ attempts and 420 synthetic total tokens, with complete reported usage. It executes no
172
+ tools and makes no embedding or live model calls. The fixture establishes contracts, not
173
+ model quality.
174
+
175
+ ## Advanced lexical example
176
+
177
+ Run from the repository root:
178
+
179
+ ```sh
180
+ npm run build
181
+ node examples/12-capability-discovery-selection/run-mock.mjs
182
+ ```
183
+
184
+ This example reuses the synthetic support manifests. Discovery returns independent
185
+ shortlists for four tasks, then the router turns one request into a bounded runtime
186
+ slice. Existing provider export checks produce the tool descriptors. A mocked ordinary
187
+ model/tool loop chooses the ticket lookup, executes the existing authorized runtime
188
+ handler, and returns an answer.
189
+
190
+ Discovery makes zero provider calls and reports complete zero token and cost totals. The
191
+ two ordinary mock model turns run locally. No key, network connection, embedding client,
192
+ or dedicated selector is used.
193
+
194
+ Imports use supported package subpaths. The executable retrieval fixtures include
195
+ compatible and forbidden capabilities without adding manifests to the normal inventory.
196
+
197
+ ## Hybrid mock
198
+
199
+ Run the explicit index build and hybrid example:
200
+
201
+ ```sh
202
+ npm run build
203
+ node examples/12-capability-discovery-selection/run-hybrid-mock.mjs
204
+ ```
205
+
206
+ This flow builds deterministic synthetic vectors, validates the complete index, and
207
+ round-trips it through host-owned memory storage. It demonstrates a semantic match with
208
+ zero lexical evidence, an invalid-index rejection before any query embedding, and
209
+ explicit lexical fallback. The ordinary mocked model/tool loop still performs tool
210
+ selection and an authorized read.
211
+
212
+ The flow records three logical mock embedding attempts: one document batch and two
213
+ independent query calls, including the query made by the ordinary runtime loop. All
214
+ embedding usage is labeled mock. These vectors provide contract evidence only. No live
215
+ embedding service or dedicated selector is used.
216
+
217
+ Hybrid callers supply an embedding client, complete model configuration, an index,
218
+ artifact and input limits, an absolute deadline, and a shared reservation ledger. Each
219
+ task joins its original fragment texts with newlines and makes at most one embedding
220
+ attempt, sequentially. The native-router wrapper forwards the caller's ledger and
221
+ deadline without changing ordinary runtime runners.
222
+
223
+ Storage implementations must atomically replace complete validated artifacts. Serving
224
+ does not load or rebuild an index implicitly. Stale source, model, revision, dimension,
225
+ or provenance bindings require explicit rebuilding or a configured whole-query lexical
226
+ fallback. An absent model revision does not establish an immutable embedding space.
227
+
228
+ Cancellation returns a fixed usage snapshot. If a provider attempt remains pending, its
229
+ reservation stays held until its observed completion settles it; host recovery handles
230
+ attempts that never finish. Clients enforce token ceilings and disable hidden retries.
231
+ Unreported usage and cost remain unknown.
232
+
233
+ ## Shared-run integration
234
+
235
+ Run `node examples/12-capability-discovery-selection/run-pipeline-mock.mjs` after
236
+ `npm run build`. The companion runs lexical/native, lexical/dedicated, hybrid/native,
237
+ and hybrid/dedicated paths through the existing tool loop. Each reads a ticket, carries
238
+ its order ID into refund preparation, and produces a preview requiring approval. It
239
+ makes no real provider calls.
240
+
241
+ Create one optional `createAicfProviderRun` with the existing runtime run ID, a
242
+ reservation budget, and an absolute deadline. Pass it to `search`; facade selection and
243
+ result-bound routers recover that scope. The ordinary Responses runner accepts
244
+ `providerExecution` with explicit model identity, provenance, input/output token
245
+ ceilings, and an application-supplied complete-input token preflight. Continuation
246
+ history is supplied to that preflight. No tokenizer, pricing, credentials, or model
247
+ defaults are discovered.
248
+
249
+ Dedicated selection remains optional:
250
+
251
+ ```js
252
+ const batch = await discovery.select({
253
+ results,
254
+ client: selectionClient,
255
+ maxInputTokens: 4000,
256
+ maxOutputTokens: 1000
257
+ });
258
+ const router = discovery.routerFor(batch, task.id);
259
+ ```
260
+
261
+ Here `results` came from a search using `providerRun`. Without a run, continue supplying
262
+ the existing explicit `budget`. A batch can select several independent tasks, but each
263
+ router exports only its own task's decision. Revalidation repeats neither ranking nor
264
+ provider work. The ordinary model still supplies arguments; normal policy, validation,
265
+ approval, and execution remain in force.
266
+
267
+ `createOpenAISelectionClient` wraps an existing Responses-compatible client and requires
268
+ a complete-input token preflight. It forwards cancellation and timeout, disables
269
+ transport retries, and uses a versioned strict structured-output projection. Embeddings
270
+ continue to use the caller's supplied client and separately built compatible index.
271
+
272
+ `providerRun.attempt` lets application planning or response calls share the same ledger.
273
+ Only calls made through the managed boundary are counted. The in-memory budget
274
+ optionally limits output tokens and retains allowance for unknown usage; use a host
275
+ ledger for distributed or durable enforcement. The runner preserves its legacy `usage`
276
+ field and adds an all-turn `providerUsage` receipt for managed turns. The run snapshot
277
+ combines unique attempts once across stages. Missing usage/cost remains unknown; mock
278
+ zero cost in this example is explicitly supplied.
279
+
280
+ New trace events contain bounded metadata and numeric receipts, including under
281
+ content-capture settings. Run-local retention stops at 256 events and reports overflow.
282
+ Hosts own trace retention and access. Hashes are not anonymization.
283
+
284
+ `createDiscoverySnapshot` creates a bounded metadata snapshot for the existing replay
285
+ extension. Snapshot validation checks bindings and membership; ranking replay
286
+ additionally requires original query fixtures and, for hybrid retrieval, compatible host
287
+ vector fixtures. It never calls a provider. Neither operation establishes model semantic
288
+ correctness. Existing `router_only` replay requires an explicit discovery handler for
289
+ these snapshots. Live replay remains separately enabled and budgeted.
290
+
291
+ The companion reports five, seven, seven, and nine customer-run mock attempts
292
+ respectively. Administrative index-build usage is reported separately. Failure cases
293
+ cover clarification, abstention, permission/source revocation, budget exhaustion, and
294
+ invalid-index rejection without query embedding. Synthetic responses and vectors
295
+ establish integration behavior; they do not qualify retrieval or selection quality.
296
+
297
+ ## Installed package and evaluation
298
+
299
+ Fake data: all requests, capabilities, identities, and outputs are synthetic. No secrets
300
+ are needed. No live provider calls occur.
301
+
302
+ Command from an installed consumer directory:
303
+
304
+ ```sh
305
+ node node_modules/ai-capability-framework/examples/12-capability-discovery-selection/run-customer-mock.mjs
306
+ ```
307
+
308
+ Expected output: one planning turn, four ordinary turns, one authorized read, one
309
+ approval-required preparation, and zero commits. The separately invoked hybrid companion
310
+ uses two mock embedding attempts. All five commands use supported public imports and
311
+ resolve manifests relative to their own modules, independent of the working directory.
312
+
313
+ Run `npm run eval:discovery:mock` from the repository, or run the packaged
314
+ `run-evaluation-mock.mjs` in the same way, for the frozen 240-case lexical baseline. The
315
+ suite has three domains, 120 train cases, 60 calibration cases, and 60 held-out cases,
316
+ with scenario families confined to one partition. Independently scripted selection
317
+ observations demonstrate selected, abstained, unavailable, and invalid-output outcomes.
318
+ Their accounting is explicitly incomplete because no provider attempts were observed. No
319
+ selector is invoked by the evaluator.
320
+
321
+ The baseline retains retrieval misses and false positives; its rates are informational.
322
+ Do not tune against the frozen held-out set. These fixtures do not qualify live models.
323
+ See the [discovery guide](../../docs/discovery.md) for defaults, ownership, limits,
324
+ budgets, replay boundaries, and evaluation metrics.
325
+
326
+ ## Optional live evaluation campaign
327
+
328
+ The
329
+ [offline regrading guide](../../docs/evals/discovery-live.md#regrade-saved-wording-offline)
330
+ shows how to reassess saved wording without model access. It preserves frozen results,
331
+ observed delivery and historical accounting, and writes a separate versioned report.
332
+
333
+ `npm run eval:discovery:live` validates the corrected v3 suite and fixed-eight-candidate
334
+ schedule with zero provider calls. `npm run eval:discovery:campaign:mock` exercises the
335
+ complete campaign, durable accounting, three held-out repetitions, and 48 workflow runs
336
+ with deterministic clients. It writes evidence under ignored `.aicf/` and makes no
337
+ quality claim about real models.
338
+
339
+ Paid execution requires `--live`, an explicit host configuration, compatible model
340
+ clients, token preflights, matching offline acceptance evidence, and a pinned
341
+ continuation of the original campaign budget. This command performs no paid work by
342
+ default. The application owns response facts and validates model wording with a recorded
343
+ deterministic fallback. Plans, terminal task outcomes and component grades are retained
344
+ in ignored evidence. The original suite and all existing companions are preserved. See
345
+ the [evaluation guide](../../docs/evals/discovery-live.md) for stages, resume behavior,
346
+ limits, and interpretation.
@@ -0,0 +1,52 @@
1
+ // Application-owned planning fixtures; these are not additional manifests.
2
+ export const customerRequest =
3
+ "Read ticket TCK-100 and prepare a refund for its order.";
4
+ export const plannedTasks = [
5
+ {
6
+ id: "read-ticket",
7
+ text: "Read ticket TCK-100 for the refund review.",
8
+ dependsOn: []
9
+ },
10
+ {
11
+ id: "prepare-refund",
12
+ text: "Prepare a refund case for the order from support ticket TCK-100.",
13
+ dependsOn: ["read-ticket"]
14
+ }
15
+ ];
16
+ export const taskExpectations = {
17
+ "read-ticket": {
18
+ compatibleIds: ["support.ticket.get"],
19
+ forbiddenIds: ["support.refund.commit_case"]
20
+ },
21
+ "prepare-refund": {
22
+ compatibleIds: ["support.refund.prepare_case"],
23
+ forbiddenIds: ["support.refund.commit_case"]
24
+ }
25
+ };
26
+
27
+ /** Example application validation, deliberately separate from framework discovery. */
28
+ export function validatePlannedTasks(value) {
29
+ if (!Array.isArray(value) || !value.length || value.length > 8)
30
+ throw new Error("Invalid application task plan.");
31
+ const known = new Set();
32
+ return value.map((task) => {
33
+ if (
34
+ !task ||
35
+ typeof task !== "object" ||
36
+ Object.keys(task).some((key) => !["id", "text", "dependsOn"].includes(key)) ||
37
+ typeof task.id !== "string" ||
38
+ !task.id.trim() ||
39
+ known.has(task.id) ||
40
+ typeof task.text !== "string" ||
41
+ !task.text.trim() ||
42
+ task.text.length > 4000 ||
43
+ !Array.isArray(task.dependsOn) ||
44
+ task.dependsOn.some((id) => !known.has(id)) ||
45
+ new Set(task.dependsOn).size !== task.dependsOn.length
46
+ ) {
47
+ throw new Error("Invalid application task plan.");
48
+ }
49
+ known.add(task.id);
50
+ return { id: task.id, text: task.text, dependsOn: [...task.dependsOn] };
51
+ });
52
+ }
@@ -0,0 +1,91 @@
1
+ import {
2
+ AicfActionLifecycleManager,
3
+ AicfHandlerRegistry,
4
+ AicfToolExecutor,
5
+ DefaultPolicyBroker,
6
+ InMemoryApprovalStore,
7
+ InMemoryAuditSink,
8
+ InMemoryIdempotencyStore,
9
+ InMemoryPreparedActionStore
10
+ } from "ai-capability-framework/runtime";
11
+ import { createDiscoveryExample } from "./discovery-example.mjs";
12
+
13
+ // Existing application runtime setup is separate from the discovery integration.
14
+ export async function createCustomerHarness() {
15
+ const { registry, runtimeContext } = await createDiscoveryExample();
16
+ runtimeContext.facts = { "refund.order_not_refundable": false };
17
+ const handlers = new AicfHandlerRegistry({ registry });
18
+ const readCalls = [],
19
+ prepareCalls = [];
20
+ handlers.register({
21
+ capabilityId: "support.ticket.get",
22
+ read: ({ args }) => {
23
+ readCalls.push(args);
24
+ return {
25
+ ticket: { ticket_id: args.ticket_id, order_id: "ORD-100", status: "open" }
26
+ };
27
+ }
28
+ });
29
+ handlers.register({
30
+ capabilityId: "support.refund.prepare_case",
31
+ prepare: ({ args }) => {
32
+ prepareCalls.push(args);
33
+ return {
34
+ data: {
35
+ ticket_id: args.ticket_id,
36
+ order_id: args.order_id,
37
+ reason_code: args.reason_code
38
+ },
39
+ riskTier: "medium",
40
+ summary: "Refund case prepared for review.",
41
+ userMessage: "The refund case is ready for approval."
42
+ };
43
+ }
44
+ });
45
+ const auditSink = new InMemoryAuditSink();
46
+ const policyBroker = new DefaultPolicyBroker({
47
+ hostPolicyHook: (input) =>
48
+ input.operation === "prepare"
49
+ ? {
50
+ status: "approval_required",
51
+ reasons: [],
52
+ requiredApprovals: [
53
+ {
54
+ approvalType: "operator_review",
55
+ reason: "Application policy requires refund review."
56
+ }
57
+ ]
58
+ }
59
+ : null
60
+ });
61
+ const actionLifecycle = new AicfActionLifecycleManager({
62
+ registry,
63
+ handlers,
64
+ auditSink,
65
+ policyBroker,
66
+ approvalStore: new InMemoryApprovalStore(),
67
+ idempotencyStore: new InMemoryIdempotencyStore(),
68
+ preparedActionStore: new InMemoryPreparedActionStore()
69
+ });
70
+ const executor = new AicfToolExecutor({
71
+ registry,
72
+ handlers,
73
+ auditSink,
74
+ policyBroker,
75
+ actionLifecycle
76
+ });
77
+ const executedOperations = [];
78
+ const execute = executor.execute.bind(executor);
79
+ executor.execute = async (input) => {
80
+ executedOperations.push(input.operation);
81
+ return execute(input);
82
+ };
83
+ return {
84
+ registry,
85
+ runtimeContext,
86
+ executor,
87
+ readCalls,
88
+ prepareCalls,
89
+ executedOperations
90
+ };
91
+ }