@cloc/provider-ai-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent.d.ts +93 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +359 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/config.d.ts +85 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +101 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/gateway.d.ts +74 -0
  11. package/dist/gateway.d.ts.map +1 -0
  12. package/dist/gateway.js +96 -0
  13. package/dist/gateway.js.map +1 -0
  14. package/dist/index.d.ts +47 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +46 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/memory-tool.d.ts +63 -0
  19. package/dist/memory-tool.d.ts.map +1 -0
  20. package/dist/memory-tool.js +183 -0
  21. package/dist/memory-tool.js.map +1 -0
  22. package/dist/output.d.ts +49 -0
  23. package/dist/output.d.ts.map +1 -0
  24. package/dist/output.js +41 -0
  25. package/dist/output.js.map +1 -0
  26. package/dist/plugin.d.ts +74 -0
  27. package/dist/plugin.d.ts.map +1 -0
  28. package/dist/plugin.js +86 -0
  29. package/dist/plugin.js.map +1 -0
  30. package/dist/request.d.ts +82 -0
  31. package/dist/request.d.ts.map +1 -0
  32. package/dist/request.js +80 -0
  33. package/dist/request.js.map +1 -0
  34. package/dist/safety.d.ts +54 -0
  35. package/dist/safety.d.ts.map +1 -0
  36. package/dist/safety.js +0 -0
  37. package/dist/safety.js.map +1 -0
  38. package/dist/secrets.d.ts +51 -0
  39. package/dist/secrets.d.ts.map +1 -0
  40. package/dist/secrets.js +47 -0
  41. package/dist/secrets.js.map +1 -0
  42. package/dist/skills-loader.d.ts +76 -0
  43. package/dist/skills-loader.d.ts.map +1 -0
  44. package/dist/skills-loader.js +99 -0
  45. package/dist/skills-loader.js.map +1 -0
  46. package/dist/stream.d.ts +58 -0
  47. package/dist/stream.d.ts.map +1 -0
  48. package/dist/stream.js +59 -0
  49. package/dist/stream.js.map +1 -0
  50. package/dist/tokens.d.ts +17 -0
  51. package/dist/tokens.d.ts.map +1 -0
  52. package/dist/tokens.js +17 -0
  53. package/dist/tokens.js.map +1 -0
  54. package/dist/tool-loop.d.ts +98 -0
  55. package/dist/tool-loop.d.ts.map +1 -0
  56. package/dist/tool-loop.js +210 -0
  57. package/dist/tool-loop.js.map +1 -0
  58. package/dist/trace.d.ts +78 -0
  59. package/dist/trace.d.ts.map +1 -0
  60. package/dist/trace.js +39 -0
  61. package/dist/trace.js.map +1 -0
  62. package/dist/validate.d.ts +54 -0
  63. package/dist/validate.d.ts.map +1 -0
  64. package/dist/validate.js +81 -0
  65. package/dist/validate.js.map +1 -0
  66. package/package.json +55 -0
  67. package/src/agent.ts +487 -0
  68. package/src/config.ts +147 -0
  69. package/src/gateway.ts +126 -0
  70. package/src/index.ts +101 -0
  71. package/src/memory-tool.ts +219 -0
  72. package/src/output.ts +67 -0
  73. package/src/plugin.ts +123 -0
  74. package/src/request.ts +178 -0
  75. package/src/safety.ts +0 -0
  76. package/src/secrets.ts +71 -0
  77. package/src/skills-loader.ts +153 -0
  78. package/src/stream.ts +80 -0
  79. package/src/tokens.ts +82 -0
  80. package/src/tool-loop.ts +268 -0
  81. package/src/trace.ts +87 -0
  82. package/src/validate.ts +118 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @cloc/provider-ai-sdk · tool-loop.ts — the budgeted render-time tool loop over the AI SDK v6
3
+ * `ToolLoopAgent` surface (007-provider-ai-sdk; 027-agentic-primitives §16b.3, §17, §9.1).
4
+ *
5
+ * The render Agent runs a bounded `call → execute-tools → feed-results → repeat` loop: the model
6
+ * may CALL a declared tool, its result is FED BACK as DATA (through safety.ts), and the loop runs
7
+ * until `stopWhen` terminates it (default `stepCountIs(20)` — the SAME trajectory budget that bounds
8
+ * the `0` console agent, §9.1, FR-013). `prepareStep` runs BEFORE each step to swap the model,
9
+ * restrict `activeTools`/`toolChoice`, and select which skills/memory enter that step (FR-011). A
10
+ * tool WITHOUT `execute` is a STOP SIGNAL (the model emits the call and the loop ends). Every tool
11
+ * call clears the §58 policy gate BEFORE execution; a denial DEGRADES (FR-014, FR-021).
12
+ *
13
+ * AI SDK v6 surface (verified against the installed ai@6 .d.ts, not stale memory):
14
+ * - `ToolLoopAgent` is the default `Agent` impl; `tool()`/`dynamicTool({ description, inputSchema,
15
+ * execute })` (v6 renamed `parameters` → `inputSchema`); multi-step loops use
16
+ * `stopWhen: stepCountIs(n)` (v6 removed `maxSteps`); `hasToolCall(name)` / `isLoopFinished()`
17
+ * are the other stop conditions; `StopCondition<TOOLS>` is `(opts) => boolean | PromiseLike`;
18
+ * `prepareStep({ steps, stepNumber, model, messages }) => { model?, toolChoice?, activeTools? }`.
19
+ * - The non-streaming `generateText`/`streamText` calls in agent.ts already drive this loop via
20
+ * `tools` + `stopWhen`; this module assembles those vendor values from the core contract.
21
+ *
22
+ * Vendor edge: this is one of the THREE files (with memory-tool.ts, skills-loader.ts) that touch
23
+ * `ai`. The core's `StopCondition`/`PrepareStep`/`ToolDef` shapes stay vendor-free (@cloc/core).
24
+ */
25
+ import type { ToolSet as AiToolSet, StopCondition as AiStopCondition, PrepareStepFunction, LanguageModel } from "ai";
26
+ import type { AgentTool } from "./request.js";
27
+ import type { StopCondition, PrepareStep, StepDirective, PolicyGateHook, CoreToolSet } from "./tokens.js";
28
+ /** Observable loop event the trace + stream consume (data-model §4). */
29
+ export type LoopEvent = {
30
+ kind: "tool-call";
31
+ tool: string;
32
+ args: unknown;
33
+ } | {
34
+ kind: "tool-result";
35
+ tool: string;
36
+ result: unknown;
37
+ };
38
+ /**
39
+ * Default loop bound — the SAME trajectory budget that bounds the `0` console agent (§9.1). v6's
40
+ * own default is `stepCountIs(20)`; we keep a smaller render default but the render layer MUST set
41
+ * `stopWhen` explicitly (FR-013). Kept for the legacy single-turn path; the agentic render path
42
+ * maps `GenOpts.stopWhen` through {@link toAiStopWhen}.
43
+ */
44
+ export declare const DEFAULT_MAX_STEPS = 8;
45
+ /**
46
+ * Build the AI SDK v6 `ToolSet` from the turn's legacy declared tools (the pre-027 shape on
47
+ * `AgentTurn.tools`). Each tool's `execute`:
48
+ * 1. emits a `tool-call` event,
49
+ * 2. invokes the user-provided `invoke` (the actual DataSource query / declared action),
50
+ * 3. frames the result as DATA (safety.ts) and emits a `tool-result` event,
51
+ * 4. returns the framed result to the loop as the observation (FR-003 feedback path).
52
+ *
53
+ * `onEvent` lets the agent forward `tool-call`/`tool-result` to the stream + trace in order. When a
54
+ * {@link PolicyGateHook} is supplied, each call clears the §58 gate FIRST and a denial DEGRADES
55
+ * (the tool returns an attributable denial as DATA, never throwing — FR-014, FR-021).
56
+ */
57
+ export declare function buildToolSet(tools: ReadonlyArray<AgentTool>, onEvent: (event: LoopEvent) => void, gate?: PolicyGateHook): AiToolSet;
58
+ /**
59
+ * Build the AI SDK v6 `ToolSet` from the 027 core {@link CoreToolSet} (`GenOpts.tools`) — the three
60
+ * sources (`plugin` / `capability` / `wired`) all join the render loop (FR-012). Each `ToolDef`'s
61
+ * `execute` is gate-checked then framed as DATA; a tool WITHOUT `execute` is registered as a STOP
62
+ * SIGNAL (v6: a tool with no `execute` ends the loop — §16b.3, isStopTool). Returns `{}` for an
63
+ * empty/undefined set so a baseline render adds no tools (FR-002).
64
+ */
65
+ export declare function buildAgenticToolSet(tools: CoreToolSet | undefined, onEvent: (event: LoopEvent) => void, gate: PolicyGateHook): AiToolSet;
66
+ /** The v6 multi-step stop condition (replaces the removed `maxSteps`). Legacy single-turn default. */
67
+ export declare function stopAfter(maxSteps?: number): AiStopCondition<AiToolSet>;
68
+ /**
69
+ * Map the core vendor-free {@link StopCondition} (`GenOpts.stopWhen`) to the AI SDK v6 stop
70
+ * condition — `stepCountIs(n)` / `hasToolCall(name)` / `isLoopFinished()` (§16b.3, §9.1). When the
71
+ * render layer sets no budget we fall back to the loop default so it can never thrash unbounded
72
+ * (FR-013, NFR-005).
73
+ *
74
+ * TODO(027/003/012, NEEDS CLARIFICATION): cache keying of a tool-using (non-pure) render — whether
75
+ * tool outputs fold into `dataVersion`, the render is uncacheable, or it is cached per tool-result
76
+ * hash. Owned by 003/012; routed to Governance.
77
+ * TODO(027, NEEDS CLARIFICATION): degraded-render behavior when the budget is exhausted BEFORE a
78
+ * complete `Output` (cheaper tier? best partial-but-valid plan?). §16b.3 guarantees boundedness but
79
+ * not the exact fallback; today we let `validateOrRepair` (validate.ts) reject an incomplete plan so
80
+ * an invalid/partial structure is NEVER emitted (Principle 3). Routed to Governance.
81
+ */
82
+ export declare function toAiStopWhen(stopWhen: StopCondition | undefined, fallbackSteps?: number): AiStopCondition<AiToolSet>;
83
+ /**
84
+ * Adapt the core vendor-free {@link PrepareStep} (per-step context engineering, FR-011) to the AI
85
+ * SDK v6 `prepareStep` callback. Runs BEFORE each step and returns the per-step overrides v6
86
+ * honors: `model` (swap), `activeTools` (restrict), `toolChoice` (restrict). Skill/memory SELECTION
87
+ * carried on the core {@link StepDirective} is applied by the caller (it owns the typed tool/skill
88
+ * maps); v6's surface only takes the model/tools knobs, so those pass straight through. Returns
89
+ * `undefined` (use outer settings) when the directive sets nothing for the step.
90
+ *
91
+ * `resolveModel` turns a core `ModelRef`-bearing directive into the routed v6 `LanguageModel`; when
92
+ * absent the step keeps the outer model. The per-step skill/memory selection is surfaced via
93
+ * `onDirective` so the runner can re-frame the prompt fragment / gate the chosen skills.
94
+ */
95
+ export declare function toAiPrepareStep(prepareStep: PrepareStep | undefined, resolveModel: (directive: StepDirective) => PrepareStepResultModel | undefined, onDirective?: (step: number, directive: StepDirective) => void): PrepareStepFunction<AiToolSet> | undefined;
96
+ /** The v6 `LanguageModel` a per-step model swap resolves to (kept vendor-internal). */
97
+ export type PrepareStepResultModel = LanguageModel;
98
+ //# sourceMappingURL=tool-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-loop.d.ts","sourceRoot":"","sources":["../src/tool-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EACV,OAAO,IAAI,SAAS,EAEpB,aAAa,IAAI,eAAe,EAChC,mBAAmB,EAEnB,aAAa,EACd,MAAM,IAAI,CAAC;AACZ,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,EACd,WAAW,EAEZ,MAAM,aAAa,CAAC;AAGrB,wEAAwE;AACxE,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAwC3D;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,EAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,EACnC,IAAI,CAAC,EAAE,cAAc,GACpB,SAAS,CAeX;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,WAAW,GAAG,SAAS,EAC9B,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,EACnC,IAAI,EAAE,cAAc,GACnB,SAAS,CAOX;AA2BD,sGAAsG;AACtG,wBAAgB,SAAS,CAAC,QAAQ,GAAE,MAA0B,GAAG,eAAe,CAAC,SAAS,CAAC,CAE1F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,aAAa,GAAG,SAAS,EACnC,aAAa,GAAE,MAA0B,GACxC,eAAe,CAAC,SAAS,CAAC,CAkB5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,YAAY,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,sBAAsB,GAAG,SAAS,EAC9E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,KAAK,IAAI,GAC7D,mBAAmB,CAAC,SAAS,CAAC,GAAG,SAAS,CAmB5C;AAeD,uFAAuF;AACvF,MAAM,MAAM,sBAAsB,GAAG,aAAa,CAAC"}
@@ -0,0 +1,210 @@
1
+ /**
2
+ * @cloc/provider-ai-sdk · tool-loop.ts — the budgeted render-time tool loop over the AI SDK v6
3
+ * `ToolLoopAgent` surface (007-provider-ai-sdk; 027-agentic-primitives §16b.3, §17, §9.1).
4
+ *
5
+ * The render Agent runs a bounded `call → execute-tools → feed-results → repeat` loop: the model
6
+ * may CALL a declared tool, its result is FED BACK as DATA (through safety.ts), and the loop runs
7
+ * until `stopWhen` terminates it (default `stepCountIs(20)` — the SAME trajectory budget that bounds
8
+ * the `0` console agent, §9.1, FR-013). `prepareStep` runs BEFORE each step to swap the model,
9
+ * restrict `activeTools`/`toolChoice`, and select which skills/memory enter that step (FR-011). A
10
+ * tool WITHOUT `execute` is a STOP SIGNAL (the model emits the call and the loop ends). Every tool
11
+ * call clears the §58 policy gate BEFORE execution; a denial DEGRADES (FR-014, FR-021).
12
+ *
13
+ * AI SDK v6 surface (verified against the installed ai@6 .d.ts, not stale memory):
14
+ * - `ToolLoopAgent` is the default `Agent` impl; `tool()`/`dynamicTool({ description, inputSchema,
15
+ * execute })` (v6 renamed `parameters` → `inputSchema`); multi-step loops use
16
+ * `stopWhen: stepCountIs(n)` (v6 removed `maxSteps`); `hasToolCall(name)` / `isLoopFinished()`
17
+ * are the other stop conditions; `StopCondition<TOOLS>` is `(opts) => boolean | PromiseLike`;
18
+ * `prepareStep({ steps, stepNumber, model, messages }) => { model?, toolChoice?, activeTools? }`.
19
+ * - The non-streaming `generateText`/`streamText` calls in agent.ts already drive this loop via
20
+ * `tools` + `stopWhen`; this module assembles those vendor values from the core contract.
21
+ *
22
+ * Vendor edge: this is one of the THREE files (with memory-tool.ts, skills-loader.ts) that touch
23
+ * `ai`. The core's `StopCondition`/`PrepareStep`/`ToolDef` shapes stay vendor-free (@cloc/core).
24
+ */
25
+ import { dynamicTool, stepCountIs, hasToolCall, isLoopFinished } from "ai";
26
+ import { frameToolResultAsData } from "./safety.js";
27
+ /**
28
+ * The ONE gated-tool-execution path shared by the legacy ({@link buildToolSet}) and the 027 agentic
29
+ * ({@link buildAgenticToolSet}) tool sets — so both gate, emit, degrade, and frame IDENTICALLY (DRY;
30
+ * FR-014, FR-015, FR-021). Steps, in order:
31
+ * 1. emit a `tool-call` event (the trace/stream observe the call in order),
32
+ * 2. clear the §58 gate (when one is supplied) — a DENY degrades: emit a `tool-result` denial and
33
+ * return it framed as DATA (never throws, never bypasses, never runs the user fn),
34
+ * 3. run the user-supplied `invoke`,
35
+ * 4. emit a `tool-result` event and return the result framed as DATA (FR-003 feedback path).
36
+ *
37
+ * `gate` is OPTIONAL so the legacy path stays callable without the §58 wiring (current behavior).
38
+ */
39
+ function makeGatedToolExecute(name,
40
+ // The user fn may return sync OR a Promise (core `ToolDef.execute` is `(i) => Promise<O> | O`);
41
+ // we always `await` so a synchronous tool is handled identically.
42
+ invoke, onEvent, gate) {
43
+ return async (args) => {
44
+ onEvent({ kind: "tool-call", tool: name, args });
45
+ if (gate) {
46
+ const decision = await gate.check({ kind: "tool", tool: name });
47
+ if (!decision.allow) {
48
+ const reason = decision.reason ?? `tool "${name}" denied`;
49
+ onEvent({ kind: "tool-result", tool: name, result: { denied: true, reason } });
50
+ // Degrade: hand the model an attributable denial as DATA (never crash, never bypass).
51
+ return frameToolResultAsData(name, { denied: true, reason });
52
+ }
53
+ }
54
+ const result = await invoke(args);
55
+ onEvent({ kind: "tool-result", tool: name, result });
56
+ // The observation re-enters the loop as DATA, never as instructions (FR-015).
57
+ return frameToolResultAsData(name, result);
58
+ };
59
+ }
60
+ /**
61
+ * Default loop bound — the SAME trajectory budget that bounds the `0` console agent (§9.1). v6's
62
+ * own default is `stepCountIs(20)`; we keep a smaller render default but the render layer MUST set
63
+ * `stopWhen` explicitly (FR-013). Kept for the legacy single-turn path; the agentic render path
64
+ * maps `GenOpts.stopWhen` through {@link toAiStopWhen}.
65
+ */
66
+ export const DEFAULT_MAX_STEPS = 8;
67
+ /**
68
+ * Build the AI SDK v6 `ToolSet` from the turn's legacy declared tools (the pre-027 shape on
69
+ * `AgentTurn.tools`). Each tool's `execute`:
70
+ * 1. emits a `tool-call` event,
71
+ * 2. invokes the user-provided `invoke` (the actual DataSource query / declared action),
72
+ * 3. frames the result as DATA (safety.ts) and emits a `tool-result` event,
73
+ * 4. returns the framed result to the loop as the observation (FR-003 feedback path).
74
+ *
75
+ * `onEvent` lets the agent forward `tool-call`/`tool-result` to the stream + trace in order. When a
76
+ * {@link PolicyGateHook} is supplied, each call clears the §58 gate FIRST and a denial DEGRADES
77
+ * (the tool returns an attributable denial as DATA, never throwing — FR-014, FR-021).
78
+ */
79
+ export function buildToolSet(tools, onEvent, gate) {
80
+ const set = {};
81
+ for (const t of tools) {
82
+ // v6: runtime tools whose input/output types are not known at dev time use `dynamicTool`
83
+ // (the typed `tool({...})` overloads infer INPUT/OUTPUT from a static schema we don't have).
84
+ set[t.name] = dynamicTool({
85
+ ...(t.description !== undefined ? { description: t.description } : {}),
86
+ // v6: `inputSchema` (was `parameters`). Zod / Standard-Schema validators are accepted.
87
+ inputSchema: t.input,
88
+ // v6 `execute(input, options)` — we ignore the options (no abort/streaming hooks here).
89
+ // Gating + eventing + DATA-framing run through the ONE shared gated executor (DRY).
90
+ execute: makeGatedToolExecute(t.name, (args) => t.invoke(args), onEvent, gate),
91
+ });
92
+ }
93
+ return set;
94
+ }
95
+ /**
96
+ * Build the AI SDK v6 `ToolSet` from the 027 core {@link CoreToolSet} (`GenOpts.tools`) — the three
97
+ * sources (`plugin` / `capability` / `wired`) all join the render loop (FR-012). Each `ToolDef`'s
98
+ * `execute` is gate-checked then framed as DATA; a tool WITHOUT `execute` is registered as a STOP
99
+ * SIGNAL (v6: a tool with no `execute` ends the loop — §16b.3, isStopTool). Returns `{}` for an
100
+ * empty/undefined set so a baseline render adds no tools (FR-002).
101
+ */
102
+ export function buildAgenticToolSet(tools, onEvent, gate) {
103
+ if (!tools)
104
+ return {};
105
+ const set = {};
106
+ for (const [name, def] of Object.entries(tools)) {
107
+ set[name] = agenticTool(name, def, onEvent, gate);
108
+ }
109
+ return set;
110
+ }
111
+ /** One core {@link ToolDef} → a gated v6 `dynamicTool`. `execute`-less = stop signal (§16b.3). */
112
+ function agenticTool(name, def, onEvent, gate) {
113
+ const base = {
114
+ ...(def.description !== undefined ? { description: def.description } : {}),
115
+ inputSchema: def.inputSchema,
116
+ };
117
+ // A tool WITHOUT execute is a STOP SIGNAL: register a dynamic tool with NO execute so the loop
118
+ // ends when the model emits the call (AI SDK v6 semantics; isStopTool narrows it). `dynamicTool`
119
+ // requires `execute`, so a stop tool is registered as the equivalent dynamic-tool object literal.
120
+ const execute = def.execute;
121
+ if (execute === undefined) {
122
+ return { ...base, type: "dynamic" };
123
+ }
124
+ return dynamicTool({
125
+ ...base,
126
+ // Same gated executor as the legacy path — gate-check, emit, degrade-to-DATA, frame (DRY).
127
+ execute: makeGatedToolExecute(name, (args) => execute(args), onEvent, gate),
128
+ });
129
+ }
130
+ /** The v6 multi-step stop condition (replaces the removed `maxSteps`). Legacy single-turn default. */
131
+ export function stopAfter(maxSteps = DEFAULT_MAX_STEPS) {
132
+ return stepCountIs(maxSteps);
133
+ }
134
+ /**
135
+ * Map the core vendor-free {@link StopCondition} (`GenOpts.stopWhen`) to the AI SDK v6 stop
136
+ * condition — `stepCountIs(n)` / `hasToolCall(name)` / `isLoopFinished()` (§16b.3, §9.1). When the
137
+ * render layer sets no budget we fall back to the loop default so it can never thrash unbounded
138
+ * (FR-013, NFR-005).
139
+ *
140
+ * TODO(027/003/012, NEEDS CLARIFICATION): cache keying of a tool-using (non-pure) render — whether
141
+ * tool outputs fold into `dataVersion`, the render is uncacheable, or it is cached per tool-result
142
+ * hash. Owned by 003/012; routed to Governance.
143
+ * TODO(027, NEEDS CLARIFICATION): degraded-render behavior when the budget is exhausted BEFORE a
144
+ * complete `Output` (cheaper tier? best partial-but-valid plan?). §16b.3 guarantees boundedness but
145
+ * not the exact fallback; today we let `validateOrRepair` (validate.ts) reject an incomplete plan so
146
+ * an invalid/partial structure is NEVER emitted (Principle 3). Routed to Governance.
147
+ */
148
+ export function toAiStopWhen(stopWhen, fallbackSteps = DEFAULT_MAX_STEPS) {
149
+ if (!stopWhen)
150
+ return stepCountIs(fallbackSteps);
151
+ switch (stopWhen.kind) {
152
+ case "stepCount":
153
+ // Guard a non-finite/negative step count from a hand-built condition so the loop stays bounded.
154
+ return stepCountIs(Number.isFinite(stopWhen.n) && stopWhen.n > 0 ? Math.trunc(stopWhen.n) : fallbackSteps);
155
+ case "hasToolCall":
156
+ return hasToolCall(stopWhen.name);
157
+ case "isLoopFinished":
158
+ return isLoopFinished();
159
+ default: {
160
+ // Exhaustiveness: a new core StopCondition kind must be handled here. Fall back to the bounded
161
+ // step cap so an unrecognized condition can never leave the loop unbounded (NFR-005).
162
+ const _exhaustive = stopWhen;
163
+ void _exhaustive;
164
+ return stepCountIs(fallbackSteps);
165
+ }
166
+ }
167
+ }
168
+ /**
169
+ * Adapt the core vendor-free {@link PrepareStep} (per-step context engineering, FR-011) to the AI
170
+ * SDK v6 `prepareStep` callback. Runs BEFORE each step and returns the per-step overrides v6
171
+ * honors: `model` (swap), `activeTools` (restrict), `toolChoice` (restrict). Skill/memory SELECTION
172
+ * carried on the core {@link StepDirective} is applied by the caller (it owns the typed tool/skill
173
+ * maps); v6's surface only takes the model/tools knobs, so those pass straight through. Returns
174
+ * `undefined` (use outer settings) when the directive sets nothing for the step.
175
+ *
176
+ * `resolveModel` turns a core `ModelRef`-bearing directive into the routed v6 `LanguageModel`; when
177
+ * absent the step keeps the outer model. The per-step skill/memory selection is surfaced via
178
+ * `onDirective` so the runner can re-frame the prompt fragment / gate the chosen skills.
179
+ */
180
+ export function toAiPrepareStep(prepareStep, resolveModel, onDirective) {
181
+ if (!prepareStep)
182
+ return undefined;
183
+ return ({ stepNumber, steps }) => {
184
+ const directive = prepareStep({
185
+ step: stepNumber,
186
+ ...(steps.length > 0 ? { lastOutput: steps[steps.length - 1] } : {}),
187
+ });
188
+ onDirective?.(stepNumber, directive);
189
+ const model = resolveModel(directive);
190
+ const result = {};
191
+ if (model !== undefined)
192
+ result.model = model;
193
+ if (directive.activeTools !== undefined)
194
+ result.activeTools = [...directive.activeTools];
195
+ if (directive.toolChoice !== undefined)
196
+ result.toolChoice = toAiToolChoice(directive.toolChoice);
197
+ return Object.keys(result).length > 0 ? result : undefined;
198
+ };
199
+ }
200
+ /**
201
+ * Map the core vendor-free tool-choice (a string like `"auto"`/`"required"`/`"none"`, or a named
202
+ * `{ tool }` selector) to the AI SDK v6 {@link ToolChoice} shape (`"auto"` | `{ type: "tool",
203
+ * toolName }`). Extracted so the mapping lives in ONE place (DRY); behavior is unchanged.
204
+ */
205
+ function toAiToolChoice(choice) {
206
+ return typeof choice === "string"
207
+ ? choice
208
+ : { type: "tool", toolName: choice.tool };
209
+ }
210
+ //# sourceMappingURL=tool-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-loop.js","sourceRoot":"","sources":["../src/tool-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @cloc/provider-ai-sdk · trace.ts — the `agent.generate` OTel subtree (FR-012, §72.2,
3
+ * contracts/trace.contract.ts).
4
+ *
5
+ * The render pipeline contributes the STRUCTURAL spans (route/ground/project/cache/eval); the
6
+ * AgentProvider contributes the `agent.generate` subtree, which the AI SDK + AI Gateway emit
7
+ * natively (provider, model, fallback hops, token counts, cost). This module pins the attribute
8
+ * names and NESTS the subtree under the active pipeline trace so the two compose into one trace =
9
+ * one replay recipe (§72.2).
10
+ *
11
+ * To stay runtime-agnostic and vendor-light, the adapter does not hard-depend on
12
+ * `@opentelemetry/api`; it records against a small structural `SpanSink` the pipeline/host backs
13
+ * with its real tracer (the AI SDK already emits OTel — the host wires `experimental_telemetry`).
14
+ * A no-op sink is the default so the adapter runs without a tracer present.
15
+ *
16
+ * TODO(C3 — spec Clarification 3): whether `prompt.tokens`/`output.tokens` (and latency) are
17
+ * MANDATED at MVP, or `cost.usd` is the only required metric. §72.2 shows tokens; routed to
18
+ * Governance (research.md C3). They are recorded when available, asserted only for cost.
19
+ */
20
+ /** The `agent.generate` attribute bag (contracts/trace.contract.ts). */
21
+ export interface AgentGenerateAttributes {
22
+ "gateway.provider": string;
23
+ "gateway.model": string;
24
+ /** Failover hops; 0 when the primary served. */
25
+ "gateway.fallback": number;
26
+ /** Per-request cost — REQUIRED (FR-012). */
27
+ "cost.usd": number;
28
+ /** §72.2 shows these; mandate is TODO(C3). Recorded when the SDK/gateway reports them. */
29
+ "prompt.tokens"?: number;
30
+ "output.tokens"?: number;
31
+ /** Total tokens for the turn (v6 `usage.totalTokens`); recorded additively when reported. */
32
+ "total.tokens"?: number;
33
+ /** Cached input tokens read (v6 `usage.inputTokenDetails.cacheReadTokens`); when reported. */
34
+ "cache.read.tokens"?: number;
35
+ }
36
+ /** Shared provenance keys carried with the pipeline spans so the trace is a replay recipe. */
37
+ export interface AgentGenerateContext {
38
+ "cloc.data_version": string;
39
+ "cloc.kit_version": string;
40
+ "cloc.seed"?: string;
41
+ "cloc.tier": 2 | 3;
42
+ }
43
+ /** Span events: the loop + validation boundary as observable events. */
44
+ export type AgentGenerateEvent = {
45
+ name: "tool.call";
46
+ tool: string;
47
+ } | {
48
+ name: "tool.result";
49
+ tool: string;
50
+ } | {
51
+ name: "validate";
52
+ ok: boolean;
53
+ } | {
54
+ name: "repair";
55
+ attempt: number;
56
+ ok: boolean;
57
+ };
58
+ /**
59
+ * The minimal structural sink the adapter records against. The host backs this with its real OTel
60
+ * tracer (nesting under `req.trace`); the default is a no-op. Mirrors the OTel span surface the
61
+ * adapter needs without importing the vendor package (keeps the adapter dependency-light, §43).
62
+ */
63
+ export interface SpanSink {
64
+ setAttributes(attrs: Partial<AgentGenerateAttributes & AgentGenerateContext>): void;
65
+ addEvent(event: AgentGenerateEvent): void;
66
+ /** Mark the subtree failed (carries the surfaced AgentError code). */
67
+ recordError(code: string, message: string): void;
68
+ end(): void;
69
+ }
70
+ /** A no-op sink — used when no tracer is wired (the adapter still runs). */
71
+ export declare const NOOP_SPAN: SpanSink;
72
+ /**
73
+ * Open the `agent.generate` span under the active pipeline trace. `start` is supplied by the host
74
+ * (it owns the real tracer + the active OTel context from `req.trace`); when absent we return the
75
+ * no-op sink. The returned sink is the seam every internal module reports through.
76
+ */
77
+ export declare function startAgentSpan(context: AgentGenerateContext, start?: (name: "agent.generate", context: AgentGenerateContext) => SpanSink): SpanSink;
78
+ //# sourceMappingURL=trace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,wEAAwE;AACxE,MAAM,WAAW,uBAAuB;IACtC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6FAA6F;IAC7F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8FAA8F;IAC9F,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,8FAA8F;AAC9F,MAAM,WAAW,oBAAoB;IACnC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;CACpB;AAED,wEAAwE;AACxE,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,OAAO,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAA;CAAE,CAAC;AAErD;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,uBAAuB,GAAG,oBAAoB,CAAC,GAAG,IAAI,CAAC;IACpF,QAAQ,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC1C,sEAAsE;IACtE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,GAAG,IAAI,IAAI,CAAC;CACb;AAED,4EAA4E;AAC5E,eAAO,MAAM,SAAS,EAAE,QAKvB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,oBAAoB,EAC7B,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,oBAAoB,KAAK,QAAQ,GAC1E,QAAQ,CAKV"}
package/dist/trace.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @cloc/provider-ai-sdk · trace.ts — the `agent.generate` OTel subtree (FR-012, §72.2,
3
+ * contracts/trace.contract.ts).
4
+ *
5
+ * The render pipeline contributes the STRUCTURAL spans (route/ground/project/cache/eval); the
6
+ * AgentProvider contributes the `agent.generate` subtree, which the AI SDK + AI Gateway emit
7
+ * natively (provider, model, fallback hops, token counts, cost). This module pins the attribute
8
+ * names and NESTS the subtree under the active pipeline trace so the two compose into one trace =
9
+ * one replay recipe (§72.2).
10
+ *
11
+ * To stay runtime-agnostic and vendor-light, the adapter does not hard-depend on
12
+ * `@opentelemetry/api`; it records against a small structural `SpanSink` the pipeline/host backs
13
+ * with its real tracer (the AI SDK already emits OTel — the host wires `experimental_telemetry`).
14
+ * A no-op sink is the default so the adapter runs without a tracer present.
15
+ *
16
+ * TODO(C3 — spec Clarification 3): whether `prompt.tokens`/`output.tokens` (and latency) are
17
+ * MANDATED at MVP, or `cost.usd` is the only required metric. §72.2 shows tokens; routed to
18
+ * Governance (research.md C3). They are recorded when available, asserted only for cost.
19
+ */
20
+ /** A no-op sink — used when no tracer is wired (the adapter still runs). */
21
+ export const NOOP_SPAN = {
22
+ setAttributes() { },
23
+ addEvent() { },
24
+ recordError() { },
25
+ end() { },
26
+ };
27
+ /**
28
+ * Open the `agent.generate` span under the active pipeline trace. `start` is supplied by the host
29
+ * (it owns the real tracer + the active OTel context from `req.trace`); when absent we return the
30
+ * no-op sink. The returned sink is the seam every internal module reports through.
31
+ */
32
+ export function startAgentSpan(context, start) {
33
+ if (!start)
34
+ return NOOP_SPAN;
35
+ const span = start("agent.generate", context);
36
+ span.setAttributes(context);
37
+ return span;
38
+ }
39
+ //# sourceMappingURL=trace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.js","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AA+CH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,SAAS,GAAa;IACjC,aAAa,KAAI,CAAC;IAClB,QAAQ,KAAI,CAAC;IACb,WAAW,KAAI,CAAC;IAChB,GAAG,KAAI,CAAC;CACT,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,OAA6B,EAC7B,KAA2E;IAE3E,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC5B,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @cloc/provider-ai-sdk · validate.ts — the validate-or-repair boundary (FR-005, NFR-001, §3).
3
+ *
4
+ * Probabilistic PLANNING is allowed; probabilistic final OUTPUT is not (Constitution Principle 3).
5
+ * The produced object is validated against the kit's Standard Schema (`req.outputSchema`, Zod).
6
+ * On failure a BOUNDED repair loop runs (`RepairPolicy.maxAttempts`); on exhaustion the request is
7
+ * REJECTED with a fatal `validation-exhausted` error — a structurally invalid object NEVER passes
8
+ * through to the client (FR-005, edge case, quickstart §6).
9
+ *
10
+ * TODO(C2 — spec Clarification 2): `maxAttempts` is provisional (research.md C2, config.ts
11
+ * DEFAULT_REPAIR_POLICY). The doc says "validate / repair" with no limit; routed to Governance.
12
+ */
13
+ import type { StandardSchemaV1 } from "./tokens.js";
14
+ import type { RepairPolicy } from "./config.js";
15
+ /** One validation failure (mirrors the Standard-Schema issue shape). */
16
+ export interface ValidationIssue {
17
+ message: string;
18
+ path?: ReadonlyArray<PropertyKey | {
19
+ key: PropertyKey;
20
+ }>;
21
+ }
22
+ /** Outcome of one validate pass. */
23
+ export type ValidateOutcome<T> = {
24
+ ok: true;
25
+ value: T;
26
+ } | {
27
+ ok: false;
28
+ issues: ReadonlyArray<ValidationIssue>;
29
+ };
30
+ /** Run the kit schema's own validator once (no repair). Async-normalized. */
31
+ export declare function validateOnce<T>(schema: StandardSchemaV1<unknown, T>, candidate: unknown): Promise<ValidateOutcome<T>>;
32
+ /** Reported to the trace/agent on each repair attempt (data-model §6 events). */
33
+ export interface ValidateEvents {
34
+ onValidate?(ok: boolean): void;
35
+ onRepair?(attempt: number, ok: boolean): void;
36
+ }
37
+ /**
38
+ * Validate `initial`; if invalid, ask `repair(issues, attempt)` for a corrected candidate, up to
39
+ * `policy.maxAttempts` times. The first valid candidate is returned. On exhaustion, REJECT with a
40
+ * fatal `validation-exhausted` error — never return an invalid object (FR-005, NFR-001).
41
+ *
42
+ * `repair` is injected by the Agent: it re-prompts the model with the validation issues. Returning
43
+ * `undefined` (the model gave up / no repair channel) short-circuits to rejection.
44
+ */
45
+ /** How many issue messages to surface in the rejection summary (the rest are summarized as a count). */
46
+ export declare const ISSUE_SUMMARY_LIMIT = 3;
47
+ export declare function validateOrRepair<T>(args: {
48
+ schema: StandardSchemaV1<unknown, T>;
49
+ initial: unknown;
50
+ policy: RepairPolicy;
51
+ repair: (issues: ReadonlyArray<ValidationIssue>, attempt: number) => Promise<unknown | undefined>;
52
+ events?: ValidateEvents;
53
+ }): Promise<T>;
54
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,aAAa,CAAC,WAAW,GAAG;QAAE,GAAG,EAAE,WAAW,CAAA;KAAE,CAAC,CAAC;CAC1D;AAED,oCAAoC;AACpC,MAAM,MAAM,eAAe,CAAC,CAAC,IACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,CAAA;CAAE,CAAC;AAE1D,6EAA6E;AAC7E,wBAAsB,YAAY,CAAC,CAAC,EAClC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,EACpC,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAM7B;AAED,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/C;AAED;;;;;;;GAOG;AACH,wGAAwG;AACxG,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE;IAC9C,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAClG,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB,GAAG,OAAO,CAAC,CAAC,CAAC,CAiCb"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @cloc/provider-ai-sdk · validate.ts — the validate-or-repair boundary (FR-005, NFR-001, §3).
3
+ *
4
+ * Probabilistic PLANNING is allowed; probabilistic final OUTPUT is not (Constitution Principle 3).
5
+ * The produced object is validated against the kit's Standard Schema (`req.outputSchema`, Zod).
6
+ * On failure a BOUNDED repair loop runs (`RepairPolicy.maxAttempts`); on exhaustion the request is
7
+ * REJECTED with a fatal `validation-exhausted` error — a structurally invalid object NEVER passes
8
+ * through to the client (FR-005, edge case, quickstart §6).
9
+ *
10
+ * TODO(C2 — spec Clarification 2): `maxAttempts` is provisional (research.md C2, config.ts
11
+ * DEFAULT_REPAIR_POLICY). The doc says "validate / repair" with no limit; routed to Governance.
12
+ */
13
+ import { validate as runStandardValidate } from "@cloc/core";
14
+ import { AgentError } from "./gateway.js";
15
+ /** Run the kit schema's own validator once (no repair). Async-normalized. */
16
+ export async function validateOnce(schema, candidate) {
17
+ const result = await runStandardValidate(schema, candidate);
18
+ if (result.issues === undefined) {
19
+ return { ok: true, value: result.value };
20
+ }
21
+ return { ok: false, issues: result.issues };
22
+ }
23
+ /**
24
+ * Validate `initial`; if invalid, ask `repair(issues, attempt)` for a corrected candidate, up to
25
+ * `policy.maxAttempts` times. The first valid candidate is returned. On exhaustion, REJECT with a
26
+ * fatal `validation-exhausted` error — never return an invalid object (FR-005, NFR-001).
27
+ *
28
+ * `repair` is injected by the Agent: it re-prompts the model with the validation issues. Returning
29
+ * `undefined` (the model gave up / no repair channel) short-circuits to rejection.
30
+ */
31
+ /** How many issue messages to surface in the rejection summary (the rest are summarized as a count). */
32
+ export const ISSUE_SUMMARY_LIMIT = 3;
33
+ export async function validateOrRepair(args) {
34
+ const { schema, initial, policy, repair, events } = args;
35
+ // A non-finite / negative bound is a config error; clamp to a sane floor so the loop is finite
36
+ // and at least validates the original once (never an infinite or skipped loop). Defaults preserve
37
+ // current behavior for the documented `maxAttempts >= 1` configs.
38
+ const maxAttempts = Number.isFinite(policy.maxAttempts) ? Math.max(0, Math.trunc(policy.maxAttempts)) : 0;
39
+ let candidate = initial;
40
+ let lastIssues = [];
41
+ // attempt 0 is the original; attempts 1..maxAttempts are repairs.
42
+ for (let attempt = 0; attempt <= maxAttempts; attempt++) {
43
+ const outcome = await validateOnce(schema, candidate);
44
+ const ok = outcome.ok;
45
+ if (attempt === 0)
46
+ events?.onValidate?.(ok);
47
+ else
48
+ events?.onRepair?.(attempt, ok);
49
+ if (outcome.ok)
50
+ return outcome.value;
51
+ lastIssues = outcome.issues;
52
+ if (attempt === maxAttempts)
53
+ break; // bound reached → reject below
54
+ const next = await repair(outcome.issues, attempt + 1);
55
+ if (next === undefined)
56
+ break; // no repair produced → reject
57
+ candidate = next;
58
+ }
59
+ // onExhaustion is "reject" by contract; invalid output is never passed through (FR-005).
60
+ throw new AgentError("validation-exhausted", `output failed kit-schema validation after ${maxAttempts} repair attempt(s): ${summarizeIssues(lastIssues)}`, true);
61
+ }
62
+ /**
63
+ * Summarize validation issues for the rejection message: the first {@link ISSUE_SUMMARY_LIMIT}
64
+ * messages joined, plus a `(+N more)` tail when there are more. Each message is annotated with its
65
+ * dotted path when present so the failure is actionable (e.g. `title: Required`).
66
+ */
67
+ function summarizeIssues(issues) {
68
+ if (issues.length === 0)
69
+ return "(no issue detail reported)";
70
+ const head = issues.slice(0, ISSUE_SUMMARY_LIMIT).map(formatIssue).join("; ");
71
+ const extra = issues.length - ISSUE_SUMMARY_LIMIT;
72
+ return extra > 0 ? `${head} (+${extra} more)` : head;
73
+ }
74
+ /** Format one issue as `path: message` (or just `message` when it has no path). */
75
+ function formatIssue(issue) {
76
+ const path = (issue.path ?? [])
77
+ .map((seg) => (typeof seg === "object" && seg !== null ? String(seg.key) : String(seg)))
78
+ .join(".");
79
+ return path ? `${path}: ${issue.message}` : issue.message;
80
+ }
81
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}