@andreprado/agentkit 0.1.0-alpha.10
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 +69 -0
- package/bin/agentkit.mjs +23 -0
- package/docs/guides/add-channel.md +114 -0
- package/docs/guides/add-knowledge.md +134 -0
- package/docs/guides/add-tool.md +342 -0
- package/docs/guides/agentkit-skills-architecture.md +471 -0
- package/docs/guides/channel-security.md +81 -0
- package/docs/guides/channels-implementation-map.md +243 -0
- package/docs/guides/channels-production-handoff.md +102 -0
- package/docs/guides/connect-telegram.md +110 -0
- package/docs/guides/connect-whatsapp-zapster.md +119 -0
- package/docs/guides/create-agent.md +220 -0
- package/docs/guides/prepare-deploy.md +209 -0
- package/docs/guides/run-evals.md +179 -0
- package/docs/guides/security-rules.md +156 -0
- package/docs/guides/use-provider.md +140 -0
- package/docs/llms-full.txt +876 -0
- package/docs/llms.txt +83 -0
- package/docs/portable-deploy-release-checklist.md +41 -0
- package/package.json +47 -0
- package/src/cli/args.ts +36 -0
- package/src/cli/cloud-client.ts +265 -0
- package/src/cli/commands/channels.ts +810 -0
- package/src/cli/commands/knowledge.ts +136 -0
- package/src/cli/constants.ts +4 -0
- package/src/cli/deploy-chat-ui.ts +392 -0
- package/src/cli/deploy-readiness.ts +348 -0
- package/src/cli/flags.ts +162 -0
- package/src/cli/help.ts +184 -0
- package/src/cli/index.ts +1276 -0
- package/src/cli/process.ts +31 -0
- package/src/cloud/artifact.ts +139 -0
- package/src/cloud/client.ts +79 -0
- package/src/cloud/contracts.ts +63 -0
- package/src/cloud/index.ts +3 -0
- package/src/create-project.ts +177 -0
- package/src/index.ts +408 -0
- package/src/providers/index.ts +25 -0
- package/src/providers/pi.ts +286 -0
- package/src/providers/test.ts +133 -0
- package/src/providers/types.ts +34 -0
- package/src/runtime/build.ts +43 -0
- package/src/runtime/channel-buffer.ts +30 -0
- package/src/runtime/channel-test-harness.ts +112 -0
- package/src/runtime/channels/telegram.ts +360 -0
- package/src/runtime/channels/website.ts +132 -0
- package/src/runtime/channels/whatsapp-meta.ts +71 -0
- package/src/runtime/channels/whatsapp-zapster.ts +278 -0
- package/src/runtime/channels.ts +138 -0
- package/src/runtime/chat.ts +218 -0
- package/src/runtime/config.ts +684 -0
- package/src/runtime/conversations.ts +38 -0
- package/src/runtime/core/deploy-state.ts +54 -0
- package/src/runtime/core/manifest.ts +213 -0
- package/src/runtime/core/targets.ts +133 -0
- package/src/runtime/database.ts +256 -0
- package/src/runtime/db-commands.ts +167 -0
- package/src/runtime/deploy-readiness.ts +105 -0
- package/src/runtime/deploy.ts +1 -0
- package/src/runtime/dev-server.ts +1247 -0
- package/src/runtime/docs.ts +36 -0
- package/src/runtime/env.ts +152 -0
- package/src/runtime/errors.ts +13 -0
- package/src/runtime/evals.ts +509 -0
- package/src/runtime/inspect.ts +203 -0
- package/src/runtime/knowledge/chunk.ts +333 -0
- package/src/runtime/knowledge/config.ts +135 -0
- package/src/runtime/knowledge/embeddings.ts +133 -0
- package/src/runtime/knowledge/ingest.ts +521 -0
- package/src/runtime/knowledge/prompt-policy.ts +30 -0
- package/src/runtime/knowledge/retrieve.ts +283 -0
- package/src/runtime/knowledge/schema.ts +56 -0
- package/src/runtime/knowledge/tool.ts +64 -0
- package/src/runtime/knowledge/vector.ts +258 -0
- package/src/runtime/runtime-contract.ts +93 -0
- package/src/runtime/spec.ts +152 -0
- package/src/runtime/sync.ts +144 -0
- package/src/runtime/targets/cloudflare/build.ts +2517 -0
- package/src/runtime/targets/container/build.ts +146 -0
- package/src/runtime/targets/container/server.ts +33 -0
- package/src/runtime/targets/vps/deploy.ts +206 -0
- package/src/runtime/tool-runner.ts +65 -0
- package/src/runtime/tools.ts +470 -0
- package/src/runtime/traces.ts +41 -0
- package/src/storage/sqlite.ts +1118 -0
- package/src/templates/blank.ts +394 -0
- package/src/templates/dentista.ts +1003 -0
- package/src/templates/index.ts +33 -0
- package/src/templates/skills/agentkit-build-agent/SKILL.md +51 -0
- package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +20 -0
- package/src/templates/skills/agentkit-build-agent/templates/sales-qualifier.instructions.md +17 -0
- package/src/templates/skills/agentkit-build-agent/templates/support-agent.instructions.md +16 -0
- package/src/templates/skills/agentkit-capsule/SKILL.md +62 -0
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
- package/src/templates/skills/agentkit-channels/SKILL.md +62 -0
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +58 -0
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +41 -0
- package/src/templates/skills/agentkit-channels/references/telegram.md +38 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +44 -0
- package/src/templates/skills/agentkit-database/SKILL.md +45 -0
- package/src/templates/skills/agentkit-database/templates/appointments.schema.sql +15 -0
- package/src/templates/skills/agentkit-database/templates/leads.schema.sql +17 -0
- package/src/templates/skills/agentkit-deploy/SKILL.md +44 -0
- package/src/templates/skills/agentkit-evals/SKILL.md +60 -0
- package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +22 -0
- package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +14 -0
- package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +14 -0
- package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +18 -0
- package/src/templates/skills/agentkit-knowledge/SKILL.md +40 -0
- package/src/templates/skills/agentkit-knowledge/templates/faq.md +14 -0
- package/src/templates/skills/agentkit-knowledge/templates/policies.md +14 -0
- package/src/templates/skills/agentkit-knowledge/templates/prices.csv +3 -0
- package/src/templates/skills/agentkit-prompts/SKILL.md +45 -0
- package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
- package/src/templates/skills/agentkit-provider/SKILL.md +57 -0
- package/src/templates/skills/agentkit-security/SKILL.md +55 -0
- package/src/templates/skills/agentkit-tools/SKILL.md +36 -0
- package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +35 -0
- package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +37 -0
- package/src/templates/skills/agentkit-tools/examples/lookup-order.tool.md +46 -0
- package/src/templates/skills/agentkit-troubleshooting/SKILL.md +52 -0
- package/src/templates/support.ts +401 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import type { AgentTool, JsonSchema, ToolContext, ToolRenderResult, ToolRuntimeContext } from "../index";
|
|
2
|
+
import type { SqliteAgentKitStore } from "../storage/sqlite";
|
|
3
|
+
import { createLocalDatabaseRunner } from "./database";
|
|
4
|
+
import { AgentKitError } from "./errors";
|
|
5
|
+
|
|
6
|
+
export type ToolCallRequest = {
|
|
7
|
+
name: string;
|
|
8
|
+
input?: unknown;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type ToolCallResult = {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
visibility: "user" | "internal";
|
|
15
|
+
input: unknown;
|
|
16
|
+
output: unknown;
|
|
17
|
+
rendered?: ToolRenderResult;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ToolRuntime = {
|
|
21
|
+
definitions: AgentTool[];
|
|
22
|
+
execute(request: ToolCallRequest): Promise<ToolCallResult>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type ToolRuntimeOptions = {
|
|
26
|
+
tools: AgentTool[];
|
|
27
|
+
store: SqliteAgentKitStore;
|
|
28
|
+
runId: string;
|
|
29
|
+
conversationId: string;
|
|
30
|
+
env?: Record<string, string | undefined>;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
defaultTimeoutMs?: number;
|
|
33
|
+
runtime: ToolRuntimeContext;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const DEFAULT_TOOL_TIMEOUT_MS = 30_000;
|
|
37
|
+
|
|
38
|
+
export function createToolRuntime(options: ToolRuntimeOptions): ToolRuntime {
|
|
39
|
+
const toolsByName = new Map<string, AgentTool>();
|
|
40
|
+
|
|
41
|
+
for (const tool of options.tools) {
|
|
42
|
+
if (toolsByName.has(tool.name)) {
|
|
43
|
+
throw new AgentKitError("invalid_config", `Duplicate tool name "${tool.name}" in agentkit.config.ts.`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
toolsByName.set(tool.name, tool);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
definitions: options.tools,
|
|
51
|
+
execute(request) {
|
|
52
|
+
return executeToolCall(request, toolsByName, options);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function executeToolCall(
|
|
58
|
+
request: ToolCallRequest,
|
|
59
|
+
toolsByName: Map<string, AgentTool>,
|
|
60
|
+
options: ToolRuntimeOptions,
|
|
61
|
+
): Promise<ToolCallResult> {
|
|
62
|
+
let input: unknown = request.input ?? {};
|
|
63
|
+
const tool = toolsByName.get(request.name);
|
|
64
|
+
const preliminarySecretValues = collectConfiguredSecretValues(tool, options.env ?? process.env);
|
|
65
|
+
const toolCall = options.store.createToolCall({
|
|
66
|
+
runId: options.runId,
|
|
67
|
+
conversationId: options.conversationId,
|
|
68
|
+
toolName: request.name,
|
|
69
|
+
visibility: tool?.visibility ?? "user",
|
|
70
|
+
permissions: tool?.permissions ?? [],
|
|
71
|
+
input,
|
|
72
|
+
secretValues: preliminarySecretValues,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
if (!tool) {
|
|
77
|
+
throw new AgentKitError("tool_not_found", `Tool "${request.name}" is not declared in agentkit.config.ts.`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
input = normalizeOptionalNulls(input, tool.inputSchema);
|
|
81
|
+
validateSchema(input, tool.inputSchema, `tool "${tool.name}" input`);
|
|
82
|
+
|
|
83
|
+
const { secrets, secretValues } = resolveToolSecrets(tool, options.env ?? process.env);
|
|
84
|
+
const database = createLocalDatabaseRunner(options.store);
|
|
85
|
+
const output = await runToolWithTimeout(
|
|
86
|
+
tool,
|
|
87
|
+
input,
|
|
88
|
+
{
|
|
89
|
+
secrets,
|
|
90
|
+
database,
|
|
91
|
+
runtime: options.runtime,
|
|
92
|
+
signal: options.signal,
|
|
93
|
+
},
|
|
94
|
+
tool.timeoutMs ?? options.defaultTimeoutMs ?? DEFAULT_TOOL_TIMEOUT_MS,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (tool.outputSchema) {
|
|
98
|
+
validateSchema(output, tool.outputSchema, `tool "${tool.name}" output`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const rendered = renderToolOutput(tool, input, output, options.runtime);
|
|
102
|
+
|
|
103
|
+
options.store.completeToolCall({
|
|
104
|
+
toolCallId: toolCall.id,
|
|
105
|
+
status: "completed",
|
|
106
|
+
output,
|
|
107
|
+
rendered,
|
|
108
|
+
secretValues,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
id: toolCall.id,
|
|
113
|
+
name: tool.name,
|
|
114
|
+
visibility: tool.visibility ?? "user",
|
|
115
|
+
input,
|
|
116
|
+
output,
|
|
117
|
+
...(rendered ? { rendered } : {}),
|
|
118
|
+
};
|
|
119
|
+
} catch (error) {
|
|
120
|
+
const secretValues = collectConfiguredSecretValues(tool, options.env ?? process.env);
|
|
121
|
+
|
|
122
|
+
options.store.completeToolCall({
|
|
123
|
+
toolCallId: toolCall.id,
|
|
124
|
+
status: "failed",
|
|
125
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
126
|
+
secretValues,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function renderToolOutput(
|
|
134
|
+
tool: AgentTool,
|
|
135
|
+
input: unknown,
|
|
136
|
+
output: unknown,
|
|
137
|
+
runtime: ToolRuntimeContext,
|
|
138
|
+
): ToolRenderResult | undefined {
|
|
139
|
+
if (!tool.render) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const rendered = tool.render(output, {
|
|
144
|
+
input,
|
|
145
|
+
runtime,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (typeof rendered === "string") {
|
|
149
|
+
return { text: rendered };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!isRecord(rendered) || typeof rendered.text !== "string" || rendered.text.trim().length === 0) {
|
|
153
|
+
throw new AgentKitError("tool_validation_error", `tool "${tool.name}" render must return a non-empty text string.`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (rendered.blocks !== undefined) {
|
|
157
|
+
if (!Array.isArray(rendered.blocks)) {
|
|
158
|
+
throw new AgentKitError("tool_validation_error", `tool "${tool.name}" render.blocks must be an array when provided.`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
for (const [index, block] of rendered.blocks.entries()) {
|
|
162
|
+
validateRenderBlock(block, `tool "${tool.name}" render.blocks[${index}]`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
text: rendered.text,
|
|
168
|
+
...(rendered.blocks ? { blocks: rendered.blocks } : {}),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function validateRenderBlock(block: unknown, label: string): void {
|
|
173
|
+
if (!isRecord(block)) {
|
|
174
|
+
throw new AgentKitError("tool_validation_error", `${label} must be an object.`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (block.type === "text") {
|
|
178
|
+
if (typeof block.text !== "string" || block.text.trim().length === 0) {
|
|
179
|
+
throw new AgentKitError("tool_validation_error", `${label}.text must be a non-empty string.`);
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (block.type === "card") {
|
|
185
|
+
if (typeof block.title !== "string" || block.title.trim().length === 0) {
|
|
186
|
+
throw new AgentKitError("tool_validation_error", `${label}.title must be a non-empty string.`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
validateOptionalString(block.subtitle, `${label}.subtitle`);
|
|
190
|
+
validateRenderFields(block.fields, `${label}.fields`);
|
|
191
|
+
validateRenderActions(block.actions, `${label}.actions`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
throw new AgentKitError("tool_validation_error", `${label}.type must be "text" or "card".`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function validateRenderFields(value: unknown, label: string): void {
|
|
199
|
+
if (value === undefined) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!Array.isArray(value)) {
|
|
204
|
+
throw new AgentKitError("tool_validation_error", `${label} must be an array when provided.`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
for (const [index, field] of value.entries()) {
|
|
208
|
+
if (!isRecord(field)) {
|
|
209
|
+
throw new AgentKitError("tool_validation_error", `${label}[${index}] must be an object.`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
validateRequiredString(field.label, `${label}[${index}].label`);
|
|
213
|
+
validateRequiredString(field.value, `${label}[${index}].value`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function validateRenderActions(value: unknown, label: string): void {
|
|
218
|
+
if (value === undefined) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!Array.isArray(value)) {
|
|
223
|
+
throw new AgentKitError("tool_validation_error", `${label} must be an array when provided.`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for (const [index, action] of value.entries()) {
|
|
227
|
+
if (!isRecord(action)) {
|
|
228
|
+
throw new AgentKitError("tool_validation_error", `${label}[${index}] must be an object.`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
validateRequiredString(action.label, `${label}[${index}].label`);
|
|
232
|
+
validateOptionalString(action.href, `${label}[${index}].href`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function validateRequiredString(value: unknown, label: string): void {
|
|
237
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
238
|
+
throw new AgentKitError("tool_validation_error", `${label} must be a non-empty string.`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function validateOptionalString(value: unknown, label: string): void {
|
|
243
|
+
if (value !== undefined && typeof value !== "string") {
|
|
244
|
+
throw new AgentKitError("tool_validation_error", `${label} must be a string when provided.`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function resolveToolSecrets(
|
|
249
|
+
tool: AgentTool,
|
|
250
|
+
env: Record<string, string | undefined>,
|
|
251
|
+
): { secrets: Record<string, string>; secretValues: string[] } {
|
|
252
|
+
const secrets: Record<string, string> = {};
|
|
253
|
+
|
|
254
|
+
for (const secretName of tool.secrets ?? []) {
|
|
255
|
+
const value = env[secretName];
|
|
256
|
+
|
|
257
|
+
if (!value) {
|
|
258
|
+
throw new AgentKitError("tool_secret_missing", `Tool "${tool.name}" requires missing secret "${secretName}".`);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
secrets[secretName] = value;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return { secrets, secretValues: Object.values(secrets) };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function collectConfiguredSecretValues(tool: AgentTool | undefined, env: Record<string, string | undefined>): string[] {
|
|
268
|
+
if (!tool) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return (tool.secrets ?? []).flatMap((secretName) => {
|
|
273
|
+
const value = env[secretName];
|
|
274
|
+
return value ? [value] : [];
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function runToolWithTimeout(
|
|
279
|
+
tool: AgentTool,
|
|
280
|
+
input: unknown,
|
|
281
|
+
context: Pick<ToolContext, "secrets" | "database" | "runtime"> & { signal?: AbortSignal },
|
|
282
|
+
timeoutMs: number,
|
|
283
|
+
): Promise<unknown> {
|
|
284
|
+
const controller = new AbortController();
|
|
285
|
+
const parentSignal = context.signal;
|
|
286
|
+
|
|
287
|
+
if (parentSignal?.aborted) {
|
|
288
|
+
throw new AgentKitError("runtime_aborted", `Tool "${tool.name}" was aborted before it started.`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const abortFromParent = () => controller.abort(parentSignal?.reason);
|
|
292
|
+
parentSignal?.addEventListener("abort", abortFromParent, { once: true });
|
|
293
|
+
|
|
294
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
298
|
+
timeout = setTimeout(() => {
|
|
299
|
+
controller.abort();
|
|
300
|
+
reject(new AgentKitError("tool_timeout", `Tool "${tool.name}" timed out after ${timeoutMs}ms.`));
|
|
301
|
+
}, timeoutMs);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const execution = Promise.resolve().then(() =>
|
|
305
|
+
tool.execute(input, {
|
|
306
|
+
secrets: context.secrets,
|
|
307
|
+
signal: controller.signal,
|
|
308
|
+
runtime: context.runtime,
|
|
309
|
+
database: context.database,
|
|
310
|
+
db: context.database,
|
|
311
|
+
storage: {
|
|
312
|
+
sql: context.database,
|
|
313
|
+
},
|
|
314
|
+
}),
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
return await Promise.race([execution, timeoutPromise]);
|
|
318
|
+
} finally {
|
|
319
|
+
if (timeout) {
|
|
320
|
+
clearTimeout(timeout);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
parentSignal?.removeEventListener("abort", abortFromParent);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function validateSchema(value: unknown, schema: JsonSchema, label: string): void {
|
|
328
|
+
const error = validateValue(value, schema, "$");
|
|
329
|
+
|
|
330
|
+
if (error) {
|
|
331
|
+
throw new AgentKitError("tool_validation_error", `${label} failed schema validation: ${error}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function validateValue(value: unknown, schema: JsonSchema, path: string): string | null {
|
|
336
|
+
if (Array.isArray(schema.enum) && !schema.enum.some((item) => Object.is(item, value))) {
|
|
337
|
+
return `${path} must be one of ${JSON.stringify(schema.enum)}.`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
341
|
+
const errors = types.map((type) => validateValueForType(value, schema, type, path)).filter((error) => error !== null);
|
|
342
|
+
|
|
343
|
+
if (errors.length < types.length) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (types.length > 1) {
|
|
348
|
+
return `${path} must match one of these schema types: ${types.join(", ")}.`;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return errors[0] ?? null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function validateValueForType(value: unknown, schema: JsonSchema, type: string, path: string): string | null {
|
|
355
|
+
switch (type) {
|
|
356
|
+
case "object":
|
|
357
|
+
return validateObject(value, schema, path);
|
|
358
|
+
case "array":
|
|
359
|
+
return validateArray(value, schema, path);
|
|
360
|
+
case "string":
|
|
361
|
+
return typeof value === "string" ? null : `${path} must be a string.`;
|
|
362
|
+
case "number":
|
|
363
|
+
return typeof value === "number" && Number.isFinite(value) ? null : `${path} must be a number.`;
|
|
364
|
+
case "integer":
|
|
365
|
+
return Number.isInteger(value) ? null : `${path} must be an integer.`;
|
|
366
|
+
case "boolean":
|
|
367
|
+
return typeof value === "boolean" ? null : `${path} must be a boolean.`;
|
|
368
|
+
case "null":
|
|
369
|
+
return value === null ? null : `${path} must be null.`;
|
|
370
|
+
default:
|
|
371
|
+
return `${path} has unsupported schema type "${type}".`;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function validateObject(value: unknown, schema: JsonSchema, path: string): string | null {
|
|
376
|
+
if (!isRecord(value)) {
|
|
377
|
+
return `${path} must be an object.`;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
for (const property of schema.required ?? []) {
|
|
381
|
+
if (!(property in value)) {
|
|
382
|
+
return `${path}.${property} is required.`;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
for (const [property, propertySchema] of Object.entries(schema.properties ?? {})) {
|
|
387
|
+
if (property in value) {
|
|
388
|
+
const error = validateValue(value[property], propertySchema, `${path}.${property}`);
|
|
389
|
+
|
|
390
|
+
if (error) {
|
|
391
|
+
return error;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (schema.additionalProperties === false) {
|
|
397
|
+
const allowedProperties = new Set(Object.keys(schema.properties ?? {}));
|
|
398
|
+
|
|
399
|
+
for (const property of Object.keys(value)) {
|
|
400
|
+
if (!allowedProperties.has(property)) {
|
|
401
|
+
return `${path}.${property} is not allowed.`;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function validateArray(value: unknown, schema: JsonSchema, path: string): string | null {
|
|
410
|
+
if (!Array.isArray(value)) {
|
|
411
|
+
return `${path} must be an array.`;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (!schema.items) {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
for (const [index, item] of value.entries()) {
|
|
419
|
+
const error = validateValue(item, schema.items, `${path}[${index}]`);
|
|
420
|
+
|
|
421
|
+
if (error) {
|
|
422
|
+
return error;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function normalizeOptionalNulls(value: unknown, schema: JsonSchema): unknown {
|
|
430
|
+
const types = schemaTypes(schema);
|
|
431
|
+
|
|
432
|
+
if (types.includes("object") && isRecord(value)) {
|
|
433
|
+
const required = new Set(schema.required ?? []);
|
|
434
|
+
const properties = schema.properties ?? {};
|
|
435
|
+
const normalized: Record<string, unknown> = { ...value };
|
|
436
|
+
|
|
437
|
+
for (const [property, propertySchema] of Object.entries(properties)) {
|
|
438
|
+
if (!(property in normalized)) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (normalized[property] === null && !required.has(property) && !allowsNull(propertySchema)) {
|
|
443
|
+
delete normalized[property];
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
normalized[property] = normalizeOptionalNulls(normalized[property], propertySchema);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return normalized;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (types.includes("array") && Array.isArray(value) && schema.items) {
|
|
454
|
+
return value.map((item) => normalizeOptionalNulls(item, schema.items as JsonSchema));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return value;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function schemaTypes(schema: JsonSchema): string[] {
|
|
461
|
+
return Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function allowsNull(schema: JsonSchema): boolean {
|
|
465
|
+
return schemaTypes(schema).includes("null") || (Array.isArray(schema.enum) && schema.enum.some((item) => item === null));
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
469
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
470
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ConversationRecord, StoredRun, StoredToolCall } from "../storage/sqlite";
|
|
2
|
+
import { openCapsuleStore } from "../storage/sqlite";
|
|
3
|
+
import { loadAgentCapsule } from "./config";
|
|
4
|
+
import { AgentKitError } from "./errors";
|
|
5
|
+
|
|
6
|
+
export type ConversationTrace = ConversationRecord & {
|
|
7
|
+
runs: ConversationTraceRun[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ConversationTraceRun = StoredRun & {
|
|
11
|
+
toolCalls: StoredToolCall[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export async function getConversationTraceFromCwd(cwd: string, conversationId: string): Promise<ConversationTrace> {
|
|
15
|
+
if (!conversationId) {
|
|
16
|
+
throw new AgentKitError("validation_error", "Missing conversation id.");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const capsule = await loadAgentCapsule(cwd);
|
|
20
|
+
const store = await openCapsuleStore(capsule);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const conversation = store.getConversation(conversationId);
|
|
24
|
+
|
|
25
|
+
if (!conversation) {
|
|
26
|
+
throw new AgentKitError("conversation_not_found", `Conversation "${conversationId}" was not found.`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const runs = store.listRunsForConversation(conversation.id).map((run) => ({
|
|
30
|
+
...run,
|
|
31
|
+
toolCalls: store.listToolCallsForRun(run.id),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
...conversation,
|
|
36
|
+
runs,
|
|
37
|
+
};
|
|
38
|
+
} finally {
|
|
39
|
+
store.close();
|
|
40
|
+
}
|
|
41
|
+
}
|