@kindgi/cli 0.2.0 → 0.3.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.
- package/README.md +12 -5
- package/dist/cli.js +4 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +75 -19
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/approval-wait.d.ts +90 -0
- package/dist/commands/approval-wait.d.ts.map +1 -0
- package/dist/commands/approval-wait.js +154 -0
- package/dist/commands/approval-wait.js.map +1 -0
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +131 -14
- package/dist/commands/artifacts.js.map +1 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +48 -5
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/deploy.js +7 -3
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/deployments.d.ts.map +1 -1
- package/dist/commands/deployments.js +36 -13
- package/dist/commands/deployments.js.map +1 -1
- package/dist/commands/doctor.d.ts +3 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +47 -14
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/eval-runs.d.ts.map +1 -1
- package/dist/commands/eval-runs.js +21 -9
- package/dist/commands/eval-runs.js.map +1 -1
- package/dist/commands/helpers.d.ts +31 -1
- package/dist/commands/helpers.d.ts.map +1 -1
- package/dist/commands/helpers.js +45 -2
- package/dist/commands/helpers.js.map +1 -1
- package/dist/commands/init.js +5 -4
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mail-triggers.d.ts.map +1 -1
- package/dist/commands/mail-triggers.js +27 -3
- package/dist/commands/mail-triggers.js.map +1 -1
- package/dist/commands/promote.d.ts.map +1 -1
- package/dist/commands/promote.js +133 -11
- package/dist/commands/promote.js.map +1 -1
- package/dist/commands/provenance.d.ts.map +1 -1
- package/dist/commands/provenance.js +13 -5
- package/dist/commands/provenance.js.map +1 -1
- package/dist/commands/providers.d.ts.map +1 -1
- package/dist/commands/providers.js +47 -4
- package/dist/commands/providers.js.map +1 -1
- package/dist/commands/schedules.d.ts.map +1 -1
- package/dist/commands/schedules.js +25 -9
- package/dist/commands/schedules.js.map +1 -1
- package/dist/commands/skills.d.ts +16 -0
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +122 -36
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/webhooks.d.ts.map +1 -1
- package/dist/commands/webhooks.js +21 -5
- package/dist/commands/webhooks.js.map +1 -1
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +7 -3
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -0
- package/dist/context.js.map +1 -1
- package/dist/dev/runtime-image.d.ts +1 -1
- package/dist/dev/runtime-image.js +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +14 -1
- package/dist/errors.js.map +1 -1
- package/dist/exports/plan.d.ts.map +1 -1
- package/dist/exports/plan.js +50 -0
- package/dist/exports/plan.js.map +1 -1
- package/dist/init/augment-scaffolder.d.ts +1 -1
- package/dist/init/augment-scaffolder.js +9 -8
- package/dist/init/augment-scaffolder.js.map +1 -1
- package/dist/init/java-augment.d.ts.map +1 -1
- package/dist/init/java-augment.js +7 -6
- package/dist/init/java-augment.js.map +1 -1
- package/dist/init/python-augment.d.ts.map +1 -1
- package/dist/init/python-augment.js +7 -6
- package/dist/init/python-augment.js.map +1 -1
- package/dist/init/scala-augment.js +7 -6
- package/dist/init/scala-augment.js.map +1 -1
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -1
- package/dist/providers/presets/anthropic.json +15 -3
- package/dist/providers/presets/azure-openai.json +10 -2
- package/dist/providers/presets/bedrock.json +6 -2
- package/dist/providers/presets/gemini-api.json +6 -2
- package/dist/providers/presets/openai.json +15 -3
- package/dist/sdk-skills/kindgi-authoring-agents/SKILL.md +21 -9
- package/dist/sdk-skills/kindgi-authoring-flows/SKILL.md +5 -3
- package/dist/sdk-skills/kindgi-authoring-guardrails/SKILL.md +202 -68
- package/dist/sdk-skills/kindgi-authoring-mcp-servers/SKILL.md +12 -188
- package/dist/sdk-skills/kindgi-authoring-mcp-servers/common-mistakes.md +44 -0
- package/dist/sdk-skills/kindgi-authoring-mcp-servers/how-it-works.md +41 -0
- package/dist/sdk-skills/kindgi-authoring-mcp-servers/other-clients.md +19 -0
- package/dist/sdk-skills/kindgi-authoring-mcp-servers/postgres.md +45 -0
- package/dist/sdk-skills/kindgi-authoring-mcp-servers/several-servers.md +32 -0
- package/dist/sdk-skills/kindgi-authoring-providers/SKILL.md +49 -906
- package/dist/sdk-skills/kindgi-authoring-providers/bedrock.md +64 -0
- package/dist/sdk-skills/kindgi-authoring-providers/common-mistakes.md +125 -0
- package/dist/sdk-skills/kindgi-authoring-providers/keys-and-registration.md +58 -0
- package/dist/sdk-skills/kindgi-authoring-providers/kindgi-cloud.md +36 -0
- package/dist/sdk-skills/kindgi-authoring-providers/path-a-anthropic.md +216 -0
- package/dist/sdk-skills/kindgi-authoring-providers/path-b-openai-compat.md +127 -0
- package/dist/sdk-skills/kindgi-authoring-providers/path-c-onnx.md +97 -0
- package/dist/sdk-skills/kindgi-authoring-providers/path-d-vertex.md +95 -0
- package/dist/sdk-skills/kindgi-authoring-providers/router.md +113 -0
- package/dist/sdk-skills/kindgi-authoring-tools/SKILL.md +12 -387
- package/dist/sdk-skills/kindgi-authoring-tools/common-mistakes.md +135 -0
- package/dist/sdk-skills/kindgi-authoring-tools/declarative-http.md +34 -0
- package/dist/sdk-skills/kindgi-authoring-tools/imperative-handler.md +65 -0
- package/dist/sdk-skills/kindgi-authoring-tools/iterating-and-testing.md +45 -0
- package/dist/sdk-skills/kindgi-authoring-tools/secrets-and-connections.md +79 -0
- package/dist/sdk-skills/kindgi-authoring-tools/tool-flags.md +45 -0
- package/dist/sdk-skills/kindgi-fix-corrections/SKILL.md +13 -178
- package/dist/sdk-skills/kindgi-fix-corrections/make-the-change.md +25 -0
- package/dist/sdk-skills/kindgi-fix-corrections/publish-and-promote.md +76 -0
- package/dist/sdk-skills/kindgi-fix-corrections/pull-corrections.md +48 -0
- package/dist/sdk-skills/kindgi-fix-corrections/worked-example.md +27 -0
- package/dist/sdk-skills/kindgi-framework-feedback/SKILL.md +10 -180
- package/dist/sdk-skills/kindgi-framework-feedback/common-mistakes.md +19 -0
- package/dist/sdk-skills/kindgi-framework-feedback/editing-feedback-file.md +33 -0
- package/dist/sdk-skills/kindgi-framework-feedback/fields-and-entry-shape.md +53 -0
- package/dist/sdk-skills/kindgi-framework-feedback/hearing-back.md +24 -0
- package/dist/sdk-skills/kindgi-framework-feedback/sending.md +50 -0
- package/dist/sdk-skills/kindgi-getting-started/SKILL.md +24 -21
- package/dist/sdk-skills/kindgi-java-authoring-agents/SKILL.md +12 -4
- package/dist/sdk-skills/kindgi-java-authoring-flows/SKILL.md +6 -4
- package/dist/sdk-skills/kindgi-java-authoring-guardrails/SKILL.md +33 -23
- package/dist/sdk-skills/kindgi-java-authoring-tools/SKILL.md +19 -12
- package/dist/sdk-skills/kindgi-java-getting-started/SKILL.md +16 -16
- package/dist/sdk-skills/kindgi-python-authoring-agents/SKILL.md +18 -6
- package/dist/sdk-skills/kindgi-python-authoring-flows/SKILL.md +6 -4
- package/dist/sdk-skills/kindgi-python-authoring-guardrails/SKILL.md +39 -30
- package/dist/sdk-skills/kindgi-python-authoring-tools/SKILL.md +19 -9
- package/dist/sdk-skills/kindgi-python-getting-started/SKILL.md +24 -21
- package/dist/sdk-skills/kindgi-scala-authoring-agents/SKILL.md +12 -4
- package/dist/sdk-skills/kindgi-scala-authoring-flows/SKILL.md +6 -4
- package/dist/sdk-skills/kindgi-scala-authoring-guardrails/SKILL.md +34 -23
- package/dist/sdk-skills/kindgi-scala-authoring-tools/SKILL.md +15 -9
- package/dist/sdk-skills/kindgi-scala-getting-started/SKILL.md +16 -16
- package/dist/sdk-skills/kindgi-using/SKILL.md +11 -11
- package/dist/templates/sample/README.md.tmpl +8 -7
- package/dist/templates/sample/agents/echo-agent/index.ts.tmpl +1 -1
- package/dist/templates/sample/kindgi.config.ts.tmpl +1 -1
- package/dist/templates/sample/package.json.tmpl +1 -1
- package/dist/templates/woocommerce/README.md.tmpl +9 -3
- 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.
|
|
16
|
-
sdk_version: "0.
|
|
15
|
+
version: "0.4.10"
|
|
16
|
+
sdk_version: "0.3.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
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
to the
|
|
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)").
|
|
192
|
-
https://docs.kindgi.com/v0.
|
|
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.
|
|
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.
|
|
17
|
-
sdk_version: "0.
|
|
16
|
+
version: "0.1.4"
|
|
17
|
+
sdk_version: "0.3.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
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
(
|
|
12
|
-
turn
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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.
|
|
21
|
-
sdk_version: "0.
|
|
21
|
+
version: "0.4.0"
|
|
22
|
+
sdk_version: "0.3.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
|
|
45
|
-
|
|
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.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
`guardrail
|
|
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
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
64
|
-
aren't in it. A check that every figure in
|
|
65
|
-
tool result or the user's message therefore
|
|
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,
|
|
85
|
-
evaluate })` from `@kindgi/sdk/define` returns a
|
|
86
|
-
`evaluate(config, trace, bindings)` resolves to
|
|
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
|
-
|
|
91
|
-
|
|
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`, `
|
|
146
|
-
`network`, and the check: its id (`check` may be the check
|
|
147
|
-
string, or the check object)
|
|
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
|
|
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
|
-
`
|
|
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,
|
|
208
|
-
|
|
209
|
-
|
|
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,
|
|
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
|
|
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 (
|
|
230
|
-
throw new Error(`acme.no-fabricated-quotes: ${
|
|
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 `
|
|
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
|
-
- **`
|
|
264
|
-
`
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
`
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
-
|
|
283
|
-
|
|
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 }, //
|
|
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 `
|
|
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`, `
|
|
506
|
+
`Guardrail`, `validateGuardrail` and the built-in checks are in
|
|
373
507
|
`@kindgi/guardrails`.
|
|
374
|
-
- API reference: https://docs.kindgi.com/v0.
|
|
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
|