@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
@@ -12,8 +12,8 @@ description: >
12
12
  kindgi-authoring-guardrails.
13
13
  type: core
14
14
  library: "@kindgi/sdk"
15
- version: "0.4.8"
16
- sdk_version: "0.2.0-rc.1"
15
+ version: "0.4.10"
16
+ sdk_version: "0.3.0-rc.0"
17
17
  pack_languages: [node]
18
18
  sources:
19
19
  - packages/agents/src/types.ts
@@ -111,10 +111,12 @@ export default defined.value;
111
111
  - **`guardrails`** — array of guardrail `id` strings. Resolved at turn
112
112
  start against the guardrails bound for the run (the tenant's
113
113
  registered guardrails). If a guardrail id isn't registered, the turn
114
- fails with `unresolved-guardrail`. Guardrails are evaluated once per
115
- turn, on the final response before it is stored — a blocking
116
- (`halt`) violation fails the turn and the response is never written
117
- to the conversation.
114
+ fails with `unresolved-guardrail`. Guardrails check the final
115
+ response before it is stored — a blocking (`halt`) violation fails
116
+ the turn and the response is never written to the conversation; a
117
+ `retry` one sends the response back to the model for a new one, up
118
+ to `retry.maxAttempts` answers in all (default 2); then the turn
119
+ fails with `guardrail-violation`.
118
120
  - **`parameters`** — typed inputs the caller supplies at invoke time.
119
121
  UI builds a "configure agent" form from these; runtime validates
120
122
  each required parameter is provided.
@@ -162,6 +164,16 @@ export default defined.value;
162
164
  retries the turn fails as before, with `toolRetries` on the error. A
163
165
  tenant's `tool-errors` policy can lower these (fewer retries, fewer
164
166
  kinds), never raise them.
167
+ - **`parallelToolCalls`** — optional, default on. When a model step asks for
168
+ several calls at once, calls to read-only tools (`mutating: false`, no
169
+ changing effect) that come one after another run at the same time, up to 8
170
+ of them, and a call to any other tool runs on its own, in the order the
171
+ model asked. A call that may wait, for another system's answer or for the
172
+ person who started the run to connect an app, runs on its own too. Once a
173
+ call needs an approval, the step's calls that haven't started run one at a
174
+ time. The results reach the conversation and the model in the model's order
175
+ either way. `false` runs every call on its own, for read-only tools that
176
+ mustn't overlap (a rate-limited API, say).
165
177
  - **`retrieval`** — what the agent reads from memory before each turn.
166
178
  Empty = no memory. Each intent is
167
179
  `{ types: ['acme.preference'], scope, mode?, limit? }`, with `scope`
@@ -188,8 +200,8 @@ export default defined.value;
188
200
  person's approval. Remembering is a tool call, so use a model with
189
201
  reliable tool calling. `{ instructionTypes: ['acme.policy'] }` turns a
190
202
  retrieved, verified fact of those types into an instruction
191
- ("Policies (verified)"). See
192
- https://docs.kindgi.com/v0.2/guides/agents/give-an-agent-memory/.
203
+ ("Policies (verified)"). "Give an agent memory" has the rest:
204
+ https://docs.kindgi.com/v0.3/guides/agents/give-an-agent-memory/
193
205
 
194
206
  ## What a turn receives
195
207
 
@@ -282,7 +294,7 @@ concerns, not author-time.
282
294
 
283
295
  - Type surface: `hover any @kindgi/sdk/define export` in your editor
284
296
  for full JSDoc.
285
- - API reference: https://docs.kindgi.com/v0.2/reference/typescript/sdk/kindgi/sdk/define/ (every `define*` spec, field by field).
297
+ - API reference: https://docs.kindgi.com/v0.3/reference/typescript/sdk/kindgi/sdk/define/ (every `define*` spec, field by field).
286
298
  - Common patterns: the `sample` template's `agents/echo-agent`
287
299
  demonstrates the smallest tool-calling shape.
288
300
 
@@ -13,8 +13,8 @@ description: >
13
13
  kindgi-authoring-agents.
14
14
  type: core
15
15
  library: "@kindgi/sdk"
16
- version: "0.1.3"
17
- sdk_version: "0.2.0-rc.1"
16
+ version: "0.1.4"
17
+ sdk_version: "0.3.0-rc.0"
18
18
  pack_languages: [node]
19
19
  sources:
20
20
  - packages/flow/src/types.ts
@@ -32,7 +32,9 @@ A **flow** is a versioned, durable graph of steps: tools (your code) and
32
32
  agents (a model's judgment), joined by edges that can carry conditions.
33
33
  It is **data**, not code: `defineFlow({...})` in `flows/<name>/index.ts`.
34
34
  The runtime runs it step by step, journals every step, and can resume a
35
- run that was interrupted. A run pins the flow version it started on.
35
+ run that was interrupted. A run keeps the flow version it started on.
36
+ A run that names no version takes the version pinned for its project
37
+ (or its org, or the tenant), else the newest version the project reaches.
36
38
 
37
39
  Use a flow when the order of the work is known: parse, then classify,
38
40
  then branch, then write. Use a single agent when the model should decide
@@ -3,22 +3,23 @@ name: kindgi-authoring-guardrails
3
3
  description: >
4
4
  Covers writing guardrails (safety checks) for a Kindgi pack: the check
5
5
  implementation via defineCheck from @kindgi/sdk/define, the guardrail
6
- declaration a pack file default-exports (the indexer's shape), the
7
- three kinds (zero-llm / llm-judge / external), actions, severity
8
- levels, scope selectors, config schemas via Zod or JSON Schema,
9
- validating a declaration with defineGuardrail from @kindgi/guardrails,
10
- and how guardrails reach agents. In 0.1 only the halt action acts
11
- (retry / escalate / compensate are only recorded: nothing runs the
12
- turn again), and a check sees only the current turn: its user message
13
- and tool results, not earlier turns'. Load this whenever you are authoring or
14
- editing code inside a pack's guardrails/ directory, defining a check,
15
- or wiring a guardrail onto an agent. Authoring tools is covered by
16
- kindgi-authoring-tools; authoring agents is covered by
6
+ declaration a pack file default-exports (the indexer's shape), the three
7
+ kinds (zero-llm / llm-judge / external), actions, severity levels, scope
8
+ selectors, config schemas via Zod or JSON Schema, validating a declaration
9
+ with validateGuardrail from @kindgi/guardrails, and how guardrails reach
10
+ agents. A guardrail judges the final answer, or with `at` a tool call
11
+ (before or after it) or each model reply that asks for tool calls. halt
12
+ fails the turn; retry sends the answer or reply back, refuses the call or
13
+ withholds its result; escalate / compensate are final-answer only and
14
+ only recorded. A check sees only the current turn. Load this whenever you
15
+ are authoring or editing code inside a pack's guardrails/ directory,
16
+ defining a check, or wiring a guardrail onto an agent. Authoring tools is
17
+ covered by kindgi-authoring-tools; authoring agents by
17
18
  kindgi-authoring-agents.
18
19
  type: core
19
20
  library: "@kindgi/sdk"
20
- version: "0.3.10"
21
- sdk_version: "0.2.0-rc.1"
21
+ version: "0.4.0"
22
+ sdk_version: "0.3.0-rc.0"
22
23
  pack_languages: [node]
23
24
  sources:
24
25
  - packages/guardrails/src/types.ts
@@ -41,29 +42,61 @@ A **guardrail** is a safety rule an agent turn must satisfy. It combines
41
42
  a **check** (the function that inspects the turn's trace) with an
42
43
  **action** (what happens when the check fails). In a pack, a guardrail
43
44
  lives at `guardrails/<name>/index.ts`. For an agent turn, the runtime
44
- evaluates every guardrail the agent lists once, on the final response,
45
- before the response is stored.
45
+ evaluates every guardrail the agent lists where its `at` says: once, on
46
+ the final response before it's stored (`'final-answer'`, the default), or
47
+ in the agent loop, on each tool call before it runs (`'before-tool'`),
48
+ on each call's result before the model sees it (`'after-tool'`), or on
49
+ each model reply that asks for calls, before any of them runs
50
+ (`'model-step'`).
46
51
 
47
52
  ## What a guardrail can do in 0.1
48
53
 
49
- Read this before you plan a guardrail. Three limits decide what can be
50
- built:
51
-
52
- - **Only `halt` acts.** A failed `halt` guardrail fails the turn with
53
- `guardrail-violation`, and the response is not stored. `retry`,
54
+ Read this before you plan a guardrail. These decide what can be built:
55
+
56
+ - **`halt` and `retry` act.** At the final answer, a failed `halt`
57
+ guardrail fails the turn with `guardrail-violation`, and the response is
58
+ not stored. A failed `retry` guardrail sends the answer back to the
59
+ model, with the check's reason in a user message, for a new one; the
60
+ failed answer isn't stored. When it has failed `retry.maxAttempts`
61
+ answers (the first one included; 2 when left out), the turn fails with
62
+ `guardrail-violation` and `details.attempts`. Each new answer is a model
63
+ call that the agent's `budget.maxSteps` counts. With a `retry` guardrail
64
+ at the final answer, the `halt` and `retry` guardrails there judge each
65
+ answer, a judge's calls included, which `budget.maxCostUsd` doesn't
66
+ count; the others judge only the answer that's sent. A new answer can
67
+ call tools again, side-effecting ones included, with approvals as usual.
54
68
  `escalate` and `compensate` are recorded on the turn's violations, as
55
- `log-only` is, and the turn completes: nothing runs the turn again,
56
- escalates or calls a compensating tool. So "block the reply and make the
57
- agent try again" can't be a guardrail. Use `halt`: the error names the
58
- guardrail and the check's reason, and the message is sent again (by the
59
- person, or by your app when it gets `guardrail-violation`).
69
+ `log-only` is, and the turn completes: nothing escalates or calls a
70
+ compensating tool. So "block the reply and make the agent try again" is
71
+ a `retry` guardrail.
72
+ - **They act in the loop too.** At `'before-tool'` (after the call's
73
+ access check, before any approval is asked), `halt` fails the turn and
74
+ `retry` refuses the call: it doesn't run, and the model gets the reason
75
+ as its result. At `'after-tool'`, `retry` withholds the result: the
76
+ reason is stored as the call's result in its place, so neither the model
77
+ nor a later turn's history sees it; a replay's recorded result is judged
78
+ as a live one is. At `'model-step'`, `retry` sends the reply back: none
79
+ of its calls runs, nothing of it is stored, and the model writes a new
80
+ reply. `retry.maxAttempts` counts replies across the turn: each reply
81
+ the guardrail sent back, or one with a call it refused or a result it
82
+ withheld, counts once, so two calls of one reply that it refuses are one
83
+ attempt. When they run out, or the turn has no steps left, the turn
84
+ fails with `attempts`, the replies there were. A tool's own result never
85
+ counts as one. A `log-only` failure joins the turn's `violations`. A
86
+ dry run skips the model call, so no guardrail runs at these points in
87
+ one.
88
+ `escalate` and `compensate` are refused away from the final answer
89
+ (by `kindgi build`, `deployment-validation-failed` on deploy,
90
+ `400 validation-failed` on register). A failure names `details.point` and, at a tool point,
91
+ `details.callId`. A resumed turn reuses each point's recorded outcome:
92
+ no judge is asked or billed twice.
60
93
  - **A check sees one turn.** The trace holds the current turn only: its
61
94
  user message (`trace.userInput`), its tool calls and their results
62
95
  (`trace.toolCalls`, `trace.toolResults`) and its response
63
- (`trace.output`). Earlier turns' messages, tool results and responses
64
- aren't in it. A check that every figure in the response comes from a
65
- tool result or the user's message therefore blocks a response that
66
- repeats a figure seen only in an earlier turn.
96
+ (`trace.output`, at the final answer only). Earlier turns' messages,
97
+ tool results and responses aren't in it. A check that every figure in
98
+ the response comes from a tool result or the user's message therefore
99
+ blocks a response that repeats a figure seen only in an earlier turn.
67
100
  - **A check's in-memory state doesn't last.** The check runs in the pack
68
101
  service's process, so anything it keeps at module level (say, a record
69
102
  per `trace.conversationId`) is lost when that process is replaced: under
@@ -81,14 +114,20 @@ message, so a response that repeats them is still blocked.
81
114
 
82
115
  ## Mental model: guardrail vs check
83
116
 
84
- - **Check** — the implementation. `defineCheck({ id, kind, configSchema?,
85
- evaluate })` from `@kindgi/sdk/define` returns a registered check whose
86
- `evaluate(config, trace, bindings)` resolves to `{ passed, reason? }`.
87
- Zero-llm checks are pure over the trace.
117
+ - **Check** — the implementation. `defineCheck({ id, kind, points?,
118
+ configSchema?, evaluate })` from `@kindgi/sdk/define` returns a
119
+ registered check whose `evaluate(config, trace, bindings)` resolves to
120
+ `{ passed, reason? }`. Zero-llm checks are pure over the trace. `points`
121
+ names where it can run, from `'before-tool'`, `'after-tool'`,
122
+ `'model-step'` and `'final-answer'`; left out, it's `['final-answer']`.
123
+ An empty list, an unknown point or one named twice throws
124
+ `invalid-check-definition`. A check that reads `trace.output` runs at
125
+ the final answer; one that reads `trace.call`, `trace.result` or
126
+ `trace.step` names the points where they're set.
88
127
  - **Guardrail** — the declaration: `id`, `kind`, the `check` it uses,
89
- the check's `config`, and `action` / `severity` / `scope`. Its type is
90
- `Guardrail` from `@kindgi/guardrails`. One check can back many
91
- guardrails with different configs.
128
+ the check's `config`, `at`, and `action` / `severity` / `scope`. Its type is `Guardrail` from `@kindgi/guardrails`. One check
129
+ can back many guardrails with different configs, each at one of the
130
+ check's points.
92
131
  - **Built-in checks** (`BUILT_IN_CHECK_IDS` in `@kindgi/guardrails`):
93
132
  `must-cite`, `never-call-tool`, `max-tool-calls`, `output-matches`,
94
133
  `tool-order`, `required-substring`, `forbidden-substring`. A guardrail
@@ -142,16 +181,27 @@ How the pack tooling reads this file:
142
181
  - The **indexer** (`packages/handler-runtime/src/kindgi-index.ts`)
143
182
  recognises a guardrail by a default export with a `kind` and an
144
183
  `action` object carrying `on-violation`. It records `id`, `name`,
145
- `kind`, `action`, `severity`, `scope`, `sandbox` / `limits` /
146
- `network`, and the check: its id (`check` may be the check id as a
147
- string, or the check object) and its config schema (from the check's
184
+ `kind`, `action`, `severity`, `scope`, `at`, `sees`, `budget`, `sandbox` /
185
+ `limits` / `network`, and the check: its id (`check` may be the check
186
+ id as a string, or the check object), its points (`checkPoints`, for a
187
+ check the module ships) and its config schema (from the check's
148
188
  `configZod`, `configSchema` or `configJsonSchema`, or a top-level
149
189
  `configZod` / `configSchema`), and the declaration's `config` — what
150
- the check runs with. It does not record `description`, `budget` or
190
+ the check runs with. It does not record `description` or
151
191
  `judgeCapabilities`. It checks `config` (none counts as `{}`) against
152
192
  the config schema: a config that doesn't fit, or a required field with
153
193
  no default left out, is a file error naming where, and `kindgi build`
154
- refuses the pack.
194
+ refuses the pack. It also refuses an `at` it doesn't know, a malformed
195
+ `scope.tools` or `points`, what a point can't do in the deploy's paths
196
+ and words (`guardrail <id>: /action/on-violation: "escalate" is
197
+ recorded at the final answer only, not at "before-tool"`, and
198
+ `/scope/tools` for `scope.tools` away from the tool points), and an
199
+ `at` the module's check doesn't run at (`guardrail <id> runs at
200
+ 'before-tool', which isn't one of its check's points (final-answer)`).
201
+ A built-in named as a string isn't checked there: deploy refuses one at
202
+ a point it doesn't run at (at `/at`, `deployment-validation-failed`),
203
+ and registering answers `422 guardrail-config-invalid`; registering
204
+ answers `400 validation-failed` for what a point can't do.
155
205
  - The **pack service** loads the same module to run the check. It uses
156
206
  the module's `evaluate` export, or the `default` / `check` export when
157
207
  that is a function or has an `evaluate` method — here, the named
@@ -160,6 +210,58 @@ How the pack tooling reads this file:
160
210
  declares no config gets them all), and a config that doesn't fit
161
211
  refused, naming where.
162
212
 
213
+ ## A guardrail in the agent loop
214
+
215
+ ```ts
216
+ // guardrails/refund-limit/index.ts
217
+ import { defineCheck } from '@kindgi/sdk/define';
218
+ import { z } from 'zod';
219
+
220
+ export const check = defineCheck({
221
+ id: 'acme.checks.refund-limit',
222
+ kind: 'zero-llm',
223
+ points: ['before-tool'],
224
+ configSchema: z.object({ maxAmount: z.number().positive().default(100) }),
225
+ evaluate: async (config, trace) => {
226
+ // At 'before-tool', trace.call is the call it judges; trace.toolCalls
227
+ // holds only the calls that ran, never this one.
228
+ const amount = trace.call?.arguments.amount;
229
+ if (typeof amount === 'number' && amount > config.maxAmount) {
230
+ return { passed: false, reason: `The refund is ${amount}; the limit is ${config.maxAmount}.` };
231
+ }
232
+ return { passed: true };
233
+ },
234
+ });
235
+
236
+ export default {
237
+ id: 'acme.refund-limit',
238
+ kind: 'zero-llm',
239
+ check,
240
+ at: 'before-tool',
241
+ scope: { tools: ['acme.orders.refund'] },
242
+ action: { 'on-violation': 'retry' },
243
+ severity: 'error',
244
+ };
245
+ ```
246
+
247
+ What the trace holds at each point:
248
+
249
+ - `trace.at` — the point. Absent at the final answer.
250
+ - `trace.call` — at `'before-tool'` and `'after-tool'`:
251
+ `{ callId, toolId, toolName, toolVersion?, arguments }`.
252
+ - `trace.result` — at `'after-tool'`: `{ output }`, or
253
+ `{ error: { code?, message } }` when the call threw.
254
+ - `trace.step` — at `'model-step'`: `{ text, toolCalls }`; `text` is
255
+ empty when the reply only asks for calls.
256
+
257
+ Away from the final answer there's no `trace.output`, and `toolCalls` /
258
+ `toolResults` hold only the calls that ran. At the final answer the
259
+ trace holds every call the model asked for. A failed `halt` at a loop
260
+ point reads
261
+ `Turn blocked by guardrail 'acme.refund-limit' before calling acme.orders.refund: …`
262
+ (`after <tool> ran`, `on a model reply`; out of attempts,
263
+ `…, after 2 replies: …`).
264
+
163
265
  ## Using a built-in check
164
266
 
165
267
  Name the built-in as the guardrail's `check`, give its `config`, and ship
@@ -191,6 +293,15 @@ Each built-in's `config` (tool lists hold tool ids, as in
191
293
  | `required-substring` | the answer is empty, or lacks any of `patterns` | `patterns: string[]`, one or more non-empty (plain text), `caseSensitive?: boolean` (false) |
192
294
  | `forbidden-substring` | the answer contains any of `patterns` | `patterns: string[]`, one or more non-empty (plain text), `caseSensitive?: boolean` (false) |
193
295
 
296
+ Where each runs (the guardrail's `at`): `must-cite` and
297
+ `required-substring` at `'final-answer'` only. `never-call-tool`,
298
+ `max-tool-calls` and `tool-order` at `'before-tool'` too, judging that
299
+ call against the turn's earlier calls, so a call they fail never runs.
300
+ `output-matches` and `forbidden-substring` at `'model-step'` and
301
+ `'after-tool'` too: they read the reply's text, or the call's result (a
302
+ string as it is, anything else as JSON, an error's message). A reply that
303
+ only asks for calls has no text and passes `output-matches`.
304
+
194
305
  A config the check doesn't take is refused: a required setting left out,
195
306
  the wrong type, a setting the check doesn't know, or a regex that doesn't
196
307
  compile. Registering it answers `422 guardrail-config-invalid` (each
@@ -202,20 +313,22 @@ turn. Each built-in's JSON Schema is its registered check's
202
313
 
203
314
  ## Validating a declaration in-process
204
315
 
205
- `defineGuardrail(spec, checks)` from `@kindgi/guardrails` validates a
316
+ `validateGuardrail(spec, checks)` from `@kindgi/guardrails` validates a
206
317
  `Guardrail` against the wire schema and a check registry: the check id
207
- must be registered, the check's `kind` must match, and `config` must
208
- pass the check's config schema. It returns a `Result`; use it in tests
209
- or wherever guardrails are registered in-process.
318
+ must be registered, the check's `kind` must match, `config` must pass
319
+ the check's config schema, and `at` must be one of the check's points
320
+ (the problem at `/at`). `escalate` and `compensate` must be at the final
321
+ answer, and `scope.tools` at a tool point. It returns a `Result`; use it
322
+ in tests or wherever guardrails are registered in-process.
210
323
 
211
324
  ```ts
212
- import { createCheckRegistry, defineGuardrail } from '@kindgi/guardrails';
325
+ import { createCheckRegistry, validateGuardrail } from '@kindgi/guardrails';
213
326
  import type { GuardrailId } from '@kindgi/sdk/types';
214
327
 
215
328
  import { check } from './index.js';
216
329
 
217
330
  const checks = createCheckRegistry([check]); // built-in checks are included
218
- const defined = defineGuardrail(
331
+ const validated = validateGuardrail(
219
332
  {
220
333
  id: 'acme.no-fabricated-quotes' as GuardrailId,
221
334
  kind: 'zero-llm',
@@ -226,8 +339,8 @@ const defined = defineGuardrail(
226
339
  },
227
340
  checks,
228
341
  );
229
- if (defined.kind === 'err') {
230
- throw new Error(`acme.no-fabricated-quotes: ${defined.error.message}`);
342
+ if (validated.kind === 'err') {
343
+ throw new Error(`acme.no-fabricated-quotes: ${validated.error.message}`);
231
344
  }
232
345
  ```
233
346
 
@@ -252,7 +365,7 @@ available to the runtime that evaluates it.
252
365
  - **`check`** — the id of a registered check (built-in, or one built
253
366
  with `defineCheck`). In a pack file it may also be the check object.
254
367
  - **`config`** — the check's parameters, validated against the check's
255
- `configSchema`: by `defineGuardrail`, and in a pack when the pack is
368
+ `configSchema`: by `validateGuardrail`, and in a pack when the pack is
256
369
  indexed (a config that doesn't fit is a file error naming where, and
257
370
  `kindgi build` refuses the pack; see "How the pack tooling reads this
258
371
  file"). In a pack, the declaration's `config` goes into the index and
@@ -260,17 +373,29 @@ available to the runtime that evaluates it.
260
373
  check's `evaluate` receives the config its schema resolves, with the
261
374
  schema's defaults applied; a check written without `defineCheck` gets
262
375
  the config as declared.
263
- - **`action.on-violation`** — `'halt'`, `'retry'` (with
264
- `retry.maxAttempts`, 1–10), `'escalate'` (with `escalateTo`),
265
- `'log-only'`, `'compensate'` (with `compensateWith`, a tool id). In an
266
- agent turn, a failed `halt` guardrail fails the turn with
267
- `guardrail-violation` and the response is not stored; failures with
268
- any other action are reported in `AgentTurnResult.violations` and the
269
- turn completes. The action handlers in `@kindgi/guardrails`
270
- (`retryHandler`, `escalateHandler`, `compensateHandler`, …) record the
271
- intent for callers that act on it. In 0.1 the runtime acts only on
272
- `halt`: `retry`, `escalate` and `compensate` are recorded on the
273
- violation, with no second attempt, escalation or compensating call.
376
+ - **`at`** — where in the turn it runs: `'final-answer'` (the default),
377
+ `'before-tool'`, `'after-tool'` or `'model-step'`. It must be one of
378
+ its check's `points`. At a tool point or a model reply, a check that
379
+ can't run (a point its check doesn't run at as
380
+ `check-point-unsupported`, or its time ran out as `check-timed-out`)
381
+ acts as a failed one: `halt` fails the turn, `retry` refuses, withholds
382
+ or sends back with the reason `its check couldn't run (<code>): <message>`.
383
+ A check that isn't registered counts there as one that runs at the final
384
+ answer only, so it fails as `check-point-unsupported`, saying no check by
385
+ that name is registered; at the final answer it's `unknown-check`. At the
386
+ final answer, a `retry` whose check can't run goes on as if it passed.
387
+ - **`action.on-violation`** — `'halt'`, `'retry'` (with `retry.maxAttempts`,
388
+ 1–10), `'escalate'` (with `escalateTo`), `'log-only'`, `'compensate'`
389
+ (with `compensateWith`, a tool id). In an agent turn, a failed `halt`
390
+ guardrail fails the turn with `guardrail-violation` and the response is
391
+ not stored; a failed `retry` guardrail sends the answer back for a new
392
+ one, or at a loop point refuses the call, withholds its result or sends
393
+ the reply back (above); failures with any other action are reported in
394
+ `AgentTurnResult.violations` and the turn completes. The action handlers
395
+ in `@kindgi/guardrails` (`retryHandler`, `escalateHandler`,
396
+ `compensateHandler`, …) record the intent for callers that act on it.
397
+ `escalate` and `compensate` are recorded on the violation, with no
398
+ escalation or compensating call in 0.1.
274
399
  - **`severity`** — `'info'` / `'warn'` / `'error'` (the default) /
275
400
  `'critical'`. Orthogonal to `action`: logs and dashboards group by
276
401
  severity; execution follows the action. A `log-only` guardrail can
@@ -278,9 +403,18 @@ available to the runtime that evaluates it.
278
403
  - **`scope`** — when the guardrail applies. `{ when: 'always' }` fires
279
404
  everywhere; `{ when: 'ci-only' }` blocks CI but not runtime;
280
405
  `{ when: 'runtime-only' }` enforces at runtime but not CI. `agents`,
281
- `flows` and `tenants` lists narrow it further.
282
- - **`budget`** — `{ maxCostUsd?, maxLatencyMs? }`, relevant to
283
- `llm-judge`. Declarative: the runtime does not enforce it.
406
+ `flows` and `tenants` lists narrow it further. `tools`, only at
407
+ `'before-tool'` and `'after-tool'`, lists the tool ids it checks; a
408
+ trailing `*` matches every id with that prefix (`'acme.crm.*'`), a `*`
409
+ anywhere else or an empty list is refused, and left out it checks every
410
+ tool.
411
+ - **`budget`** — `{ maxCostUsd?, maxLatencyMs? }`. The agent loop
412
+ enforces `maxLatencyMs` before a tool call, after it and on each model
413
+ reply (`at`), with a default per kind when it's absent (10 s
414
+ `zero-llm`, 60 s `llm-judge`, 30 s any other); a check that runs past
415
+ it fails as one that couldn't run (`check-timed-out`), handled by the
416
+ guardrail's action. It isn't enforced at the final answer, and
417
+ `maxCostUsd` isn't enforced.
284
418
  - **`judgeCapabilities`** — for `llm-judge`: the capability
285
419
  declaration used to route the judge model.
286
420
 
@@ -308,7 +442,7 @@ export const toneProfessional: Guardrail = {
308
442
  judgeCapabilities: { needs: [{ feature: 'structured-output' }] },
309
443
  action: { 'on-violation': 'log-only' },
310
444
  severity: 'warn',
311
- budget: { maxCostUsd: 0.01, maxLatencyMs: 5000 }, // declarative, not enforced
445
+ budget: { maxCostUsd: 0.01, maxLatencyMs: 5000 }, // maxLatencyMs: enforced at the loop's points only
312
446
  };
313
447
  ```
314
448
 
@@ -361,7 +495,7 @@ ready for the stricter enforcement.
361
495
  silent typos. Prefer Zod for TS-side inference on
362
496
  `evaluate(config, ...)`.
363
497
 
364
- 5. **Ignoring the `Result` from `defineGuardrail`.** It returns
498
+ 5. **Ignoring the `Result` from `validateGuardrail`.** It returns
365
499
  `Result<Guardrail, …>`; check `kind` and throw at load time.
366
500
  `defineCheck` itself throws when its `configSchema` can't be
367
501
  compiled.
@@ -369,9 +503,9 @@ ready for the stricter enforcement.
369
503
  ## References
370
504
 
371
505
  - Type surface: hover any `@kindgi/sdk/define` export for full JSDoc;
372
- `Guardrail`, `defineGuardrail` and the built-in checks are in
506
+ `Guardrail`, `validateGuardrail` and the built-in checks are in
373
507
  `@kindgi/guardrails`.
374
- - API reference: https://docs.kindgi.com/v0.2/reference/typescript/sdk/kindgi/sdk/define/
508
+ - API reference: https://docs.kindgi.com/v0.3/reference/typescript/sdk/kindgi/sdk/define/
375
509
  - Built-in check implementations: `packages/guardrails/src/checks.ts`.
376
510
 
377
511
  ## When the framework itself is the problem