@kindgi/cli 0.2.0-rc.1 → 0.3.0-rc.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 (152) hide show
  1. package/README.md +12 -5
  2. package/dist/cli.js +4 -3
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/agents.d.ts.map +1 -1
  5. package/dist/commands/agents.js +75 -19
  6. package/dist/commands/agents.js.map +1 -1
  7. package/dist/commands/approval-wait.d.ts +90 -0
  8. package/dist/commands/approval-wait.d.ts.map +1 -0
  9. package/dist/commands/approval-wait.js +154 -0
  10. package/dist/commands/approval-wait.js.map +1 -0
  11. package/dist/commands/artifacts.d.ts.map +1 -1
  12. package/dist/commands/artifacts.js +131 -14
  13. package/dist/commands/artifacts.js.map +1 -1
  14. package/dist/commands/auth.d.ts.map +1 -1
  15. package/dist/commands/auth.js +48 -5
  16. package/dist/commands/auth.js.map +1 -1
  17. package/dist/commands/deploy.js +7 -3
  18. package/dist/commands/deploy.js.map +1 -1
  19. package/dist/commands/deployments.d.ts.map +1 -1
  20. package/dist/commands/deployments.js +36 -13
  21. package/dist/commands/deployments.js.map +1 -1
  22. package/dist/commands/doctor.d.ts +3 -2
  23. package/dist/commands/doctor.d.ts.map +1 -1
  24. package/dist/commands/doctor.js +47 -14
  25. package/dist/commands/doctor.js.map +1 -1
  26. package/dist/commands/eval-runs.d.ts.map +1 -1
  27. package/dist/commands/eval-runs.js +21 -9
  28. package/dist/commands/eval-runs.js.map +1 -1
  29. package/dist/commands/helpers.d.ts +31 -1
  30. package/dist/commands/helpers.d.ts.map +1 -1
  31. package/dist/commands/helpers.js +45 -2
  32. package/dist/commands/helpers.js.map +1 -1
  33. package/dist/commands/init.js +5 -4
  34. package/dist/commands/init.js.map +1 -1
  35. package/dist/commands/mail-triggers.d.ts.map +1 -1
  36. package/dist/commands/mail-triggers.js +27 -3
  37. package/dist/commands/mail-triggers.js.map +1 -1
  38. package/dist/commands/promote.d.ts.map +1 -1
  39. package/dist/commands/promote.js +133 -11
  40. package/dist/commands/promote.js.map +1 -1
  41. package/dist/commands/provenance.d.ts.map +1 -1
  42. package/dist/commands/provenance.js +13 -5
  43. package/dist/commands/provenance.js.map +1 -1
  44. package/dist/commands/providers.d.ts.map +1 -1
  45. package/dist/commands/providers.js +47 -4
  46. package/dist/commands/providers.js.map +1 -1
  47. package/dist/commands/schedules.d.ts.map +1 -1
  48. package/dist/commands/schedules.js +25 -9
  49. package/dist/commands/schedules.js.map +1 -1
  50. package/dist/commands/skills.d.ts +16 -0
  51. package/dist/commands/skills.d.ts.map +1 -1
  52. package/dist/commands/skills.js +122 -36
  53. package/dist/commands/skills.js.map +1 -1
  54. package/dist/commands/webhooks.d.ts.map +1 -1
  55. package/dist/commands/webhooks.js +21 -5
  56. package/dist/commands/webhooks.js.map +1 -1
  57. package/dist/config.d.ts +8 -0
  58. package/dist/config.d.ts.map +1 -1
  59. package/dist/config.js +11 -0
  60. package/dist/config.js.map +1 -1
  61. package/dist/context.d.ts +7 -3
  62. package/dist/context.d.ts.map +1 -1
  63. package/dist/context.js +1 -0
  64. package/dist/context.js.map +1 -1
  65. package/dist/dev/runtime-image.d.ts +1 -1
  66. package/dist/dev/runtime-image.js +1 -1
  67. package/dist/errors.d.ts.map +1 -1
  68. package/dist/errors.js +14 -1
  69. package/dist/errors.js.map +1 -1
  70. package/dist/exports/plan.d.ts.map +1 -1
  71. package/dist/exports/plan.js +50 -0
  72. package/dist/exports/plan.js.map +1 -1
  73. package/dist/init/augment-scaffolder.d.ts +1 -1
  74. package/dist/init/augment-scaffolder.js +9 -8
  75. package/dist/init/augment-scaffolder.js.map +1 -1
  76. package/dist/init/java-augment.d.ts.map +1 -1
  77. package/dist/init/java-augment.js +7 -6
  78. package/dist/init/java-augment.js.map +1 -1
  79. package/dist/init/python-augment.d.ts.map +1 -1
  80. package/dist/init/python-augment.js +7 -6
  81. package/dist/init/python-augment.js.map +1 -1
  82. package/dist/init/scala-augment.js +7 -6
  83. package/dist/init/scala-augment.js.map +1 -1
  84. package/dist/main.d.ts +3 -0
  85. package/dist/main.d.ts.map +1 -1
  86. package/dist/main.js +1 -0
  87. package/dist/main.js.map +1 -1
  88. package/dist/providers/presets/anthropic.json +15 -3
  89. package/dist/providers/presets/azure-openai.json +10 -2
  90. package/dist/providers/presets/bedrock.json +6 -2
  91. package/dist/providers/presets/gemini-api.json +6 -2
  92. package/dist/providers/presets/openai.json +15 -3
  93. package/dist/sdk-skills/kindgi-authoring-agents/SKILL.md +21 -9
  94. package/dist/sdk-skills/kindgi-authoring-flows/SKILL.md +5 -3
  95. package/dist/sdk-skills/kindgi-authoring-guardrails/SKILL.md +202 -68
  96. package/dist/sdk-skills/kindgi-authoring-mcp-servers/SKILL.md +12 -188
  97. package/dist/sdk-skills/kindgi-authoring-mcp-servers/common-mistakes.md +44 -0
  98. package/dist/sdk-skills/kindgi-authoring-mcp-servers/how-it-works.md +41 -0
  99. package/dist/sdk-skills/kindgi-authoring-mcp-servers/other-clients.md +19 -0
  100. package/dist/sdk-skills/kindgi-authoring-mcp-servers/postgres.md +45 -0
  101. package/dist/sdk-skills/kindgi-authoring-mcp-servers/several-servers.md +32 -0
  102. package/dist/sdk-skills/kindgi-authoring-providers/SKILL.md +49 -906
  103. package/dist/sdk-skills/kindgi-authoring-providers/bedrock.md +64 -0
  104. package/dist/sdk-skills/kindgi-authoring-providers/common-mistakes.md +125 -0
  105. package/dist/sdk-skills/kindgi-authoring-providers/keys-and-registration.md +58 -0
  106. package/dist/sdk-skills/kindgi-authoring-providers/kindgi-cloud.md +36 -0
  107. package/dist/sdk-skills/kindgi-authoring-providers/path-a-anthropic.md +216 -0
  108. package/dist/sdk-skills/kindgi-authoring-providers/path-b-openai-compat.md +127 -0
  109. package/dist/sdk-skills/kindgi-authoring-providers/path-c-onnx.md +97 -0
  110. package/dist/sdk-skills/kindgi-authoring-providers/path-d-vertex.md +95 -0
  111. package/dist/sdk-skills/kindgi-authoring-providers/router.md +113 -0
  112. package/dist/sdk-skills/kindgi-authoring-tools/SKILL.md +12 -387
  113. package/dist/sdk-skills/kindgi-authoring-tools/common-mistakes.md +135 -0
  114. package/dist/sdk-skills/kindgi-authoring-tools/declarative-http.md +34 -0
  115. package/dist/sdk-skills/kindgi-authoring-tools/imperative-handler.md +65 -0
  116. package/dist/sdk-skills/kindgi-authoring-tools/iterating-and-testing.md +45 -0
  117. package/dist/sdk-skills/kindgi-authoring-tools/secrets-and-connections.md +79 -0
  118. package/dist/sdk-skills/kindgi-authoring-tools/tool-flags.md +45 -0
  119. package/dist/sdk-skills/kindgi-fix-corrections/SKILL.md +13 -178
  120. package/dist/sdk-skills/kindgi-fix-corrections/make-the-change.md +25 -0
  121. package/dist/sdk-skills/kindgi-fix-corrections/publish-and-promote.md +76 -0
  122. package/dist/sdk-skills/kindgi-fix-corrections/pull-corrections.md +48 -0
  123. package/dist/sdk-skills/kindgi-fix-corrections/worked-example.md +27 -0
  124. package/dist/sdk-skills/kindgi-framework-feedback/SKILL.md +10 -180
  125. package/dist/sdk-skills/kindgi-framework-feedback/common-mistakes.md +19 -0
  126. package/dist/sdk-skills/kindgi-framework-feedback/editing-feedback-file.md +33 -0
  127. package/dist/sdk-skills/kindgi-framework-feedback/fields-and-entry-shape.md +53 -0
  128. package/dist/sdk-skills/kindgi-framework-feedback/hearing-back.md +24 -0
  129. package/dist/sdk-skills/kindgi-framework-feedback/sending.md +50 -0
  130. package/dist/sdk-skills/kindgi-getting-started/SKILL.md +24 -21
  131. package/dist/sdk-skills/kindgi-java-authoring-agents/SKILL.md +12 -4
  132. package/dist/sdk-skills/kindgi-java-authoring-flows/SKILL.md +6 -4
  133. package/dist/sdk-skills/kindgi-java-authoring-guardrails/SKILL.md +33 -23
  134. package/dist/sdk-skills/kindgi-java-authoring-tools/SKILL.md +19 -12
  135. package/dist/sdk-skills/kindgi-java-getting-started/SKILL.md +16 -16
  136. package/dist/sdk-skills/kindgi-python-authoring-agents/SKILL.md +18 -6
  137. package/dist/sdk-skills/kindgi-python-authoring-flows/SKILL.md +6 -4
  138. package/dist/sdk-skills/kindgi-python-authoring-guardrails/SKILL.md +39 -30
  139. package/dist/sdk-skills/kindgi-python-authoring-tools/SKILL.md +19 -9
  140. package/dist/sdk-skills/kindgi-python-getting-started/SKILL.md +24 -21
  141. package/dist/sdk-skills/kindgi-scala-authoring-agents/SKILL.md +12 -4
  142. package/dist/sdk-skills/kindgi-scala-authoring-flows/SKILL.md +6 -4
  143. package/dist/sdk-skills/kindgi-scala-authoring-guardrails/SKILL.md +34 -23
  144. package/dist/sdk-skills/kindgi-scala-authoring-tools/SKILL.md +15 -9
  145. package/dist/sdk-skills/kindgi-scala-getting-started/SKILL.md +16 -16
  146. package/dist/sdk-skills/kindgi-using/SKILL.md +11 -11
  147. package/dist/templates/sample/README.md.tmpl +8 -7
  148. package/dist/templates/sample/agents/echo-agent/index.ts.tmpl +1 -1
  149. package/dist/templates/sample/kindgi.config.ts.tmpl +1 -1
  150. package/dist/templates/sample/package.json.tmpl +1 -1
  151. package/dist/templates/woocommerce/README.md.tmpl +9 -3
  152. package/package.json +13 -13
@@ -1,33 +1,25 @@
1
1
  ---
2
2
  name: kindgi-authoring-providers
3
3
  description: >
4
- Wire an LLM model provider so a Kindgi pack's agents
5
- can actually call a real model. Covers four paths — hosted via
6
- Anthropic native adapter, Gemini on Vertex AI (Google Application
7
- Default Credentials, no API key), hosted via the OpenAI-compat adapter
8
- (works with OpenAI, Groq, self-hosted vLLM and Ollama, and any other
9
- OpenAI-compatible endpoint, a hosted gateway such as OpenRouter
10
- included), and local
11
- via the in-process ONNX adapter — plus the credential flow (in
12
- `kindgi dev` the key lives in Kindgi's own `.kindgi/secrets.env`, set
13
- with `kindgi secrets set`'s no-echo prompt, or in the project's env files
14
- (`.env`, `.env.local`); the Kindgi runtime reads it through the dotenv
15
- secret binding at agent-turn time). One provider row exposes one
16
- connection with one or more models under `metadata.models[]`; the
17
- router picks the (provider, model) tuple per invocation. Load this
18
- when the user asks to "register a provider", "use Claude / GPT /
19
- Groq / Llama / Gemini", "wire Anthropic", "use Vertex", "use Bedrock", "set up an API key for the agent",
20
- "give the pack on Kindgi Cloud its model key",
21
- "register the provider on staging",
22
- or when an agent has been authored but the pack has no provider
23
- registered yet, or another worktree or a teammate's `kindgi dev` has
24
- none. Authoring agents themselves is covered by
25
- kindgi-authoring-agents; getting-started is covered by
26
- kindgi-getting-started.
4
+ Wire an LLM model provider so a Kindgi pack's agents call a real model:
5
+ Anthropic, the OpenAI-compatible adapter (OpenAI, Groq, OpenRouter,
6
+ vLLM, Ollama, any OpenAI-compatible endpoint), Gemini on Vertex AI
7
+ (Application Default Credentials, no key), Amazon Bedrock, or a local
8
+ in-process ONNX model. Covers where the key lives (`kindgi secrets
9
+ set`'s no-echo prompt, or the project's `.env` files, read at turn
10
+ time), one provider row with several models under `metadata.models[]`,
11
+ and how the router picks a (provider, model) per turn. Load this when
12
+ the user asks to "register a provider", "use Claude / GPT / Groq / Llama
13
+ / Gemini", "wire Anthropic", "use Vertex", "use Bedrock", "set up an API
14
+ key for the agent", "give the pack on Kindgi Cloud its model key",
15
+ "register the provider on staging", or when an agent's replies come from
16
+ dev-echo because the pack, another worktree or a teammate's `kindgi dev`
17
+ has no provider yet. Authoring agents is kindgi-authoring-agents; first
18
+ steps are kindgi-getting-started.
27
19
  type: core
28
20
  library: "@kindgi/sdk"
29
- version: "0.9.17"
30
- sdk_version: "0.2.0-rc.1"
21
+ version: "0.10.1"
22
+ sdk_version: "0.3.0-rc.0"
31
23
  pack_languages: [node, python, java, scala]
32
24
  sources:
33
25
  - packages/adapters/model-anthropic/src/provider.ts
@@ -39,6 +31,7 @@ sources:
39
31
  - packages/secrets-dotenv/src/index.ts
40
32
  ---
41
33
 
34
+
42
35
  # Wiring a model provider for a Kindgi pack
43
36
 
44
37
  > **Running `kindgi`:** in a Node project the CLI is a devDependency
@@ -57,876 +50,34 @@ agent turn. It is a **fallback** provider: it answers only when no other
57
50
  registered provider fits the agent, so registering a real one takes over
58
51
  with nothing else to change. This skill covers wiring a real one.
59
52
 
60
- ## Mental model
61
-
62
- ```
63
- Project .env / .env.local dotenv SecretBinding runtime ProviderRegistry
64
- ───────────────────────── ───────────────────── ────────────────────────
65
- ANTHROPIC_API_KEY=sk-... → resolve('local', hydrated per tenant on first
66
- 'ANTHROPIC_API_KEY') access; each row =
67
- { metadata, adapter_id,
68
- secret_ref?,
69
- adapter_config? }
70
- │ │
71
- ▼ ▼
72
- adapter factory ──── resolves apiKey lazily via closure
73
- │
74
- ▼
75
- ModelProvider ────── router picks a (provider, model)
76
- tuple when an agent's
77
- capabilities.needs matches
78
- ```
79
-
80
- Three moving parts:
81
- 1. **API key on disk** — in `kindgi dev` (environment `local`) the dotenv
82
- secret binding reads the project's own env files: `.env`, then
83
- `.env.local` on top (change the list with `dev.envFiles` in
84
- `kindgi.config.ts`, `envFiles` under `[tool.kindgi.dev]` in a Python
85
- pack's `pyproject.toml`, or `dev.envFiles` in a Java or Scala pack's
86
- `kindgi.config.json`), then Kindgi's own `.kindgi/secrets.env` on top.
87
- A key already in the app's `.env` just works. `kindgi secrets set`
88
- (interactive, no-echo) writes `.kindgi/secrets.env`, a file the app
89
- doesn't load (a framework loads `.env.local` into every route); `--app`
90
- writes the app's file instead, for a value the app reads too (a webhook
91
- signing secret). For non-sensitive values (log levels, region names,
92
- feature flags), `kindgi env set NAME VALUE --env=local` writes the app's
93
- `.env.local` with a positional value — never route real secrets
94
- through it. `KINDGI_*`
95
- names configure Kindgi itself and are never resolvable as secrets.
96
- 2. **Provider registration** — declared in the pack's config, which is
97
- committed, and registered by `kindgi dev` at each boot; on a runtime
98
- `kindgi dev` doesn't run, `kindgi providers register` (`POST /v1/providers`)
99
- 3. **Agent's `capabilities.needs`** — matched at the (provider, model) tuple level at turn time
100
-
101
- **Registration body:** `{ metadata, adapter_id, secret_ref?, adapter_config? }`.
102
- `secret_ref` points at the credential; `adapter_config` holds the adapter's
103
- non-secret connection settings (a cloud project, a base URL) as flat string,
104
- number or boolean values. Both are stored with the provider and handed to the
105
- runtime; `kindgi providers list` / `get` show only `metadata`. Never put a
106
- credential in `adapter_config`.
107
-
108
- **Shape rule:** `ProviderMetadata.id` names the CONNECTION (e.g. `anthropic`,
109
- `groq`, `local-onnx`); per-model fields (`name`, `contextWindow`, `cost`,
110
- `features`) live inside `metadata.models[]`. One row can expose many models
111
- under the same API key.
112
-
113
- **Fallbacks:** `"fallback": true` in `metadata` makes a provider a
114
- fallback — the router considers it only when no other provider satisfies
115
- the agent's capability, and the turn's result then carries a
116
- `fallback-provider` warning. `dev-echo` is one; a cheap local model can be
117
- yours.
118
-
119
- ## On Kindgi Cloud: the key and its provider go under `prod`
120
-
121
- A pack deployed with `kindgi deploy --hosted` runs on your workspace's
122
- runtime on Kindgi Cloud, and that runtime serves the `prod` environment,
123
- before and after you go live. The examples below use `--env=local`, for
124
- `kindgi dev`. For Kindgi Cloud, name `prod` instead:
125
-
126
- ```sh
127
- kindgi secrets set ANTHROPIC_API_KEY --env=prod --scope=tenant # a no-echo prompt
128
- kindgi providers register --preset=anthropic --env=prod
129
- kindgi deploy --hosted --env=prod
130
- ```
131
-
132
- - The runtime reads a provider's key in the environment its
133
- `secret_ref.envName` names, at tenant scope. Keep the key and the
134
- provider both on `prod`. A provider registered without `--env` reads
135
- `local`, not the `prod` key: `register` refuses when the pack's own env
136
- files lack the key (its message names `--env`), and otherwise the first
137
- turn can't find the key (mistake 1). By hand, `provider.json` has
138
- `"secret_ref": { "envName": "prod", … }`.
139
- - `kindgi secrets set` has no default environment: name `--env` every time.
140
- - The deployment `kindgi deploy` sends names no environment. Its
141
- `--env=prod` picks the pack's own `prod` settings, such as its signing
142
- key.
143
- - `kindgi deploy` doesn't register providers, and the providers declared in
144
- the pack's config are `kindgi dev`'s only. Register once on `prod`, by
145
- hand, and again when you add a provider or change one.
146
- - OpenAI, the Gemini API, Groq and OpenRouter take the same two lines,
147
- with their preset and key: `openai` and `OPENAI_API_KEY`, `gemini-api`
148
- and `GEMINI_API_KEY`, `groq` and `GROQ_API_KEY`, `openrouter` and
149
- `OPENROUTER_API_KEY`. Bedrock, Vertex AI and Azure OpenAI don't fit
150
- them: see Amazon Bedrock and Path D below, and `kindgi providers
151
- presets` for `azure-openai`'s settings.
152
-
153
- The whole flow, from signing the CLI in to going live:
154
- https://docs.kindgi.com/v0.2/hosted/deploy-your-agent/#your-model-key-and-its-provider
155
-
156
- ## Path A — Hosted, native Anthropic
157
-
158
- Best fidelity to Anthropic's API (prompt caching, latest models, tool
159
- use). Requires an `ANTHROPIC_API_KEY`.
160
-
161
- A model's `structured-output` feature is a routing label: the model can
162
- follow a JSON schema natively, but Kindgi's typed outputs use instructions,
163
- then parse, check against the schema and repair, on every provider.
164
-
165
- **Step 1 — set the key:**
166
- ```sh
167
- kindgi secrets set ANTHROPIC_API_KEY --env=local --scope=tenant
168
- # Prompts twice, echo disabled: paste the key, then again to confirm.
169
- ```
170
- Under `kindgi dev`, this writes to `.kindgi/secrets.env` at the pack root
171
- via the dotenv secret binding — or add `ANTHROPIC_API_KEY=…` to that file yourself; `kindgi dev`
172
- also reads the app's `.env` / `.env.local`. If the pack lives inside an app
173
- whose env file already has the key, it works as it is; `kindgi secrets copy`
174
- gives Kindgi its own copy and never edits the app's files (the app may use
175
- the key itself: leave removing it to the person). Keep env files gitignored. For pipelines/CI, pipe the value with
176
- `echo -n "$KEY" | kindgi secrets set ANTHROPIC_API_KEY --env=local --scope=tenant --from-stdin`,
177
- or read from a mode-0600 file with `--from-file <path>`. Never pass a
178
- credential on argv.
179
-
180
- For a pack on Kindgi Cloud, `--env=prod` instead: the key goes to your
181
- workspace's runtime, not to a file (On Kindgi Cloud, above).
182
-
183
- **Step 2 — declare it in the pack's config, and commit it.** `kindgi dev`
184
- registers what the config declares on every boot: in every git worktree
185
- (each has its own dev database), after `--reset`, and on a teammate's
186
- machine. The declaration holds a key's name, never the key, so it goes in
187
- git with the rest of the config. Committing it is how a teammate's
188
- `kindgi dev`, or another worktree's, gets the same provider:
189
- ```ts
190
- // in kindgi.config.ts
191
- providers: [
192
- { preset: 'anthropic', models: ['claude-sonnet-5-5'] }, // key ANTHROPIC_API_KEY, from the env files
193
- { preset: 'gemini', project: 'acme-gcp', models: ['gemini-3.8-flash'] },
194
- { spec: { /* the provider.json body below */ } },
195
- ],
196
- ```
197
- ```toml
198
- # in pyproject.toml: one table per provider, same keys
199
- [[tool.kindgi.providers]]
200
- preset = "anthropic"
201
- models = ["claude-sonnet-5-5"]
202
- ```
203
- In a Java or Scala pack's `kindgi.config.json`, the same keys:
204
- `"providers": [{"preset": "anthropic", "models": ["claude-sonnet-5-5"]}]`.
205
- - A preset entry takes `models`, the settings its preset asks for (`project`
206
- for `gemini`; `resourceName` and `deployments` for `azure-openai`; `region`
207
- for `bedrock`), `secret` (the key's name, in place of the preset's) and
208
- `maxOutputTokens`, spelled the same in `pyproject.toml` and
209
- `kindgi.config.json`. Its own settings are required and another preset's
210
- are refused. `deployments` is a map of model to deployment
211
- (`{ "gpt-6.1-sol": "gpt-6-1-sol" }`, a `[tool.kindgi.providers.deployments]`
212
- table in `pyproject.toml`, model names quoted), not the `--deployments` flag's
213
- `model=deployment,…`, and it names exactly the models the entry registers;
214
- a `spec` entry is a `--spec` body. A
215
- key is always a secret's name (`secret_ref`); a credential in
216
- `adapter_config` is refused.
217
- - Each boot prints `Providers from kindgi.config.ts:` (the pack's config file) with one line each:
218
- `registered`, `unchanged`, `registered again (changed in kindgi.config.ts)`,
219
- `unregistered (no longer in kindgi.config.ts)`, or ⚠ `not registered: <KEY>
220
- is not in .env, .env.local, .kindgi/secrets.env` (set the key, then
221
- restart: the config isn't watched).
222
- - A declaration applies at the next boot: the config isn't watched, so
223
- restart `kindgi dev` after changing it. Each person, and each worktree,
224
- sets the key in its own env files (step 1).
225
- - A provider with that id that `kindgi dev` didn't register is left as it is;
226
- if its region or models differ, a ⚠ line names the
227
- `kindgi providers unregister` that lets the config's version apply.
228
- - `.kindgi/dev/providers.json` is `kindgi dev`'s own record of what it
229
- registered. `.kindgi/` is gitignored, so that record stays in this
230
- worktree, and it isn't where a provider is declared.
231
-
232
- Before pinning a Claude model, check its status on Anthropic's model
233
- deprecations page (https://platform.claude.com/docs/en/about-claude/model-deprecations): a turn routed to a retired model fails. Prefer the
234
- preset's default model. `anthropic` without `models` registers Opus 5.5,
235
- Sonnet 5.5 (the default), Haiku 5.5 and Haiku 4.5. Each
236
- preset names a default model (`metadata.defaultModel`, marked `(default)`
237
- when it registers), which an agent with no preference gets. A preset
238
- registered before 0.1.4 has none: unregister it and register it again.
239
- The Claude 5.5 and GPT-6 models take no `temperature` (`"sampling": false`:
240
- the call goes without it, with a `sampling-unsupported` warning), and a
241
- model's `thinking` says how it thinks; thinking counts against
242
- `maxOutputTokens` and bills as output.
243
- The preset carries the models, context windows, default output caps (what
244
- a call asks for when its agent sets no `maxOutputTokens`; not the model's own
245
- limit) and current prices (`kindgi providers presets` lists the presets and
246
- when their prices were checked); an entry's `maxOutputTokens` sets another cap. That's
247
- all for Anthropic; go to step 4. The rest of this path is a spec of your
248
- own, by hand.
249
-
250
- **Staging and production: register by hand, once per runtime.** The
251
- declaration drives `kindgi dev` only. A runtime `kindgi dev` doesn't run
252
- (staging, production, Kindgi Cloud) gets its providers from
253
- `kindgi providers register` against that runtime, once, and again when you
254
- add a provider or change one. A registration is the runtime's, shared by
255
- every environment it serves: register once in all when one runtime serves
256
- both staging and prod, and once on each when they're separate runtimes. The
257
- key isn't shared: set it first in each environment that runs the pack
258
- (`kindgi secrets set ANTHROPIC_API_KEY --env=<environment>`), then:
259
- ```sh
260
- kindgi providers register --preset=anthropic --env=<env> # the preset's models
261
- kindgi providers register --preset=anthropic --models=claude-sonnet-5-5 --env=<env> # just one
262
- ```
263
- - `--env` names one of the pack's environments. On a runtime that keeps
264
- its values under one env, the key goes in that environment's project, and
265
- `register` with it prints the `kindgi secrets set` line, to run again with
266
- each other environment's name. On a runtime that serves several
267
- environments, or with an `--env` that isn't one of the pack's,
268
- `kindgi secrets set` asks for a `--scope`. On Kindgi Cloud, it's `prod`
269
- (On Kindgi Cloud, above; mistake 1). `--max-output-tokens=<n>` sets
270
- another output cap.
271
- - To change a provider there, unregister it first (mistake 5), then
272
- register it again.
273
- - `kindgi deploy` doesn't register providers. `kindgi promote` checks the
274
- target first and stops (exit 1) while it has no provider, naming
275
- `kindgi providers register --preset=<name>`.
276
- - Nothing in git records what a runtime has registered:
277
- `kindgi providers list` against it shows it.
278
- - Under `kindgi dev`, `kindgi providers register` puts a provider in this
279
- worktree's dev database only, and leaves nothing to commit. Use it to try a
280
- provider, then declare it.
281
-
282
- **Step 2 (by hand) — write `provider.json`** at the pack root, and commit it:
283
- like the declaration, it names its key (`secret_ref`) and never holds it.
284
- Under `kindgi dev`, declare its body as a `spec` entry; on another runtime,
285
- register it with `--spec` (step 3). One connection,
286
- two models — matches how the Anthropic SDK actually works (the API
287
- key is per-vendor; the model is per-call):
288
- ```json
289
- {
290
- "metadata": {
291
- "id": "anthropic",
292
- "region": "us-east-1",
293
- "models": [
294
- {
295
- "name": "claude-opus-5-5",
296
- "contextWindow": 1000000,
297
- "features": ["tool-use", "structured-output"],
298
- "cost": {
299
- "promptUsdPer1kTokens": 0.004,
300
- "completionUsdPer1kTokens": 0.02
301
- },
302
- "description": "Most capable Opus — long-running agentic and knowledge work."
303
- },
304
- {
305
- "name": "claude-sonnet-5-5",
306
- "contextWindow": 1000000,
307
- "features": ["tool-use", "structured-output"],
308
- "cost": {
309
- "promptUsdPer1kTokens": 0.002,
310
- "completionUsdPer1kTokens": 0.01
311
- },
312
- "description": "Balanced performance/cost."
313
- }
314
- ],
315
- "description": "Anthropic Claude via native adapter."
316
- },
317
- "adapter_id": "@kindgi/adapter-model-anthropic",
318
- "secret_ref": { "envName": "local", "name": "ANTHROPIC_API_KEY" }
319
- }
320
- ```
321
-
322
- > ⚠ **Units gotcha:** the field is `promptUsdPer1kTokens` (per THOUSAND
323
- > tokens), but every vendor pricing page (Anthropic, OpenAI, Groq)
324
- > advertises rates PER MILLION. Divide by 1000 before you paste. Sonnet
325
- > 5.5 is "$2 / MTok input" → `0.002`, "$10 / MTok output" → `0.01`. A
326
- > misplaced factor makes real cost look 1000× real; a $0.15 budget
327
- > blows in the first turn.
328
-
329
- **Step 3 — register:**
330
- ```sh
331
- kindgi providers register --spec=@provider.json
332
- ```
333
- Returns `{providerId: "anthropic"}` on success. Idempotent in the sense
334
- that re-registering the same id returns `provider-already-registered`;
335
- unregister first if the config actually changed.
336
-
337
- **Step 4 — verify:**
338
- ```sh
339
- kindgi providers list
340
- ```
341
-
342
- **Step 5 — run:** any agent whose `capabilities: [{needs: [{feature:
343
- 'tool-use'}]}]` is now routed to a (`anthropic`, `<one of the models>`)
344
- tuple. The router picks by score; prefer a provider with
345
- `preferredProvider` and pin a model with a `models: { allow: [...] }`
346
- capability requirement (see "How the router picks…" below).
347
-
348
- Nothing to switch off: `dev-echo` is a fallback, so the new provider
349
- answers every agent it satisfies. A turn that still lands on dev-echo
350
- carries the `fallback-provider` and `dev-echo-not-a-model` warnings — see
351
- mistake 9.
53
+ ## How it fits together
352
54
 
353
- **The other one-key presets** work the same way, with their own key:
354
- `--preset=openai` (`OPENAI_API_KEY`), `--preset=gemini-api` (`GEMINI_API_KEY`,
355
- a Google AI Studio key; `gemini` is Vertex AI), `--preset=groq`
356
- (`GROQ_API_KEY`) and `--preset=openrouter` (`OPENROUTER_API_KEY`).
357
- `kindgi providers presets` lists them with their models.
55
+ 1. **The API key** sits in Kindgi's own `.kindgi/secrets.env`
56
+ (`kindgi secrets set NAME --env=local --scope=tenant`, a no-echo prompt)
57
+ or the project's `.env` / `.env.local`. The runtime reads it through the
58
+ dotenv secret binding at turn time. Never put a credential in
59
+ `adapter_config`.
60
+ 2. **The provider** is declared in the pack's config, committed, and
61
+ registered by `kindgi dev` at each boot. On a runtime `kindgi dev`
62
+ doesn't run, `kindgi providers register` registers it. One row is one
63
+ connection (`metadata.id`, such as `anthropic`), its models under
64
+ `metadata.models[]`.
65
+ 3. **The agent's `capabilities.needs`** is matched against each
66
+ (provider, model) at turn time.
358
67
 
359
- ## Path B — Hosted via OpenAI-compat
68
+ ## Your case: read this file
360
69
 
361
- Works with **any** OpenAI-compatible endpoint. Same adapter, different
362
- `baseURL`:
363
-
364
- | Provider | baseURL |
70
+ | Your case | Read |
365
71
  |---|---|
366
- | OpenAI | `https://api.openai.com/v1` |
367
- | Groq | `https://api.groq.com/openai/v1` |
368
- | Together | `https://api.together.xyz/v1` |
369
- | Fireworks | `https://api.fireworks.ai/inference/v1` |
370
- | DeepSeek | `https://api.deepseek.com/v1` |
371
- | LiteLLM proxy | `http://localhost:4000/v1` |
372
- | OpenRouter (a hosted gateway) | `https://openrouter.ai/api/v1` |
373
-
374
- The connection carries the `baseURL` (in `adapter_config`);
375
- each endpoint is a separate provider row because each has its own API
376
- key + region. Within one row, list every model that endpoint exposes.
377
-
378
- **Step 1 — set the key:**
379
- ```sh
380
- kindgi secrets set GROQ_API_KEY --env=local --scope=tenant
381
- # Interactive prompt; paste and enter.
382
- ```
383
-
384
- **Step 2 — `provider.json`** (Groq, exposing two Llama variants):
385
- ```json
386
- {
387
- "metadata": {
388
- "id": "groq",
389
- "region": "us-central-1",
390
- "models": [
391
- {
392
- "name": "llama-3.3-70b-versatile",
393
- "contextWindow": 128000,
394
- "features": ["tool-use"],
395
- "cost": {
396
- "promptUsdPer1kTokens": 0.00059,
397
- "completionUsdPer1kTokens": 0.00079
398
- }
399
- },
400
- {
401
- "name": "llama-3.1-8b-instant",
402
- "contextWindow": 128000,
403
- "features": ["tool-use"],
404
- "cost": {
405
- "promptUsdPer1kTokens": 0.00005,
406
- "completionUsdPer1kTokens": 0.00008
407
- },
408
- "description": "Fast + cheap tier — routing, classification."
409
- }
410
- ],
411
- "description": "Groq-hosted Llama via OpenAI-compat."
412
- },
413
- "adapter_id": "@kindgi/adapter-model-openai-compat",
414
- "secret_ref": { "envName": "local", "name": "GROQ_API_KEY" },
415
- "adapter_config": { "baseURL": "https://api.groq.com/openai/v1" }
416
- }
417
- ```
418
-
419
- **Steps 3–5** same as Path A.
420
-
421
- ## Path C — Local via in-process ONNX (runtime from source only)
422
-
423
- > ⚠️ **Not in the runtime image, so not under `kindgi dev`.** The adapter
424
- > runs ONNX through `onnxruntime-node`, which ships glibc binaries only,
425
- > and the Kindgi runtime image is Alpine (musl): the adapter can't load
426
- > there (`Error loading shared library ld-linux-…`). `kindgi dev` runs
427
- > that image, so this path fails under it. It works only when the
428
- > runtime itself runs from source on macOS or a glibc Linux. **For a
429
- > local model under `kindgi dev`, use Ollama** ([Local via Ollama](#local-via-ollama-via-path-b)).
430
-
431
- > ⚠️ **Dev-only.** The in-process ONNX adapter writes weights to
432
- > `~/.cache/huggingface/hub/` — a per-machine cache with no production
433
- > recipe (no volume-mount recipe, no image-bake pattern, no offline
434
- > mode, no SHA pinning). Good for smoke tests and CI runners that run
435
- > the runtime from source and keep the same disk between runs. **Do not
436
- > ship packs that rely on this adapter to production.** Use hosted
437
- > providers (Path A) or Ollama (Path B) instead.
438
-
439
- No API key. No network. Bundled with the framework — the adapter ships
440
- `smollm2-360m` by default (~273 MB weights, cached at
441
- `~/.cache/huggingface/hub/models--HuggingFaceTB--SmolLM2-360M-Instruct/`
442
- after `kindgi adapters prepare`).
443
-
444
- **Not for reasoning-heavy agents.** SmolLM 360M is fine for smoke
445
- tests, canary agents, dev/CI. Real work needs a hosted model or a
446
- larger local model via Ollama (Path B).
447
-
448
- **`provider.json`** (single-model):
449
- ```json
450
- {
451
- "metadata": {
452
- "id": "local-onnx",
453
- "region": "in-process",
454
- "models": [
455
- {
456
- "name": "smollm2-360m",
457
- "contextWindow": 2048,
458
- "features": ["tool-use"],
459
- "cost": {
460
- "promptUsdPer1kTokens": 0,
461
- "completionUsdPer1kTokens": 0
462
- }
463
- }
464
- ],
465
- "description": "Local ONNX smollm2-360m — dev + smoke tests."
466
- },
467
- "adapter_id": "@kindgi/adapter-model-in-process"
468
- }
469
- ```
470
-
471
- Multi-model variant — the in-process adapter loads each pipeline lazily,
472
- so listing several models keeps memory low until they're actually
473
- invoked:
474
-
475
- ```json
476
- {
477
- "metadata": {
478
- "id": "local-onnx",
479
- "region": "in-process",
480
- "models": [
481
- { "name": "smollm2-135m", "contextWindow": 2048, "features": [],
482
- "cost": { "promptUsdPer1kTokens": 0, "completionUsdPer1kTokens": 0 } },
483
- { "name": "smollm2-360m", "contextWindow": 2048, "features": ["tool-use"],
484
- "cost": { "promptUsdPer1kTokens": 0, "completionUsdPer1kTokens": 0 } },
485
- { "name": "qwen3-0.6b", "contextWindow": 32768, "features": ["tool-use", "long-context"],
486
- "cost": { "promptUsdPer1kTokens": 0, "completionUsdPer1kTokens": 0 } }
487
- ]
488
- },
489
- "adapter_id": "@kindgi/adapter-model-in-process"
490
- }
491
- ```
492
-
493
- No `secret_ref` needed. Register + **prepare** + run:
494
- ```sh
495
- kindgi providers register --spec=@provider.json
496
- kindgi adapters prepare @kindgi/adapter-model-in-process --model=smollm2-360m
497
- kindgi runs start --agent=<agent-id> --input='{...}'
498
- ```
499
-
500
- **Always `prepare` before `runs start`.** It downloads the ONNX weights
501
- (~273 MB for smollm2-360m, ~30s on a typical connection) and streams
502
- per-file progress. Skipping it works — the first model call downloads
503
- lazily — but that stalls a request path on a multi-hundred-MB
504
- fetch with no observability, which is bad in dev and unacceptable in
505
- production. Skip only for scripted teardown where the model is already
506
- cached (`~/.cache/huggingface/hub/models--HuggingFaceTB--SmolLM2-360M-Instruct/`).
507
-
508
- `prepare` is idempotent: subsequent invocations are cache hits and
509
- return almost instantly, so put it in the script that boots your
510
- from-source runtime.
511
-
512
- Multi-model providers: prepare each model separately.
513
- ```sh
514
- kindgi adapters prepare @kindgi/adapter-model-in-process --model=smollm2-135m
515
- kindgi adapters prepare @kindgi/adapter-model-in-process --model=smollm2-360m
516
- kindgi adapters prepare @kindgi/adapter-model-in-process --model=qwen3-0.6b
517
- ```
518
-
519
- ## Local via Ollama (via Path B)
520
-
521
- For bigger local models — Ollama runs them, we point at the OpenAI-compat
522
- endpoint it exposes:
523
-
524
- ```json
525
- {
526
- "metadata": {
527
- "id": "ollama",
528
- "region": "local",
529
- "models": [
530
- {
531
- "name": "llama3",
532
- "contextWindow": 8192,
533
- "features": ["tool-use"],
534
- "cost": {
535
- "promptUsdPer1kTokens": 0,
536
- "completionUsdPer1kTokens": 0
537
- }
538
- }
539
- ],
540
- "description": "Local Ollama Llama 3 8B."
541
- },
542
- "adapter_id": "@kindgi/adapter-model-openai-compat",
543
- "adapter_config": { "baseURL": "http://localhost:11434/v1" }
544
- }
545
- ```
546
-
547
- Ollama doesn't check a key, so the row has no `secret_ref`: without one,
548
- the adapter sends a placeholder key, as local servers expect.
549
-
550
- **Any model you serve yourself** (Ollama, vLLM, llama.cpp's
551
- `llama-server`, LM Studio) must do two things for an agent:
552
- - **Call tools in the OpenAI format.** Serve it with tool calling on
553
- (vLLM `--enable-auto-tool-choice --tool-call-parser <the model's>`,
554
- `llama-server --jinja`), and pick a model that supports tools.
555
- - **Answer without its thinking.** A thinking model (Qwen and others)
556
- writes its reasoning first unless asked not to, and a typed answer then
557
- isn't JSON (`output-schema-violation`). Turn thinking off on the server
558
- (vLLM `--default-chat-template-kwargs '{"enable_thinking": false}'`,
559
- `llama-server --reasoning off`), or, for a shared server you can't
560
- reconfigure, per request from the provider row:
561
- `"extraBody.chat_template_kwargs.enable_thinking": false` in
562
- `adapter_config`. `adapter_config` is flat: one key per request field,
563
- dots nest.
564
-
565
- Assumes Ollama is installed + the model is pulled (`ollama pull llama3`).
566
- See ollama.com for install / hardware requirements — Llama 3 8B needs
567
- ~8GB RAM, Llama 3 70B needs ~48GB VRAM or ~140GB RAM.
568
-
569
- ## Path D — Gemini on Vertex AI
570
-
571
- Gemini through Google Cloud's Vertex AI. No API key: the adapter uses
572
- **Google Application Default Credentials** — your `gcloud` login on a
573
- laptop, the attached service account on Cloud Run (and other Google Cloud
574
- runtimes). The same registration works in both.
575
-
576
- **Step 1 — credentials:**
577
- ```sh
578
- gcloud auth application-default login # once per laptop
579
- ```
580
- The account (locally) or the service account (deployed) needs the
581
- **Vertex AI User** role (`roles/aiplatform.user`) in the project. Running
582
- outside Google Cloud: put a service-account key (its JSON) in a secret
583
- (`kindgi secrets set GEMINI_SA_KEY --env=local --scope=tenant`) and add
584
- `"secret_ref": { "envName": "local", "name": "GEMINI_SA_KEY" }`. A runtime
585
- with `KINDGI_TENANT_HOST_ACCESS=isolated` requires that key: it doesn't lend
586
- its own credentials to tenant configuration.
587
-
588
- **Step 2 — `provider.json`:**
589
- ```json
590
- {
591
- "metadata": {
592
- "id": "gemini",
593
- "region": "global",
594
- "models": [
595
- {
596
- "name": "gemini-3.8-flash",
597
- "contextWindow": 1048576,
598
- "features": ["tool-use", "structured-output", "long-context"],
599
- "maxOutputTokens": 65536,
600
- "thinking": { "mode": "adaptive", "lowest": "low" },
601
- "cost": { "promptUsdPer1kTokens": 0.00075, "completionUsdPer1kTokens": 0.00375 }
602
- },
603
- {
604
- "name": "gemini-3.5-flash-lite",
605
- "contextWindow": 1048576,
606
- "features": ["tool-use", "structured-output", "long-context"],
607
- "maxOutputTokens": 65536,
608
- "thinking": { "mode": "adaptive", "lowest": "minimal" },
609
- "cost": { "promptUsdPer1kTokens": 0.0003, "completionUsdPer1kTokens": 0.0025 }
610
- }
611
- ]
612
- },
613
- "adapter_id": "@kindgi/adapter-model-gemini",
614
- "adapter_config": { "project": "<your-gcp-project-id>" }
615
- }
616
- ```
617
- - `adapter_config.project` is required: the Google Cloud project Vertex
618
- bills and authorises against.
619
- - `metadata.region` is the Vertex location: `global`, or a region such as
620
- `us-central1` or `northamerica-northeast1` when data must stay in one
621
- place. `unspecified` means `global`. Different locations are different
622
- provider rows. Check that the location serves the model: Gemini 3.8 Flash
623
- isn't served from `us-central1`.
624
- - Don't register `gemini-2.5-pro` or `gemini-2.5-flash`: Vertex AI retires
625
- both on 2026-10-20.
626
- - Rates are per 1K tokens, from Google's published pricing; check them
627
- before relying on budgets. Thinking tokens bill as output, and Gemini 3.8
628
- Flash thinks by default. `gemini-3.8-flash`'s rates above are Google's
629
- launch price, through 2026-12-31 ($0.0015 / $0.0075 from 2027-01-01).
630
- `longContext` (`{ thresholdTokens, promptUsdPer1kTokens,
631
- completionUsdPer1kTokens }` in a model's `cost`) switches the whole call
632
- to the higher rates past the threshold; `cachedPromptMultiplier` (default 0.25) prices cached
633
- prompt tokens.
634
- - `thinking` is how the model thinks, as the preset has it. `lowest` is
635
- the least level it takes, which a judge's call asks for.
636
- - To make a model think less, set `adapter_config.thinkingLevels`, a level
637
- for each model it names: `"thinkingLevels": "gemini-3.8-flash=low"`. The
638
- levels are `minimal`, `low`, `medium` and `high`. A named model needs
639
- `thinking` in its entry and a level no lower than its `lowest`, or the
640
- registration is refused. A model it doesn't name thinks as Google sets.
641
- It takes effect from the 0.2.0-rc.1 adapter on; an older one ignores it.
642
-
643
- **Step 3 — register and check:**
644
- ```sh
645
- kindgi providers register --spec=@provider.json
646
- kindgi providers list
647
- ```
648
- Or skip step 2: `kindgi providers register --preset=gemini --project=<your-gcp-project-id>`
649
- registers both models above.
650
- Then pin it from an agent with `preferredProvider: 'gemini'` (and a model
651
- with `preferredModel`; in Python, `preferred_provider="gemini"` and
652
- `preferred_model=…`; in Java, `.set("preferredProvider", "gemini")`), or let
653
- the router pick by capability.
654
-
655
- ## Amazon Bedrock
656
-
657
- Nova, Claude and GPT on Amazon Bedrock. The `bedrock` preset signs in as the
658
- runtime's own AWS identity (`KINDGI_AWS_IDENTITY` says where it comes from),
659
- so it takes no key:
660
- ```sh
661
- kindgi providers register --preset=bedrock --region=us-east-2
662
- kindgi providers register --preset=bedrock --region=us-east-2 \
663
- --models=us.anthropic.claude-haiku-4-5-20251001-v1:0 # Claude, not Nova Pro
664
- ```
665
- `--region` is the US Region the account uses Bedrock in: the preset's models
666
- are US cross-region inference profiles. Its default model is Amazon Nova Pro
667
- (`us.amazon.nova-pro-v1:0`). Claude and GPT also need the AWS account's
668
- access to their vendor's models
669
- (https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html).
670
-
671
- **On a runtime with `KINDGI_TENANT_HOST_ACCESS=isolated`, use a Bedrock API
672
- key.** That runtime doesn't lend its own credentials to tenant configuration,
673
- so it refuses the preset at registration and stores nothing:
674
- ```text
675
- ✗ /adapter_config/auth: adapter_config.auth = aws-identity (the default) signs in as the runtime's own AWS identity, which this deployment doesn't let tenant configuration use (KINDGI_TENANT_HOST_ACCESS). Set auth = api-key, with secret_ref naming a Bedrock API key.
676
- ```
677
- `--secret` doesn't change that: the preset still signs in as the runtime.
678
- Register from a spec whose `adapter_config.auth` is `api-key` and whose
679
- `secret_ref` names the key:
680
-
681
- 1. **A long-term Bedrock API key**, made in the Region the provider uses. A
682
- short-term key lasts 12 hours at most, so a registration can't keep one,
683
- and a key works only in the Region it was made in. AWS recommends
684
- long-term keys for exploration only: tell the person so, with AWS's page
685
- (https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html).
686
- 2. **Stored as a tenant secret**, with the no-echo prompt:
687
- `kindgi secrets set BEDROCK_API_KEY --env=production --scope=tenant`.
688
- 3. **Registered:** `kindgi providers register --spec=@bedrock.json`, with
689
- ```json
690
- {
691
- "adapter_id": "@kindgi/adapter-model-bedrock",
692
- "adapter_config": { "auth": "api-key" },
693
- "secret_ref": { "envName": "production", "name": "BEDROCK_API_KEY" },
694
- "metadata": {
695
- "id": "bedrock",
696
- "region": "us-east-2",
697
- "models": [
698
- { "name": "us.anthropic.claude-haiku-4-5-20251001-v1:0", "contextWindow": 200000,
699
- "maxOutputTokens": 16000, "features": ["tool-use", "parallel-tool-use"],
700
- "cost": { "promptUsdPer1kTokens": 0.0011, "completionUsdPer1kTokens": 0.0055 } }
701
- ]
702
- }
703
- }
704
- ```
705
-
706
- One provider is one Region and one key: another Region is another provider,
707
- with a key made there. The key is read when a turn calls the model, not at
708
- registration.
709
-
710
- The same holds for the other providers that can sign in as the runtime on
711
- `isolated`. Gemini on Vertex AI takes a service-account key in `secret_ref`
712
- (Path D). Azure OpenAI's `auth: entra` is refused: use the `azure-openai`
713
- preset, with the resource's key. A provider registered before the runtime
714
- moved to `isolated` that signs in as the runtime isn't used there: a turn
715
- that needs it fails with `422 capability-routing-failed`. Unregister it, and
716
- register it again with a key.
717
-
718
- Docs: https://docs.kindgi.com/v0.2/guides/models/#with-a-bedrock-api-key
719
-
720
- ## How the router picks between multiple providers + models
721
-
722
- When an agent turn fires, the router expands each registered provider
723
- into one entry per model (`metadata.models[]`), filters the resulting
724
- (provider, model) tuples against the agent's `capabilities.needs` (and
725
- tenant policy), then sorts survivors in this order:
726
-
727
- 1. **Preferred provider / model.** Tuples matching the agent's
728
- `preferredProvider` and `preferredModel` are promoted to the front.
729
- - Both set → promote the exact tuple.
730
- - Only `preferredModel` set → promote any provider exposing that model.
731
- - Only `preferredProvider` set → promote every model of that provider.
732
- `defineAgent` takes both (`preferredProvider`, `preferredModel`), and
733
- so does a Python `Agent` (`preferred_provider=`, `preferred_model=`) and
734
- a Java `Agent.define(…)` (`set("preferredProvider", …)`).
735
- 2. **`capability.prefer[]` weights.** If the agent's capability
736
- declares `prefer: [{feature: 'thinking', weight: 3}, ...]`, tuples
737
- with matching model features (or provider attributes) get higher
738
- scores. Sorted by summed score, descending.
739
- 3. **Deterministic tiebreak.** When scores tie, tuples sort by provider
740
- id, then the provider's `defaultModel` before its other models, then
741
- model name — replay-safe and stable. A provider without a
742
- `defaultModel` falls back to its first model by name.
743
-
744
- **Practical rule:** preferences are soft — they rank, they don't
745
- exclude. To guarantee which model runs, make it a hard requirement in
746
- the capability: `{ models: { allow: ['claude-sonnet-5-5'] } }` (and
747
- `{ providers: { allow: ['anthropic'] } }` to pin the connection). If no
748
- registered tuple satisfies it, the turn fails with
749
- `capability-routing-failed` instead of silently using another model.
750
-
751
- **A/B testing a specific agent across models within one Anthropic
752
- registration:**
753
-
754
- ```ts
755
- // agents/weather-agent-sonnet/index.ts
756
- defineAgent({
757
- id: 'my-pack.weather-agent-sonnet',
758
- // …same name, instructions, tools, retrieval and guardrails as the opus variant
759
- capabilities: [
760
- { needs: [{ feature: 'tool-use' }, { models: { allow: ['claude-sonnet-5-5'] } }] },
761
- ],
762
- preferredProvider: 'anthropic',
763
- });
764
-
765
- // agents/weather-agent-opus/index.ts
766
- defineAgent({
767
- id: 'my-pack.weather-agent-opus',
768
- // …
769
- capabilities: [
770
- { needs: [{ feature: 'tool-use' }, { models: { allow: ['claude-opus-5-5'] } }] },
771
- ],
772
- preferredProvider: 'anthropic',
773
- });
774
- ```
775
-
776
- The same pair in a Python pack (`kindgi.Agent`; module-level, one file each):
777
-
778
- ```python
779
- # agents/weather_agent_sonnet.py
780
- weather_agent_sonnet = Agent(
781
- id="my-pack.weather-agent-sonnet",
782
- # …same version, name, instructions, tools and guardrails as the opus variant
783
- capabilities=[
784
- {"needs": [{"feature": "tool-use"}, {"models": {"allow": ["claude-sonnet-5-5"]}}]},
785
- ],
786
- preferred_provider="anthropic",
787
- )
788
-
789
- # agents/weather_agent_opus.py
790
- weather_agent_opus = Agent(
791
- id="my-pack.weather-agent-opus",
792
- # …
793
- capabilities=[
794
- {"needs": [{"feature": "tool-use"}, {"models": {"allow": ["claude-opus-5-5"]}}]},
795
- ],
796
- preferred_provider="anthropic",
797
- )
798
- ```
799
-
800
- Both agents share the tool + instructions; only the pinned model
801
- differs. One Anthropic provider registration + one API key covers
802
- both. Register the provider once, then invoke each agent to compare
803
- outputs on the same inputs.
804
-
805
- **A/B testing across vendors:**
806
-
807
- ```ts
808
- defineAgent({
809
- id: 'my-pack.weather-agent-groq',
810
- // …
811
- capabilities: [
812
- { needs: [{ feature: 'tool-use' }, { models: { allow: ['llama-3.3-70b-versatile'] } }] },
813
- ],
814
- preferredProvider: 'groq',
815
- });
816
-
817
- defineAgent({
818
- id: 'my-pack.weather-agent-anthropic',
819
- // …
820
- capabilities: [
821
- { needs: [{ feature: 'tool-use' }, { models: { allow: ['claude-sonnet-5-5'] } }] },
822
- ],
823
- preferredProvider: 'anthropic',
824
- });
825
- ```
826
-
827
- ## Common mistakes
828
-
829
- 0. **`adapter_id` uses the short name.** The `adapter_id` field must
830
- be the FULL npm package name of the adapter — `"@kindgi/adapter-model-anthropic"`,
831
- NOT `"anthropic"`. Adapters are registered with the runtime under
832
- their full package names, and a short name matches none of them, so
833
- registering is refused: the runtime has no adapter by that name
834
- (`✗ /adapter_id: …`). The model adapters are
835
- `@kindgi/adapter-model-anthropic`, `@kindgi/adapter-model-gemini`,
836
- `@kindgi/adapter-model-openai-compat`,
837
- `@kindgi/adapter-model-azure-openai`, `@kindgi/adapter-model-bedrock` and
838
- `@kindgi/adapter-model-in-process`; `kindgi providers presets` shows
839
- the id each preset uses.
840
-
841
- 1. **`envName` mismatch between the key and the provider's `secret_ref`.**
842
- The runtime reads a provider's key in the environment its
843
- `secret_ref.envName` names, at tenant scope. The SecretBinding refuses
844
- to cross-resolve, so a key held under another environment gives
845
- `secret-not-found` at agent-turn time. Where each one lands:
846
- `kindgi secrets set` needs `--env=<name>`; `kindgi providers register
847
- --preset` writes `--env`'s name, or `local` without it; a `--spec` file
848
- names its own (`"secret_ref": {"envName": "production", ...}`). Under
849
- `kindgi dev`, `local` is the project's env files (`.env`, `.env.local`)
850
- and `.kindgi/secrets.env`; any other name is `.env.<envName>`. Match
851
- them. On Kindgi Cloud, both are `prod`.
852
-
853
- 2. **Missing `features` on the model.** Agent declares
854
- `capabilities: [{needs: [{feature: 'tool-use'}]}]`; at least one
855
- model inside `metadata.models[]` must list `"features":
856
- ["tool-use"]` or the router filters every tuple out. If `kindgi
857
- runs start` returns "no provider satisfies capability", check the
858
- per-model `features` list — not a provider-level field anymore.
859
-
860
- 3. **Empty `models[]`.** Registration rejects with `empty-models`. Every
861
- provider row must expose at least one model. Duplicate `name` within
862
- the same array is also rejected (`duplicate-model-name`).
863
-
864
- 4. **Setting `secret_ref` for the in-process adapter.** `in-process`
865
- ignores `secret_ref` — no key needed. Leaving it in is harmless but
866
- noisy.
867
-
868
- 5. **Registering the same provider id twice with different config.**
869
- Returns `409 provider-already-registered`. Unregister first:
870
- ```sh
871
- kindgi providers unregister <provider-id>
872
- ```
873
- Then re-register.
874
-
875
- 6. **Key not found by the runtime.** `kindgi dev` reads the env files
876
- at the PACK ROOT (the directory with `kindgi.config.ts`, a Python
877
- pack's `pyproject.toml` with `[tool.kindgi]`, or a Java or Scala pack's
878
- `kindgi.config.json`) — `.env` and
879
- `.env.local`, or whatever `dev.envFiles` lists; the boot log prints
880
- which files it found. A `KINDGI_`-prefixed name is Kindgi runtime
881
- config and never resolves as a secret. Outside `kindgi dev`, the
882
- runtime resolves secrets through the deployment's `SecretBinding`
883
- (a KMS- or database-backed store, for example).
884
-
885
- 7. **Forgetting to restart `kindgi dev` after registering a provider.**
886
- You don't need to — provider registrations go to the running runtime
887
- over HTTP (`POST /v1/providers`), which invalidates the tenant's
888
- cached provider set. The next `kindgi runs start` sees the new
889
- provider.
890
-
891
- 8. **Compound `preferredProvider` strings.** `preferredProvider` is a
892
- provider (connection) id such as `'anthropic'`; the model is a
893
- separate choice. A value like `'anthropic-claude-sonnet-5-5'`
894
- (vendor + model baked together) matches no provider id, so the hint
895
- does nothing. Pin the model with a `models: { allow: [...] }`
896
- requirement instead.
897
-
898
- 9. **Replies still come from dev-echo** (`⚠ dev-echo isn't a real model: …`
899
- then `Tool responded: …`; the turn's result has the `fallback-provider`
900
- and `dev-echo-not-a-model` warnings). dev-echo is a fallback: it
901
- answers only when no registered provider satisfies the agent. So your
902
- provider doesn't — check its models' `features` against the agent's
903
- `capabilities.needs` (mistake 2), a `models` / `providers` allow-list
904
- that names nothing registered, and the tenant's policy. `kindgi
905
- providers list` shows what is registered.
906
-
907
- 10. **A setting the adapter can't use.** Registering checks the spec
908
- against its adapter (no network call, no key read) and refuses what
909
- it can't use: `422 provider-config-invalid`, nothing stored, one line
910
- per problem with its JSON-pointer path:
911
- ```text
912
- Error [invalid-request]: Provider "ollama" doesn't fit adapter @kindgi/adapter-model-openai-compat: adapter_config.api must be one of responses, chat-completions.
913
- ✗ /adapter_config/api: adapter_config.api must be one of responses, chat-completions.
914
- ```
915
- Fix each `✗` line's setting and register again. A key the adapter
916
- needs is checked too (`✗ /secret_ref: …`); whether the key works, or
917
- the endpoint answers, isn't (the first turn finds out). A
918
- registration stored before 0.1.5 wasn't checked: `kindgi doctor`
919
- names its problems (`GET /v1/providers/<id>/check`); unregister it
920
- and register it again.
921
-
922
- 11. **A provider registered by hand under `kindgi dev`, with nothing
923
- committed.** `kindgi providers register` puts it in this worktree's dev
924
- database only. Another worktree, a `kindgi dev --reset`, or a
925
- teammate's `kindgi dev` has no provider, and its turns land on
926
- dev-echo (mistake 9). Declare the provider in the pack's config and
927
- commit it (step 2). `.kindgi/dev/providers.json` isn't the file to
928
- commit: it's `kindgi dev`'s local record of what it registered, and
929
- `.kindgi/` is gitignored.
72
+ | Anthropic, or another one-key preset: OpenAI, the Gemini API, Groq, OpenRouter | [path-a-anthropic.md](path-a-anthropic.md) |
73
+ | Any OpenAI-compatible endpoint by hand, or a local model through Ollama | [path-b-openai-compat.md](path-b-openai-compat.md) |
74
+ | A local in-process ONNX model, with the runtime run from source | [path-c-onnx.md](path-c-onnx.md) |
75
+ | Gemini on Vertex AI, signed in with Application Default Credentials | [path-d-vertex.md](path-d-vertex.md) |
76
+ | Amazon Bedrock | [bedrock.md](bedrock.md) |
77
+ | The pack on Kindgi Cloud: the key and the provider on `prod` | [kindgi-cloud.md](kindgi-cloud.md) |
78
+ | Where keys are read from, the registration body, fallbacks | [keys-and-registration.md](keys-and-registration.md) |
79
+ | Several providers or models: which one answers | [router.md](router.md) |
80
+ | An error, or replies still from dev-echo | [common-mistakes.md](common-mistakes.md) |
930
81
 
931
82
  ## Verifying end-to-end
932
83
 
@@ -937,8 +88,9 @@ kindgi dev
937
88
  # 1. Set the key (interactive, no-echo prompt)
938
89
  kindgi secrets set ANTHROPIC_API_KEY --env=local --scope=tenant
939
90
 
940
- # 2. Declare the provider in the pack's config (step 2), commit it, then
941
- # restart `kindgi dev`: its boot prints `anthropic: registered`
91
+ # 2. Declare the provider in the pack's config (step 2 of your path),
92
+ # commit it, then restart `kindgi dev`: its boot prints
93
+ # `anthropic: registered`
942
94
 
943
95
  # 3. Confirm it's there (list shows nested models per provider)
944
96
  kindgi providers list
@@ -954,19 +106,10 @@ there: set the key with `--env=prod` (step 1), register the provider by
954
106
  hand (`kindgi providers register --preset=anthropic --env=prod`), deploy
955
107
  with `kindgi deploy --hosted --env=prod`, then run the agent.
956
108
 
957
- If the run completes with `status: "completed"`, the whole chain works
958
- end-to-end. If it fails with `secret-not-found`, check point (1). If it
959
- fails with "no provider satisfies capability", check point (2). If it
960
- completes with dev-echo's canned reply, see mistake 9.
961
-
962
- ## References
963
-
964
- - `packages/adapters/model-anthropic/src/provider.ts` — Anthropic adapter
965
- - `packages/adapters/model-openai-compat/src/provider.ts` — Universal adapter, full list of tested endpoints in header comment
966
- - `packages/adapters/model-in-process/src/provider.ts` — Local ONNX adapter
967
- - `packages/adapters/model-bedrock/src/config.ts` — Amazon Bedrock's settings, and how it signs in
968
- - `packages/api/src/routes/providers.ts` — `POST /v1/providers` route
969
- - `packages/secrets-dotenv/src/index.ts` — Dev-mode secret binding
109
+ If the run completes with `status: "completed"`, the whole chain works.
110
+ `secret-not-found` points at the key, "no provider satisfies capability" at
111
+ the model's `features`, and dev-echo's canned reply at mistake 9 in
112
+ [common-mistakes.md](common-mistakes.md).
970
113
 
971
114
  ## When the framework itself is the problem
972
115