@kindgi/agents 0.1.2 → 0.1.4-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.
- package/dist/blocks.d.ts +101 -0
- package/dist/blocks.d.ts.map +1 -0
- package/dist/blocks.js +149 -0
- package/dist/blocks.js.map +1 -0
- package/dist/conversation-binding.d.ts +9 -1
- package/dist/conversation-binding.d.ts.map +1 -1
- package/dist/define.d.ts +17 -2
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +93 -6
- package/dist/define.js.map +1 -1
- package/dist/guardrails-gate.d.ts +7 -4
- package/dist/guardrails-gate.d.ts.map +1 -1
- package/dist/guardrails-gate.js +5 -2
- package/dist/guardrails-gate.js.map +1 -1
- package/dist/handlers/compose-result.d.ts.map +1 -1
- package/dist/handlers/compose-result.js +3 -0
- package/dist/handlers/compose-result.js.map +1 -1
- package/dist/handlers/context.d.ts +24 -0
- package/dist/handlers/context.d.ts.map +1 -1
- package/dist/handlers/dispatch-tools.d.ts.map +1 -1
- package/dist/handlers/dispatch-tools.js +69 -43
- package/dist/handlers/dispatch-tools.js.map +1 -1
- package/dist/handlers/errors.d.ts +12 -1
- package/dist/handlers/errors.d.ts.map +1 -1
- package/dist/handlers/errors.js.map +1 -1
- package/dist/handlers/evaluate-guardrails.d.ts.map +1 -1
- package/dist/handlers/evaluate-guardrails.js +36 -1
- package/dist/handlers/evaluate-guardrails.js.map +1 -1
- package/dist/handlers/gate-decision.d.ts +62 -0
- package/dist/handlers/gate-decision.d.ts.map +1 -0
- package/dist/handlers/gate-decision.js +55 -0
- package/dist/handlers/gate-decision.js.map +1 -0
- package/dist/handlers/model-call.d.ts +10 -0
- package/dist/handlers/model-call.d.ts.map +1 -1
- package/dist/handlers/model-call.js +120 -34
- package/dist/handlers/model-call.js.map +1 -1
- package/dist/handlers/persist-provenance.d.ts.map +1 -1
- package/dist/handlers/persist-provenance.js +3 -1
- package/dist/handlers/persist-provenance.js.map +1 -1
- package/dist/handlers/persist-user-message.d.ts.map +1 -1
- package/dist/handlers/persist-user-message.js +5 -16
- package/dist/handlers/persist-user-message.js.map +1 -1
- package/dist/handlers/public-types.d.ts +37 -3
- package/dist/handlers/public-types.d.ts.map +1 -1
- package/dist/handlers/rehydrate.d.ts.map +1 -1
- package/dist/handlers/rehydrate.js +91 -1
- package/dist/handlers/rehydrate.js.map +1 -1
- package/dist/handlers/render-prompt.d.ts.map +1 -1
- package/dist/handlers/render-prompt.js +4 -1
- package/dist/handlers/render-prompt.js.map +1 -1
- package/dist/handlers/replay.d.ts +143 -0
- package/dist/handlers/replay.d.ts.map +1 -0
- package/dist/handlers/replay.js +177 -0
- package/dist/handlers/replay.js.map +1 -0
- package/dist/handlers/resolve-blocks.d.ts +32 -0
- package/dist/handlers/resolve-blocks.d.ts.map +1 -0
- package/dist/handlers/resolve-blocks.js +129 -0
- package/dist/handlers/resolve-blocks.js.map +1 -0
- package/dist/handlers/resolve-tools.d.ts +6 -2
- package/dist/handlers/resolve-tools.d.ts.map +1 -1
- package/dist/handlers/resolve-tools.js +59 -27
- package/dist/handlers/resolve-tools.js.map +1 -1
- package/dist/handlers/result-shape.d.ts +7 -0
- package/dist/handlers/result-shape.d.ts.map +1 -1
- package/dist/handlers/result-shape.js.map +1 -1
- package/dist/handlers/run-retrievals.d.ts.map +1 -1
- package/dist/handlers/run-retrievals.js +35 -35
- package/dist/handlers/run-retrievals.js.map +1 -1
- package/dist/handlers/run-snapshot.d.ts.map +1 -1
- package/dist/handlers/run-snapshot.js +1 -0
- package/dist/handlers/run-snapshot.js.map +1 -1
- package/dist/handlers/setup.d.ts +2 -0
- package/dist/handlers/setup.d.ts.map +1 -1
- package/dist/handlers/setup.js +29 -12
- package/dist/handlers/setup.js.map +1 -1
- package/dist/handlers/tool-hitl.d.ts +5 -9
- package/dist/handlers/tool-hitl.d.ts.map +1 -1
- package/dist/handlers/tool-hitl.js +5 -0
- package/dist/handlers/tool-hitl.js.map +1 -1
- package/dist/handlers/turn-environment.d.ts +13 -2
- package/dist/handlers/turn-environment.d.ts.map +1 -1
- package/dist/handlers/turn-environment.js +12 -3
- package/dist/handlers/turn-environment.js.map +1 -1
- package/dist/handlers/turn-provenance.d.ts +67 -0
- package/dist/handlers/turn-provenance.d.ts.map +1 -0
- package/dist/handlers/turn-provenance.js +160 -0
- package/dist/handlers/turn-provenance.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/invoke.d.ts +14 -1
- package/dist/invoke.d.ts.map +1 -1
- package/dist/invoke.js +37 -19
- package/dist/invoke.js.map +1 -1
- package/dist/pins.d.ts +54 -0
- package/dist/pins.d.ts.map +1 -0
- package/dist/pins.js +51 -0
- package/dist/pins.js.map +1 -0
- package/dist/prompt.d.ts +12 -2
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +41 -4
- package/dist/prompt.js.map +1 -1
- package/dist/provenance-emit.d.ts +4 -2
- package/dist/provenance-emit.d.ts.map +1 -1
- package/dist/provenance-emit.js +4 -2
- package/dist/provenance-emit.js.map +1 -1
- package/dist/run-snapshot-binding.d.ts +5 -0
- package/dist/run-snapshot-binding.d.ts.map +1 -1
- package/dist/schema.d.ts +34 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +16 -0
- package/dist/schema.js.map +1 -1
- package/dist/streaming.d.ts +5 -0
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js.map +1 -1
- package/dist/types.d.ts +65 -2
- package/dist/types.d.ts.map +1 -1
- package/migrations/0003_hesitant_captain_cross.sql +2 -0
- package/migrations/0004_stormy_moondragon.sql +1 -0
- package/migrations/meta/0003_snapshot.json +321 -0
- package/migrations/meta/0004_snapshot.json +327 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +15 -15
- package/src/blocks.ts +231 -0
- package/src/conversation-binding.ts +9 -1
- package/src/define.ts +104 -7
- package/src/guardrails-gate.ts +17 -3
- package/src/handlers/compose-result.ts +3 -0
- package/src/handlers/context.ts +24 -0
- package/src/handlers/dispatch-tools.ts +75 -44
- package/src/handlers/errors.ts +13 -0
- package/src/handlers/evaluate-guardrails.ts +41 -1
- package/src/handlers/gate-decision.ts +101 -0
- package/src/handlers/model-call.ts +163 -33
- package/src/handlers/persist-provenance.ts +3 -1
- package/src/handlers/persist-user-message.ts +3 -16
- package/src/handlers/public-types.ts +38 -3
- package/src/handlers/rehydrate.ts +141 -9
- package/src/handlers/render-prompt.ts +13 -7
- package/src/handlers/replay.ts +314 -0
- package/src/handlers/resolve-blocks.ts +188 -0
- package/src/handlers/resolve-tools.ts +76 -28
- package/src/handlers/result-shape.ts +8 -0
- package/src/handlers/run-retrievals.ts +45 -41
- package/src/handlers/run-snapshot.ts +1 -0
- package/src/handlers/setup.ts +30 -22
- package/src/handlers/tool-hitl.ts +6 -10
- package/src/handlers/turn-environment.ts +28 -3
- package/src/handlers/turn-provenance.ts +230 -0
- package/src/index.ts +44 -0
- package/src/invoke.ts +49 -20
- package/src/pins.ts +98 -0
- package/src/prompt.ts +52 -4
- package/src/provenance-emit.ts +4 -1
- package/src/run-snapshot-binding.ts +6 -0
- package/src/schema.ts +16 -0
- package/src/streaming.ts +5 -0
- package/src/types.ts +68 -2
|
@@ -21,6 +21,7 @@ import { mergeTenantPolicies } from '../tenant-policy.js';
|
|
|
21
21
|
import type { Conversation } from '../types.js';
|
|
22
22
|
import type { TurnContext } from './context.js';
|
|
23
23
|
import { throwAgentTurnFailure } from './errors.js';
|
|
24
|
+
import { type PinnedBlockVersions, resolveTurnBlocks } from './resolve-blocks.js';
|
|
24
25
|
import { resolveTurnTools } from './resolve-tools.js';
|
|
25
26
|
import { type ToolErrorPolicy, effectiveToolErrorPolicy } from './tool-errors.js';
|
|
26
27
|
|
|
@@ -61,16 +62,33 @@ export interface PinnedRoute {
|
|
|
61
62
|
readonly model: string;
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* The tool version each of the agent's tool references resolved to, by
|
|
67
|
+
* tool id, as `setup` journals it: a resumed turn runs these versions,
|
|
68
|
+
* whatever the registry holds by then.
|
|
69
|
+
*/
|
|
70
|
+
export type PinnedToolVersions = Readonly<Record<string, string>>;
|
|
71
|
+
|
|
64
72
|
/**
|
|
65
73
|
* Resolve the turn's guardrails, tools, tenant policy, tool-error policy
|
|
66
74
|
* and model onto `ctx`. With `pinned`, the route is that provider and
|
|
67
75
|
* model, still under the tenant's current policy; one no longer
|
|
68
|
-
* registered or allowed fails the turn.
|
|
76
|
+
* registered or allowed fails the turn. With `pinnedTools`, each tool is
|
|
77
|
+
* that exact version, not its range resolved again; one no longer
|
|
78
|
+
* registered fails the turn.
|
|
69
79
|
*/
|
|
70
80
|
export async function resolveTurnEnvironment(
|
|
71
81
|
ctx: TurnContext,
|
|
72
82
|
pinned?: PinnedRoute,
|
|
73
|
-
|
|
83
|
+
pinnedTools?: PinnedToolVersions,
|
|
84
|
+
pinnedBlocks?: PinnedBlockVersions,
|
|
85
|
+
): Promise<
|
|
86
|
+
PinnedRoute & {
|
|
87
|
+
readonly toolCount: number;
|
|
88
|
+
readonly toolVersions: PinnedToolVersions;
|
|
89
|
+
readonly blockVersions?: PinnedBlockVersions;
|
|
90
|
+
}
|
|
91
|
+
> {
|
|
74
92
|
const invResolution = resolveGuardrails(ctx.input.agent, ctx.bindings);
|
|
75
93
|
if (invResolution.missing.length > 0) {
|
|
76
94
|
throwAgentTurnFailure({
|
|
@@ -84,7 +102,10 @@ export async function resolveTurnEnvironment(
|
|
|
84
102
|
// This turn's tools come from the tenant's own registry — never a
|
|
85
103
|
// registry shared across concurrent turns of other tenants.
|
|
86
104
|
const tenantTools = await ctx.bindings.toolRegistry.forTenant(ctx.input.tenantId);
|
|
87
|
-
ctx.tools = resolveTurnTools(tenantTools, ctx.input.agent);
|
|
105
|
+
ctx.tools = resolveTurnTools(tenantTools, ctx.input.agent, pinnedTools);
|
|
106
|
+
// The data blocks, at the versions pinned (the turn's own on resume).
|
|
107
|
+
const blocks = await resolveTurnBlocks(ctx, pinnedBlocks);
|
|
108
|
+
if (blocks !== undefined) ctx.blocks = blocks;
|
|
88
109
|
|
|
89
110
|
const capability = ctx.input.agent.capabilities[0];
|
|
90
111
|
if (capability === undefined) {
|
|
@@ -151,6 +172,10 @@ export async function resolveTurnEnvironment(
|
|
|
151
172
|
providerId: routed.value.provider.metadata.id,
|
|
152
173
|
model: routed.value.model.name,
|
|
153
174
|
toolCount: ctx.tools.definitions.length,
|
|
175
|
+
toolVersions: Object.fromEntries(
|
|
176
|
+
[...ctx.tools.byName].map(([id, binding]) => [id, binding.resolvedVersion]),
|
|
177
|
+
),
|
|
178
|
+
...(blocks !== undefined && { blockVersions: blocks.versions }),
|
|
154
179
|
};
|
|
155
180
|
}
|
|
156
181
|
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The provenance nodes and edges an agent turn records, in one place:
|
|
6
|
+
* the steps add them as they run, and a turn resumed after a park
|
|
7
|
+
* (`rehydrate.ts`) adds those of the steps that ran before it from what
|
|
8
|
+
* they left durable (the conversation, the journal). Either way the DAG
|
|
9
|
+
* is the same.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ProvenanceBuilder } from '@kindgi/provenance';
|
|
13
|
+
import type { Timestamp } from '@kindgi/types';
|
|
14
|
+
|
|
15
|
+
import type { ConversationMessage, RetrievedFact } from '../types.js';
|
|
16
|
+
import type { TurnContext } from './context.js';
|
|
17
|
+
import type { GateDecision } from './gate-decision.js';
|
|
18
|
+
|
|
19
|
+
/** The turn's user message. */
|
|
20
|
+
export function addInputNode(provenance: ProvenanceBuilder, message: ConversationMessage): void {
|
|
21
|
+
provenance.addNode({
|
|
22
|
+
id: `input:${message.sequence}`,
|
|
23
|
+
kind: 'input',
|
|
24
|
+
timestamp: message.createdAt,
|
|
25
|
+
...(message.actor !== undefined && { actor: message.actor }),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The facts the turn retrieved for its user message. */
|
|
30
|
+
export function addRetrievalNodes(
|
|
31
|
+
provenance: ProvenanceBuilder,
|
|
32
|
+
retrieved: readonly RetrievedFact[],
|
|
33
|
+
input: ConversationMessage,
|
|
34
|
+
): void {
|
|
35
|
+
for (const r of retrieved) {
|
|
36
|
+
provenance.addNode({
|
|
37
|
+
id: `retrieval:${r.fact.id}`,
|
|
38
|
+
kind: 'retrieval',
|
|
39
|
+
timestamp: input.createdAt,
|
|
40
|
+
...(r.fact.contentHash !== undefined && { contentHash: r.fact.contentHash }),
|
|
41
|
+
attributes: {
|
|
42
|
+
factId: r.fact.id,
|
|
43
|
+
factType: r.fact.type,
|
|
44
|
+
intentScope: r.intent.scope,
|
|
45
|
+
...(r.score !== undefined && { score: r.score }),
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
provenance.addEdge({
|
|
49
|
+
from: `retrieval:${r.fact.id}`,
|
|
50
|
+
to: `input:${input.sequence}`,
|
|
51
|
+
kind: 'influenced-by',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** One model call: its identity; its usage is the cost ledger's, by `callId`. */
|
|
57
|
+
export interface ModelCallFacts {
|
|
58
|
+
readonly step: number;
|
|
59
|
+
readonly callId: string;
|
|
60
|
+
readonly provider: { readonly id: string; readonly model: string };
|
|
61
|
+
readonly finishReason: string;
|
|
62
|
+
readonly at: Timestamp;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A model call, `caused-by` the turn's user message and `influenced-by`
|
|
67
|
+
* each of this turn's tool results it read: all of them so far, since
|
|
68
|
+
* the call's input holds every message of the turn before it.
|
|
69
|
+
*/
|
|
70
|
+
export function addModelCallNode(
|
|
71
|
+
provenance: ProvenanceBuilder,
|
|
72
|
+
call: ModelCallFacts,
|
|
73
|
+
inputSequence: number,
|
|
74
|
+
toolResultIds: readonly string[],
|
|
75
|
+
): void {
|
|
76
|
+
const id = `model-call:${call.step}`;
|
|
77
|
+
provenance.addNode({
|
|
78
|
+
id,
|
|
79
|
+
kind: 'model-call',
|
|
80
|
+
timestamp: call.at,
|
|
81
|
+
modelVersion: `${call.provider.id}/${call.provider.model}`,
|
|
82
|
+
attributes: {
|
|
83
|
+
step: call.step,
|
|
84
|
+
callId: call.callId,
|
|
85
|
+
providerId: call.provider.id,
|
|
86
|
+
model: call.provider.model,
|
|
87
|
+
finishReason: call.finishReason,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
provenance.addEdge({ from: id, to: `input:${inputSequence}`, kind: 'caused-by' });
|
|
91
|
+
for (const invocationId of toolResultIds) {
|
|
92
|
+
provenance.addEdge({ from: id, to: `tool-result:${invocationId}`, kind: 'influenced-by' });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* A tool call the model at `step` made, and its result: the stored tool
|
|
98
|
+
* message (a tool's output, a rejected approval's, or a failed call's).
|
|
99
|
+
* `version` is the tool the agent's binding resolved, when it has one.
|
|
100
|
+
*/
|
|
101
|
+
export function addToolNodes(
|
|
102
|
+
provenance: ProvenanceBuilder,
|
|
103
|
+
step: number,
|
|
104
|
+
result: ConversationMessage,
|
|
105
|
+
version: { readonly resolvedVersion: string; readonly requestedRange: string } | undefined,
|
|
106
|
+
): void {
|
|
107
|
+
const call = result.toolCall;
|
|
108
|
+
if (call === undefined) return;
|
|
109
|
+
const callNodeId = `tool-call:${call.invocationId}`;
|
|
110
|
+
const resultNodeId = `tool-result:${call.invocationId}`;
|
|
111
|
+
provenance.addNode({
|
|
112
|
+
id: callNodeId,
|
|
113
|
+
kind: 'tool-call',
|
|
114
|
+
timestamp: result.createdAt,
|
|
115
|
+
attributes: {
|
|
116
|
+
toolId: call.toolId,
|
|
117
|
+
invocationId: call.invocationId,
|
|
118
|
+
// The version the registry picked for the turn and the range the
|
|
119
|
+
// agent asked for, so a replay can pin against the same version.
|
|
120
|
+
...(version !== undefined && {
|
|
121
|
+
toolVersion: version.resolvedVersion,
|
|
122
|
+
toolVersionRange: version.requestedRange,
|
|
123
|
+
}),
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
provenance.addNode({ id: resultNodeId, kind: 'tool-result', timestamp: result.createdAt });
|
|
127
|
+
provenance.addEdge({ from: callNodeId, to: `model-call:${step}`, kind: 'invoked' });
|
|
128
|
+
provenance.addEdge({ from: resultNodeId, to: callNodeId, kind: 'produced' });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The tool calls of one model step: a node pair for each result the step
|
|
133
|
+
* stored, and their invocation ids added to the turn's tool results.
|
|
134
|
+
*/
|
|
135
|
+
export function addStepToolNodes(
|
|
136
|
+
ctx: TurnContext,
|
|
137
|
+
step: number,
|
|
138
|
+
stored: readonly ConversationMessage[],
|
|
139
|
+
): void {
|
|
140
|
+
for (const result of stored) {
|
|
141
|
+
if (result.role !== 'tool' || result.toolCall === undefined) continue;
|
|
142
|
+
const { invocationId, toolId } = result.toolCall;
|
|
143
|
+
if (ctx.provenance !== undefined) {
|
|
144
|
+
addToolNodes(ctx.provenance, step, result, versionOf(ctx, toolId));
|
|
145
|
+
const approval = ctx.toolApprovals?.get(invocationId);
|
|
146
|
+
if (approval !== undefined) {
|
|
147
|
+
addToolApprovalNodes(
|
|
148
|
+
ctx.provenance,
|
|
149
|
+
invocationId,
|
|
150
|
+
approval,
|
|
151
|
+
ctx.input.agent.id as unknown as string,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
ctx.toolResultIds ??= [];
|
|
156
|
+
ctx.toolResultIds.push(invocationId);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** A tool call's approval, decided: the wait it parked on and the answer. */
|
|
161
|
+
export interface ToolApproval {
|
|
162
|
+
readonly waitTokenId: string;
|
|
163
|
+
/** When the call parked on the approval. */
|
|
164
|
+
readonly parkedAt: Timestamp;
|
|
165
|
+
/** When the decision reached the run. */
|
|
166
|
+
readonly decidedAt: Timestamp;
|
|
167
|
+
readonly decision: GateDecision;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The approval a tool call waited on, as the session gate records its own:
|
|
172
|
+
* a `wait` node (the agent parked) `resumed-from` a `resume` node (the
|
|
173
|
+
* decision, its actor whoever decided). The call `waited-on` the wait, and
|
|
174
|
+
* its result was `caused-by` the decision: the tool's output when approved,
|
|
175
|
+
* the rejection when not.
|
|
176
|
+
*/
|
|
177
|
+
export function addToolApprovalNodes(
|
|
178
|
+
provenance: ProvenanceBuilder,
|
|
179
|
+
invocationId: string,
|
|
180
|
+
approval: ToolApproval,
|
|
181
|
+
agentId: string,
|
|
182
|
+
): void {
|
|
183
|
+
const waitId = `tool-hitl-gate-wait:${invocationId}`;
|
|
184
|
+
const resumeId = `tool-hitl-gate-resume:${invocationId}`;
|
|
185
|
+
const { decision } = approval;
|
|
186
|
+
provenance.addNode({
|
|
187
|
+
id: waitId,
|
|
188
|
+
kind: 'wait',
|
|
189
|
+
timestamp: approval.parkedAt,
|
|
190
|
+
actor: `agent:${agentId}`,
|
|
191
|
+
attributes: { gate: 'tool-call', invocationId, waitTokenId: approval.waitTokenId },
|
|
192
|
+
});
|
|
193
|
+
provenance.addNode({
|
|
194
|
+
id: resumeId,
|
|
195
|
+
kind: 'resume',
|
|
196
|
+
timestamp: approval.decidedAt,
|
|
197
|
+
...(decision.decidedBy !== undefined && { actor: decision.decidedBy }),
|
|
198
|
+
attributes: {
|
|
199
|
+
gate: 'tool-call',
|
|
200
|
+
decision: decisionOf(decision),
|
|
201
|
+
...(!decision.approved &&
|
|
202
|
+
decision.reason === 'rejected' &&
|
|
203
|
+
decision.rationale !== undefined && { rationale: decision.rationale }),
|
|
204
|
+
...(decision.approvalId !== undefined && { approvalId: decision.approvalId }),
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
provenance.addEdge({ from: `tool-call:${invocationId}`, to: waitId, kind: 'waited-on' });
|
|
208
|
+
provenance.addEdge({ from: waitId, to: resumeId, kind: 'resumed-from' });
|
|
209
|
+
provenance.addEdge({ from: `tool-result:${invocationId}`, to: resumeId, kind: 'caused-by' });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** A gate decision as a `resume` node records it. */
|
|
213
|
+
export function decisionOf(decision: GateDecision): 'approve' | 'reject' | 'unreadable' {
|
|
214
|
+
if (decision.approved) return 'approve';
|
|
215
|
+
return decision.reason === 'rejected' ? 'reject' : 'unreadable';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** The version the agent's binding of `toolId` resolved, if it has one. */
|
|
219
|
+
function versionOf(
|
|
220
|
+
ctx: TurnContext,
|
|
221
|
+
toolId: string,
|
|
222
|
+
): { readonly resolvedVersion: string; readonly requestedRange: string } | undefined {
|
|
223
|
+
const bindings = [...(ctx.tools?.byName.values() ?? [])];
|
|
224
|
+
const binding =
|
|
225
|
+
ctx.tools?.byName.get(toolId) ??
|
|
226
|
+
bindings.find((b) => (b.tool.id as unknown as string) === toolId);
|
|
227
|
+
return binding === undefined
|
|
228
|
+
? undefined
|
|
229
|
+
: { resolvedVersion: binding.resolvedVersion, requestedRange: binding.requestedRange };
|
|
230
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -17,10 +17,44 @@ export type {
|
|
|
17
17
|
RunSnapshotWriteInput,
|
|
18
18
|
} from './run-snapshot-binding.js';
|
|
19
19
|
export { defineAgent } from './define.js';
|
|
20
|
+
export {
|
|
21
|
+
BLOCK_KINDS,
|
|
22
|
+
MODEL_SETTINGS_SCHEMA,
|
|
23
|
+
settingsSchemaIssues,
|
|
24
|
+
validateBlock,
|
|
25
|
+
} from './blocks.js';
|
|
26
|
+
export type {
|
|
27
|
+
BlockDefinition,
|
|
28
|
+
BlockIssue,
|
|
29
|
+
BlockKind,
|
|
30
|
+
BlockReader,
|
|
31
|
+
InvalidBlock,
|
|
32
|
+
ModelSettings,
|
|
33
|
+
PromptBlockContent,
|
|
34
|
+
SettingsBlockContent,
|
|
35
|
+
} from './blocks.js';
|
|
20
36
|
export type { DefineAgentSpec } from './define.js';
|
|
21
37
|
export { resolveEffectiveHitlPolicy } from './hitl-policy.js';
|
|
38
|
+
export {
|
|
39
|
+
AGENT_GATE_SUBJECTS,
|
|
40
|
+
SESSION_GATE_SUBJECT,
|
|
41
|
+
TOOL_CALL_GATE_SUBJECT,
|
|
42
|
+
readGateDecision,
|
|
43
|
+
} from './handlers/gate-decision.js';
|
|
44
|
+
export type { GateDecision, GateDecisionValue } from './handlers/gate-decision.js';
|
|
22
45
|
export type { EffectiveHitlPolicy } from './hitl-policy.js';
|
|
23
46
|
export { agentStepOutput, invokeAgent, resumeAgentTurn } from './invoke.js';
|
|
47
|
+
export { isReadOnlyTool } from './handlers/replay.js';
|
|
48
|
+
export { SESSION_GATE_RECORD } from './handlers/setup.js';
|
|
49
|
+
export type {
|
|
50
|
+
ReplayApproval,
|
|
51
|
+
ReplayBinding,
|
|
52
|
+
ReplayToolDecision,
|
|
53
|
+
ReplayToolInput,
|
|
54
|
+
ReplayToolTrace,
|
|
55
|
+
ReplayTurnRef,
|
|
56
|
+
ReplayTurnReport,
|
|
57
|
+
} from './handlers/replay.js';
|
|
24
58
|
export type {
|
|
25
59
|
AgentStepOutput,
|
|
26
60
|
AgentTurnAbortedError,
|
|
@@ -80,6 +114,14 @@ export type {
|
|
|
80
114
|
RenderFailureError,
|
|
81
115
|
RenderResult,
|
|
82
116
|
} from './prompt.js';
|
|
117
|
+
export { pinChanges, pinsDigest } from './pins.js';
|
|
118
|
+
export type {
|
|
119
|
+
AgentDerivation,
|
|
120
|
+
AgentDerivationReason,
|
|
121
|
+
AgentPins,
|
|
122
|
+
PinChange,
|
|
123
|
+
PinSet,
|
|
124
|
+
} from './pins.js';
|
|
83
125
|
export { createAgentRegistry } from './registry.js';
|
|
84
126
|
export type { AgentRegistry } from './registry.js';
|
|
85
127
|
export {
|
|
@@ -98,12 +140,14 @@ export type {
|
|
|
98
140
|
AgentBindings,
|
|
99
141
|
AgentId,
|
|
100
142
|
AgentOutputSpec,
|
|
143
|
+
BlockRef,
|
|
101
144
|
Conversation,
|
|
102
145
|
ConversationId,
|
|
103
146
|
ConversationMessage,
|
|
104
147
|
ConversationPolicy,
|
|
105
148
|
MessageRole,
|
|
106
149
|
PromptParameter,
|
|
150
|
+
PromptRef,
|
|
107
151
|
RetrievalIntent,
|
|
108
152
|
RetrievedFact,
|
|
109
153
|
ToolRef,
|
package/src/invoke.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import type { Principal } from '@kindgi/authz';
|
|
5
5
|
import type { KernelError, RunResult } from '@kindgi/runtime';
|
|
6
|
-
import type { Result, RunId, TenantId } from '@kindgi/types';
|
|
6
|
+
import type { OrgId, Result, RunId, TenantId } from '@kindgi/types';
|
|
7
7
|
|
|
8
8
|
import { AGENT_TURN_FLOW } from './agent-turn-flow.js';
|
|
9
9
|
import { DEFAULT_MAX_WALL_MS } from './handlers/constants.js';
|
|
@@ -14,6 +14,7 @@ import type { InvokeAgentBindings, InvokeAgentInput } from './handlers/public-ty
|
|
|
14
14
|
import { rehydrateTurnContext } from './handlers/rehydrate.js';
|
|
15
15
|
import type { AgentTurnResult } from './handlers/result-shape.js';
|
|
16
16
|
import { projectRunResult } from './project-run-result.js';
|
|
17
|
+
import type { RunSnapshotRecord } from './run-snapshot-binding.js';
|
|
17
18
|
import type { Agent } from './types.js';
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -82,7 +83,15 @@ export async function invokeAgent(
|
|
|
82
83
|
flow: AGENT_TURN_FLOW,
|
|
83
84
|
handlers,
|
|
84
85
|
input: input.userMessage,
|
|
86
|
+
// The run's record names the agent, its version and the conversation.
|
|
87
|
+
agent: {
|
|
88
|
+
id: input.agent.id,
|
|
89
|
+
version: input.agent.version,
|
|
90
|
+
conversationId: input.conversationId,
|
|
91
|
+
},
|
|
85
92
|
...(input.parent !== undefined && { parent: input.parent }),
|
|
93
|
+
// A replay's run says so, and which eval run and past run it is for.
|
|
94
|
+
...(input.replay !== undefined && { replay: input.replay }),
|
|
86
95
|
...(input.dryRun === true && { options: { dryRun: true } }),
|
|
87
96
|
// Authorization — carry principal + authz into the run so every
|
|
88
97
|
// tool invocation inside the agent's turn is checked.
|
|
@@ -106,6 +115,12 @@ export interface ResumeAgentTurnInput {
|
|
|
106
115
|
* handler's agent-version-mismatch guard).
|
|
107
116
|
*/
|
|
108
117
|
readonly agent: Agent;
|
|
118
|
+
/**
|
|
119
|
+
* The org of the run's project (the snapshot's `projectId`), when it has
|
|
120
|
+
* one. The caller resolves it from the project, as for `invokeAgent`'s
|
|
121
|
+
* `orgId`, so the resumed turn's tools and guardrails see it too.
|
|
122
|
+
*/
|
|
123
|
+
readonly orgId?: OrgId;
|
|
109
124
|
}
|
|
110
125
|
|
|
111
126
|
/**
|
|
@@ -203,25 +218,7 @@ export async function resumeAgentTurn(
|
|
|
203
218
|
}
|
|
204
219
|
|
|
205
220
|
// 3. Reconstruct the InvokeAgentInput envelope from the snapshot.
|
|
206
|
-
const reconstructedInput
|
|
207
|
-
tenantId: snapshot.tenantId,
|
|
208
|
-
projectId: snapshot.projectId,
|
|
209
|
-
agent: input.agent,
|
|
210
|
-
conversationId: snapshot.conversationId,
|
|
211
|
-
userMessage: snapshot.userMessage,
|
|
212
|
-
...(snapshot.parameters !== undefined && { parameters: snapshot.parameters }),
|
|
213
|
-
...(snapshot.input !== undefined && { input: snapshot.input }),
|
|
214
|
-
...(snapshot.participantId !== undefined && { participantId: snapshot.participantId }),
|
|
215
|
-
...(snapshot.dryRun && { dryRun: true }),
|
|
216
|
-
...(snapshot.principal !== undefined &&
|
|
217
|
-
snapshot.principal !== null && {
|
|
218
|
-
principal: snapshot.principal as Principal,
|
|
219
|
-
}),
|
|
220
|
-
...(snapshot.authz !== undefined &&
|
|
221
|
-
snapshot.authz !== null && {
|
|
222
|
-
authz: snapshot.authz as { readonly fgaApiUrl: string },
|
|
223
|
-
}),
|
|
224
|
-
};
|
|
221
|
+
const reconstructedInput = turnInputFromSnapshot(snapshot, input);
|
|
225
222
|
|
|
226
223
|
// 4. Build the same TurnContext + handlers as invokeAgent().
|
|
227
224
|
const started = Date.now();
|
|
@@ -299,3 +296,35 @@ export type {
|
|
|
299
296
|
InvokeAgentBindings,
|
|
300
297
|
InvokeAgentInput,
|
|
301
298
|
} from './handlers/public-types.js';
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* The `InvokeAgentInput` a resumed turn runs with: the one captured in its
|
|
302
|
+
* snapshot at run start, the agent the caller resolved, and the org of the
|
|
303
|
+
* run's project, which the caller resolves as for `invokeAgent`.
|
|
304
|
+
*/
|
|
305
|
+
export function turnInputFromSnapshot(
|
|
306
|
+
snapshot: RunSnapshotRecord,
|
|
307
|
+
input: Pick<ResumeAgentTurnInput, 'agent' | 'orgId'>,
|
|
308
|
+
): InvokeAgentInput {
|
|
309
|
+
return {
|
|
310
|
+
tenantId: snapshot.tenantId,
|
|
311
|
+
projectId: snapshot.projectId,
|
|
312
|
+
...(input.orgId !== undefined && { orgId: input.orgId }),
|
|
313
|
+
agent: input.agent,
|
|
314
|
+
conversationId: snapshot.conversationId,
|
|
315
|
+
userMessage: snapshot.userMessage,
|
|
316
|
+
...(snapshot.parameters !== undefined && { parameters: snapshot.parameters }),
|
|
317
|
+
...(snapshot.input !== undefined && { input: snapshot.input }),
|
|
318
|
+
...(snapshot.participantId !== undefined && { participantId: snapshot.participantId }),
|
|
319
|
+
...(snapshot.dryRun && { dryRun: true }),
|
|
320
|
+
...(snapshot.principal !== undefined &&
|
|
321
|
+
snapshot.principal !== null && {
|
|
322
|
+
principal: snapshot.principal as Principal,
|
|
323
|
+
}),
|
|
324
|
+
...(snapshot.authz !== undefined &&
|
|
325
|
+
snapshot.authz !== null && {
|
|
326
|
+
authz: snapshot.authz as { readonly fgaApiUrl: string },
|
|
327
|
+
}),
|
|
328
|
+
...(snapshot.replay !== undefined && snapshot.replay !== null && { replay: snapshot.replay }),
|
|
329
|
+
};
|
|
330
|
+
}
|
package/src/pins.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
|
|
6
|
+
import { canonicalize } from '@kindgi/schema';
|
|
7
|
+
import type { VersionDerivation, VersionDerivationReason } from '@kindgi/types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The exact version of each block an agent version runs: its lockfile.
|
|
11
|
+
*
|
|
12
|
+
* An agent names its tools by range (`{ id: 'acme.lookup', version:
|
|
13
|
+
* '^1.0.0' }`), like `package.json`. When a version of the agent is
|
|
14
|
+
* published, the runtime resolves each range once, and every run of that
|
|
15
|
+
* version uses the versions recorded here. So a new tool version reaches
|
|
16
|
+
* an agent only through a new agent version, and two runs of one agent
|
|
17
|
+
* version always run the same blocks.
|
|
18
|
+
*
|
|
19
|
+
* Set by the runtime at publish, never authored. A version published
|
|
20
|
+
* before pins existed has none and resolves its ranges per run.
|
|
21
|
+
*/
|
|
22
|
+
export interface AgentPins {
|
|
23
|
+
/** Tool id → the exact version this agent version runs. */
|
|
24
|
+
readonly tools: Readonly<Record<string, string>>;
|
|
25
|
+
/** Prompt block id → exact version. Empty until the agent references prompt blocks. */
|
|
26
|
+
readonly prompts: Readonly<Record<string, string>>;
|
|
27
|
+
/** Settings block id → exact version. Empty until the agent references settings blocks. */
|
|
28
|
+
readonly settings: Readonly<Record<string, string>>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* One string naming a set of pins: `sha256:<hex>` of the pins'
|
|
33
|
+
* canonical JSON (keys sorted, no whitespace). Two agent versions with
|
|
34
|
+
* the same digest run the same blocks; the digest is what a comparison
|
|
35
|
+
* of versions, or a gate, records and compares. The Python SDK's
|
|
36
|
+
* `pins_digest` computes the same string.
|
|
37
|
+
*/
|
|
38
|
+
export function pinsDigest(pins: AgentPins): string {
|
|
39
|
+
const canonical = canonicalize({
|
|
40
|
+
tools: pins.tools,
|
|
41
|
+
prompts: pins.prompts,
|
|
42
|
+
settings: pins.settings,
|
|
43
|
+
});
|
|
44
|
+
return `sha256:${createHash('sha256').update(canonical, 'utf8').digest('hex')}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Why a deploy registered an agent version under another number (`VersionDerivationReason`). */
|
|
48
|
+
export type AgentDerivationReason = VersionDerivationReason;
|
|
49
|
+
|
|
50
|
+
/** The version an agent version was registered in place of, and why. */
|
|
51
|
+
export type AgentDerivation = VersionDerivation;
|
|
52
|
+
|
|
53
|
+
/** One pin that differs between two versions of an agent or a flow. */
|
|
54
|
+
export interface PinChange {
|
|
55
|
+
readonly kind: 'tool' | 'prompt' | 'setting' | 'agent';
|
|
56
|
+
readonly id: string;
|
|
57
|
+
/** The earlier version's pin; absent when it didn't pin this block. */
|
|
58
|
+
readonly from?: string;
|
|
59
|
+
/** The later version's pin; absent when it doesn't pin this block. */
|
|
60
|
+
readonly to?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const PIN_KINDS = [
|
|
64
|
+
['tools', 'tool'],
|
|
65
|
+
['prompts', 'prompt'],
|
|
66
|
+
['settings', 'setting'],
|
|
67
|
+
['agents', 'agent'],
|
|
68
|
+
] as const;
|
|
69
|
+
|
|
70
|
+
/** A version's pins by kind: an agent's (`AgentPins`) or a flow's (`FlowPins`). */
|
|
71
|
+
export type PinSet = Partial<
|
|
72
|
+
Record<(typeof PIN_KINDS)[number][0], Readonly<Record<string, string>>>
|
|
73
|
+
>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The pins that differ from `before` to `after`, by kind then id. With
|
|
77
|
+
* no `before` (a version published before pins), every pin of `after`
|
|
78
|
+
* is a change.
|
|
79
|
+
*/
|
|
80
|
+
export function pinChanges(before: PinSet | undefined, after: PinSet): PinChange[] {
|
|
81
|
+
const changes: PinChange[] = [];
|
|
82
|
+
for (const [key, kind] of PIN_KINDS) {
|
|
83
|
+
const was = before?.[key] ?? {};
|
|
84
|
+
const now = after[key] ?? {};
|
|
85
|
+
for (const id of [...new Set([...Object.keys(was), ...Object.keys(now)])].sort()) {
|
|
86
|
+
const from = was[id];
|
|
87
|
+
const to = now[id];
|
|
88
|
+
if (from === to) continue;
|
|
89
|
+
changes.push({
|
|
90
|
+
kind,
|
|
91
|
+
id,
|
|
92
|
+
...(from !== undefined && { from }),
|
|
93
|
+
...(to !== undefined && { to }),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return changes;
|
|
98
|
+
}
|
package/src/prompt.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import { Liquid } from 'liquidjs';
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { PromptBlockContent } from './blocks.js';
|
|
7
|
+
import type { Agent, PromptParameter, PromptRef } from './types.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Reserved variable namespaces the runtime injects automatically.
|
|
@@ -20,8 +21,18 @@ import type { Agent, PromptParameter } from './types.js';
|
|
|
20
21
|
* - `input` — the turn's structured input (`InvokeAgentInput.input`),
|
|
21
22
|
* e.g. `{{ input.grievance.summary }}`; unset when
|
|
22
23
|
* the turn has none.
|
|
24
|
+
* - `settings` — the agent's settings blocks' values, by block id:
|
|
25
|
+
* `{{ settings["acme.weights"].recency }}` reads
|
|
26
|
+
* block `acme.weights`; unset when it has none.
|
|
23
27
|
*/
|
|
24
|
-
export const AUTO_INJECTED_VARS = [
|
|
28
|
+
export const AUTO_INJECTED_VARS = [
|
|
29
|
+
'today',
|
|
30
|
+
'now',
|
|
31
|
+
'agent',
|
|
32
|
+
'conversation',
|
|
33
|
+
'input',
|
|
34
|
+
'settings',
|
|
35
|
+
] as const;
|
|
25
36
|
|
|
26
37
|
/**
|
|
27
38
|
* Shape passed to `renderInstructions`. Framework auto-vars are
|
|
@@ -37,6 +48,8 @@ export interface RenderContext {
|
|
|
37
48
|
};
|
|
38
49
|
/** The turn's structured input, rendered as `{{ input.* }}`. */
|
|
39
50
|
readonly input?: unknown;
|
|
51
|
+
/** The agent's settings blocks' values, by block id: `{{ settings["<id>"].<key> }}`. */
|
|
52
|
+
readonly settings?: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
|
|
40
53
|
/**
|
|
41
54
|
* Optional clock override — tests inject a fixed date. Defaults to
|
|
42
55
|
* `new Date()`.
|
|
@@ -84,14 +97,21 @@ export interface RenderFailureError {
|
|
|
84
97
|
* Uses `strictVariables: true` so an unresolved `{{ var }}` is an error
|
|
85
98
|
* — never a silent empty string. This is load-bearing for correctness
|
|
86
99
|
* (a system prompt with a missing firm name is worse than a hard fail).
|
|
100
|
+
*
|
|
101
|
+
* When the instructions come from a prompt block, `prompt` is the
|
|
102
|
+
* version the turn runs (its template and declared parameters); an
|
|
103
|
+
* agent whose instructions are a prompt block can't render without it.
|
|
87
104
|
*/
|
|
88
105
|
export function renderInstructions(
|
|
89
106
|
agent: Agent,
|
|
90
107
|
context: RenderContext,
|
|
108
|
+
prompt?: PromptBlockContent,
|
|
91
109
|
):
|
|
92
110
|
| { readonly ok: true; readonly value: RenderResult }
|
|
93
111
|
| { readonly ok: false; readonly error: PromptRenderError } {
|
|
94
|
-
const
|
|
112
|
+
const source = instructionsSource(agent, prompt);
|
|
113
|
+
if (!source.ok) return source;
|
|
114
|
+
const { template, declared } = source.value;
|
|
95
115
|
const missing = requiredMissing(declared, context.parameters);
|
|
96
116
|
if (missing.length > 0) {
|
|
97
117
|
return {
|
|
@@ -107,7 +127,7 @@ export function renderInstructions(
|
|
|
107
127
|
const merged = mergeContext(agent, declared, context);
|
|
108
128
|
const liquid = new Liquid({ strictVariables: true, strictFilters: true });
|
|
109
129
|
try {
|
|
110
|
-
const rendered = liquid.parseAndRenderSync(
|
|
130
|
+
const rendered = liquid.parseAndRenderSync(template, merged);
|
|
111
131
|
return { ok: true, value: { rendered, context: merged } };
|
|
112
132
|
} catch (cause) {
|
|
113
133
|
// LiquidJS throws UndefinedVariableError for unresolved refs — surface
|
|
@@ -135,6 +155,33 @@ export function renderInstructions(
|
|
|
135
155
|
}
|
|
136
156
|
}
|
|
137
157
|
|
|
158
|
+
/** The template to render and the parameters it declares: the prompt block's, or the agent's own. */
|
|
159
|
+
function instructionsSource(
|
|
160
|
+
agent: Agent,
|
|
161
|
+
prompt: PromptBlockContent | undefined,
|
|
162
|
+
):
|
|
163
|
+
| {
|
|
164
|
+
readonly ok: true;
|
|
165
|
+
readonly value: { readonly template: string; readonly declared: readonly PromptParameter[] };
|
|
166
|
+
}
|
|
167
|
+
| { readonly ok: false; readonly error: PromptRenderError } {
|
|
168
|
+
if (prompt !== undefined) {
|
|
169
|
+
return { ok: true, value: { template: prompt.template, declared: prompt.parameters ?? [] } };
|
|
170
|
+
}
|
|
171
|
+
if (typeof agent.instructions === 'string') {
|
|
172
|
+
return { ok: true, value: { template: agent.instructions, declared: agent.parameters ?? [] } };
|
|
173
|
+
}
|
|
174
|
+
const ref: PromptRef = agent.instructions;
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
error: {
|
|
178
|
+
code: 'render-failure',
|
|
179
|
+
message: `The instructions come from prompt block "${ref.prompt}" (${ref.version}), which wasn't loaded`,
|
|
180
|
+
cause: null,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
138
185
|
function requiredMissing(
|
|
139
186
|
declared: readonly PromptParameter[],
|
|
140
187
|
supplied: Readonly<Record<string, unknown>>,
|
|
@@ -181,5 +228,6 @@ function mergeContext(
|
|
|
181
228
|
};
|
|
182
229
|
}
|
|
183
230
|
if (context.input !== undefined) values.input = context.input;
|
|
231
|
+
if (context.settings !== undefined) values.settings = context.settings;
|
|
184
232
|
return values;
|
|
185
233
|
}
|