@andreprado/agentkit 0.1.0-alpha.2 → 0.1.0-alpha.20
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 +67 -6
- package/docs/guides/add-channel.md +118 -7
- package/docs/guides/add-knowledge.md +144 -0
- package/docs/guides/add-managed-composio.md +163 -0
- package/docs/guides/add-tool.md +1 -1
- package/docs/guides/channel-security.md +97 -32
- package/docs/guides/connect-discord.md +178 -0
- package/docs/guides/connect-slack.md +126 -0
- package/docs/guides/connect-telegram.md +78 -1
- package/docs/guides/connect-whatsapp-zapster.md +112 -8
- package/docs/guides/create-agent.md +45 -4
- package/docs/guides/debug-channel.md +147 -0
- package/docs/guides/improve-from-production.md +151 -0
- package/docs/guides/prepare-deploy.md +47 -17
- package/docs/guides/replay-production-traces.md +72 -0
- package/docs/guides/run-evals.md +147 -20
- package/docs/guides/security-rules.md +7 -6
- package/docs/guides/send-feedback.md +135 -0
- package/docs/guides/use-provider.md +27 -3
- package/docs/llms-full.txt +303 -55
- package/docs/llms.txt +57 -7
- package/package.json +2 -5
- package/src/cli/args.ts +57 -0
- package/src/cli/cloud-client.ts +377 -0
- package/src/cli/commands/channels.ts +1315 -0
- package/src/cli/commands/feedback.ts +438 -0
- package/src/cli/commands/knowledge.ts +136 -0
- package/src/cli/commands/transcribe.ts +171 -0
- package/src/cli/constants.ts +4 -0
- package/src/cli/deploy-chat-ui.ts +535 -0
- package/src/cli/deploy-readiness.ts +481 -0
- package/src/cli/flags.ts +162 -0
- package/src/cli/help.ts +236 -0
- package/src/cli/index.ts +1167 -1005
- package/src/cli/process.ts +31 -0
- package/src/cloud/artifact.ts +139 -0
- package/src/cloud/client.ts +80 -0
- package/src/cloud/contracts.ts +63 -0
- package/src/cloud/index.ts +3 -0
- package/src/create-project.ts +21 -6
- package/src/index.ts +479 -7
- package/src/providers/pi.ts +70 -16
- package/src/providers/test.ts +88 -1
- package/src/providers/types.ts +7 -0
- package/src/runtime/channel-buffer.ts +30 -0
- package/src/runtime/channel-test-harness.ts +8 -1
- package/src/runtime/channels/discord.ts +896 -0
- package/src/runtime/channels/slack.ts +646 -0
- package/src/runtime/channels/telegram.ts +466 -23
- package/src/runtime/channels/whatsapp-meta.ts +9 -0
- package/src/runtime/channels/whatsapp-zapster.ts +677 -40
- package/src/runtime/channels.ts +86 -3
- package/src/runtime/chat.ts +130 -38
- package/src/runtime/config.ts +483 -18
- package/src/runtime/core/manifest.ts +103 -5
- package/src/runtime/core/targets.ts +5 -5
- package/src/runtime/database.ts +93 -2
- package/src/runtime/db-commands.ts +9 -0
- package/src/runtime/deploy-readiness.ts +46 -4
- package/src/runtime/deploy.ts +1 -1
- package/src/runtime/dev-server.ts +759 -41
- package/src/runtime/env.ts +8 -3
- package/src/runtime/evals.ts +589 -43
- package/src/runtime/improve.ts +868 -0
- package/src/runtime/inspect.ts +194 -4
- package/src/runtime/integrations/composio.ts +423 -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 +303 -0
- package/src/runtime/knowledge/schema.ts +100 -0
- package/src/runtime/knowledge/tool.ts +64 -0
- package/src/runtime/knowledge/vector.ts +258 -0
- package/src/runtime/prompt-context.ts +141 -0
- package/src/runtime/runtime-contract.ts +86 -8
- package/src/runtime/skills.ts +95 -0
- package/src/runtime/spec.ts +152 -0
- package/src/runtime/sync.ts +144 -0
- package/src/runtime/targets/cloudflare/build.ts +1430 -185
- package/src/runtime/targets/container/server.ts +1 -1
- package/src/runtime/targets/vps/deploy.ts +26 -9
- package/src/runtime/tool-runner.ts +9 -1
- package/src/runtime/tools.ts +128 -2
- package/src/runtime/traces.ts +41 -0
- package/src/runtime/transcription.ts +483 -0
- package/src/storage/sqlite.ts +149 -3
- package/src/templates/blank.ts +76 -17
- package/src/templates/dentista.ts +1011 -0
- package/src/templates/index.ts +2 -0
- package/src/templates/skills/agentkit-build-agent/SKILL.md +52 -0
- package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +21 -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 +70 -0
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +15 -0
- package/src/templates/skills/agentkit-channels/SKILL.md +104 -0
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +65 -0
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +66 -0
- package/src/templates/skills/agentkit-channels/references/discord.md +93 -0
- package/src/templates/skills/agentkit-channels/references/slack.md +56 -0
- package/src/templates/skills/agentkit-channels/references/telegram.md +72 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +77 -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 +50 -0
- package/src/templates/skills/agentkit-evals/SKILL.md +109 -0
- package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +29 -0
- package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +18 -0
- package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +18 -0
- package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +27 -0
- package/src/templates/skills/agentkit-improve/SKILL.md +86 -0
- package/src/templates/skills/agentkit-improve/references/replay-side-effects.md +18 -0
- package/src/templates/skills/agentkit-improve/references/trace-packets.md +22 -0
- package/src/templates/skills/agentkit-improve/templates/regression.eval.md +18 -0
- package/src/templates/skills/agentkit-integrations/SKILL.md +76 -0
- package/src/templates/skills/agentkit-knowledge/SKILL.md +43 -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 +47 -0
- package/src/templates/skills/agentkit-prompts/templates/knowledge-grounded-faq.instructions.md +11 -0
- package/src/templates/skills/agentkit-provider/SKILL.md +60 -0
- package/src/templates/skills/agentkit-security/SKILL.md +56 -0
- package/src/templates/skills/agentkit-tools/SKILL.md +37 -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 +76 -0
- package/src/templates/support.ts +77 -18
- package/docs/guides/channels-production-handoff.md +0 -99
- package/docs/portable-deploy-release-checklist.md +0 -41
- package/src/runtime/targets/cloudflare/deploy.ts +0 -5475
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { chmod, mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import { AgentKitError } from "../../runtime/errors";
|
|
7
|
+
import { findAgentCapsuleRoot, loadAgentCapsule } from "../../runtime/config";
|
|
8
|
+
import { readLocalDeployStateIfExists } from "../../runtime/core/deploy-state";
|
|
9
|
+
import type { ParsedArgs } from "../args";
|
|
10
|
+
import {
|
|
11
|
+
cloudApiRequest,
|
|
12
|
+
readCloudAuthForApiUrl,
|
|
13
|
+
resolveCloudApiUrl,
|
|
14
|
+
type FeedbackSendResponse,
|
|
15
|
+
} from "../cloud-client";
|
|
16
|
+
import { parseOptionalStringFlag } from "../flags";
|
|
17
|
+
|
|
18
|
+
type FeedbackKind =
|
|
19
|
+
| "bug"
|
|
20
|
+
| "confusion"
|
|
21
|
+
| "missing_docs"
|
|
22
|
+
| "feature_request"
|
|
23
|
+
| "deploy_issue"
|
|
24
|
+
| "runtime_issue"
|
|
25
|
+
| "other";
|
|
26
|
+
|
|
27
|
+
type FeedbackAbout = "last-run" | "deploy" | "manual";
|
|
28
|
+
|
|
29
|
+
type FeedbackDraft = {
|
|
30
|
+
schema_version: "agentkit.feedback.v1";
|
|
31
|
+
client_report_id: string;
|
|
32
|
+
kind: FeedbackKind;
|
|
33
|
+
summary: string;
|
|
34
|
+
message?: string;
|
|
35
|
+
source: Record<string, unknown>;
|
|
36
|
+
context: Record<string, unknown>;
|
|
37
|
+
diagnostics: Record<string, unknown>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const feedbackKinds = new Set<FeedbackKind>([
|
|
41
|
+
"bug",
|
|
42
|
+
"confusion",
|
|
43
|
+
"missing_docs",
|
|
44
|
+
"feature_request",
|
|
45
|
+
"deploy_issue",
|
|
46
|
+
"runtime_issue",
|
|
47
|
+
"other",
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
const feedbackAboutValues = new Set<FeedbackAbout>(["last-run", "deploy", "manual"]);
|
|
51
|
+
|
|
52
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
53
|
+
|
|
54
|
+
export async function handleFeedbackCommand(args: ParsedArgs): Promise<void> {
|
|
55
|
+
const [subcommand, draftPath] = args.positional;
|
|
56
|
+
|
|
57
|
+
if (subcommand === "create") {
|
|
58
|
+
const draft = await buildFeedbackDraft(process.cwd(), args);
|
|
59
|
+
const written = await writeFeedbackDraft(process.cwd(), draft, args.flags.out);
|
|
60
|
+
|
|
61
|
+
console.log("Feedback draft created");
|
|
62
|
+
console.log(`JSON: ${relativePath(process.cwd(), written.jsonPath)}`);
|
|
63
|
+
console.log(`Markdown: ${relativePath(process.cwd(), written.markdownPath)}`);
|
|
64
|
+
console.log(`Send: agentkit feedback send ${relativePath(process.cwd(), written.jsonPath)}`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (subcommand === "preview") {
|
|
69
|
+
const draft = draftPath
|
|
70
|
+
? await readFeedbackDraft(resolve(process.cwd(), draftPath))
|
|
71
|
+
: await buildFeedbackDraft(process.cwd(), args);
|
|
72
|
+
console.log(JSON.stringify(draft, null, 2));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (subcommand === "send") {
|
|
77
|
+
const apiUrl = await resolveCloudApiUrl(args.flags.api);
|
|
78
|
+
const auth = await readCloudAuthForApiUrl(apiUrl);
|
|
79
|
+
|
|
80
|
+
if (!auth) {
|
|
81
|
+
throw new AgentKitError(
|
|
82
|
+
"auth_required",
|
|
83
|
+
`Log in before sending feedback: agentkit login --token <token> --api ${apiUrl}`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const draft = draftPath
|
|
88
|
+
? await readFeedbackDraft(resolve(process.cwd(), draftPath))
|
|
89
|
+
: await buildFeedbackDraft(process.cwd(), args);
|
|
90
|
+
|
|
91
|
+
if (!draftPath && args.flags.save === true) {
|
|
92
|
+
const written = await writeFeedbackDraft(process.cwd(), draft, args.flags.out);
|
|
93
|
+
console.log(`Feedback draft saved: ${relativePath(process.cwd(), written.jsonPath)}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const payload = (await cloudApiRequest(apiUrl, "/v1/feedback", {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: {
|
|
99
|
+
"Idempotency-Key": draft.client_report_id,
|
|
100
|
+
},
|
|
101
|
+
body: JSON.stringify(feedbackDraftToSubmission(draft)),
|
|
102
|
+
})) as FeedbackSendResponse;
|
|
103
|
+
|
|
104
|
+
const feedback = payload.feedback;
|
|
105
|
+
|
|
106
|
+
if (!feedback?.id) {
|
|
107
|
+
throw new AgentKitError("feedback_response_invalid", "AgentKit Cloud returned an invalid feedback response.");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log("Feedback sent");
|
|
111
|
+
console.log(`ID: ${feedback.id}`);
|
|
112
|
+
console.log(`Kind: ${feedback.kind ?? draft.kind}`);
|
|
113
|
+
console.log(`Status: ${feedback.status ?? "new"}`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
throw new Error(
|
|
118
|
+
"Usage: agentkit feedback create|preview|send [draft.json] [--about last-run|deploy|manual] [--kind bug|confusion|missing_docs|feature_request|deploy_issue|runtime_issue|other] [--summary <text>] [--message <text>] [--api <url>]",
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function buildFeedbackDraft(cwd: string, args: ParsedArgs): Promise<FeedbackDraft> {
|
|
123
|
+
const about = parseFeedbackAbout(args.flags.about);
|
|
124
|
+
const kind = parseFeedbackKind(args.flags.kind, about === "deploy" ? "deploy_issue" : "other");
|
|
125
|
+
const message = parseOptionalStringFlag(args.flags.message, "message", "agentkit feedback create --message <text>");
|
|
126
|
+
const summary =
|
|
127
|
+
parseOptionalStringFlag(args.flags.summary, "summary", "agentkit feedback create --summary <text>") ??
|
|
128
|
+
summarizeMessage(message);
|
|
129
|
+
|
|
130
|
+
if (!summary) {
|
|
131
|
+
throw new Error("Missing summary. Usage: agentkit feedback create --summary <text> [--message <text>]");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const now = new Date().toISOString();
|
|
135
|
+
const context = await collectFeedbackContext(cwd);
|
|
136
|
+
const draft: FeedbackDraft = {
|
|
137
|
+
schema_version: "agentkit.feedback.v1",
|
|
138
|
+
client_report_id: `fbdraft_${Date.now().toString(36)}_${randomBytes(6).toString("hex")}`,
|
|
139
|
+
kind,
|
|
140
|
+
summary: redactText(summary).slice(0, 200),
|
|
141
|
+
...(message ? { message: redactText(message).slice(0, 5_000) } : {}),
|
|
142
|
+
source: {
|
|
143
|
+
channel: "cli",
|
|
144
|
+
about,
|
|
145
|
+
created_at: now,
|
|
146
|
+
agentkit_version: await readAgentKitVersion(),
|
|
147
|
+
},
|
|
148
|
+
context,
|
|
149
|
+
diagnostics: {
|
|
150
|
+
redaction: "agentkit-cli-redaction-v1",
|
|
151
|
+
note:
|
|
152
|
+
about === "last-run"
|
|
153
|
+
? "AgentKit does not persist a last-command transcript yet; this draft includes current capsule and deploy context."
|
|
154
|
+
: "This draft includes current capsule and deploy context.",
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return redactJson(draft) as FeedbackDraft;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function collectFeedbackContext(cwd: string): Promise<Record<string, unknown>> {
|
|
162
|
+
const context: Record<string, unknown> = {
|
|
163
|
+
platform: {
|
|
164
|
+
node: process.version,
|
|
165
|
+
bun: (globalThis as { Bun?: { version?: string } }).Bun?.version,
|
|
166
|
+
platform: process.platform,
|
|
167
|
+
arch: process.arch,
|
|
168
|
+
package_manager: await detectPackageManager(cwd),
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const root = await findAgentCapsuleRoot(cwd).catch(() => null);
|
|
173
|
+
|
|
174
|
+
if (!root) {
|
|
175
|
+
context.capsule = {
|
|
176
|
+
detected: false,
|
|
177
|
+
cwd_name: basename(cwd),
|
|
178
|
+
};
|
|
179
|
+
return redactJson(context) as Record<string, unknown>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const capsule = await loadAgentCapsule(root).catch((error: unknown) => ({
|
|
183
|
+
error: error instanceof Error ? error.message : String(error),
|
|
184
|
+
}));
|
|
185
|
+
const deployState = await readLocalDeployStateIfExists(root).catch(() => null);
|
|
186
|
+
|
|
187
|
+
if ("config" in capsule) {
|
|
188
|
+
context.capsule = {
|
|
189
|
+
detected: true,
|
|
190
|
+
root_name: basename(root),
|
|
191
|
+
name: capsule.config.name,
|
|
192
|
+
runtime: capsule.config.runtime,
|
|
193
|
+
provider: capsule.config.provider,
|
|
194
|
+
access: capsule.config.access,
|
|
195
|
+
storage: capsule.config.storage,
|
|
196
|
+
secret_names: capsule.config.secrets,
|
|
197
|
+
tool_count: capsule.config.tools?.length ?? 0,
|
|
198
|
+
channel_count: capsule.config.channels?.length ?? 0,
|
|
199
|
+
};
|
|
200
|
+
} else {
|
|
201
|
+
context.capsule = {
|
|
202
|
+
detected: true,
|
|
203
|
+
root_name: basename(root),
|
|
204
|
+
load_error: capsule.error,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (deployState) {
|
|
209
|
+
context.deploy = {
|
|
210
|
+
project_id: deployState.project_id,
|
|
211
|
+
deploy_id: deployState.deploy_id,
|
|
212
|
+
target: deployState.target,
|
|
213
|
+
runtime: deployState.runtime,
|
|
214
|
+
owner_state: deployState.owner_state,
|
|
215
|
+
access: deployState.access,
|
|
216
|
+
url: deployState.url,
|
|
217
|
+
created_at: deployState.created_at,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return redactJson(context) as Record<string, unknown>;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function detectPackageManager(cwd: string): Promise<string> {
|
|
225
|
+
const candidates = [
|
|
226
|
+
["bun.lock", "bun"],
|
|
227
|
+
["bun.lockb", "bun"],
|
|
228
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
229
|
+
["yarn.lock", "yarn"],
|
|
230
|
+
["package-lock.json", "npm"],
|
|
231
|
+
] as const;
|
|
232
|
+
|
|
233
|
+
for (const [file, name] of candidates) {
|
|
234
|
+
if (await pathExists(join(cwd, file))) {
|
|
235
|
+
return name;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return "unknown";
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function writeFeedbackDraft(
|
|
243
|
+
cwd: string,
|
|
244
|
+
draft: FeedbackDraft,
|
|
245
|
+
outFlag: string | boolean | undefined,
|
|
246
|
+
): Promise<{ jsonPath: string; markdownPath: string }> {
|
|
247
|
+
const out = parseOptionalStringFlag(outFlag, "out", "agentkit feedback create --out <path>");
|
|
248
|
+
const defaultDir = join(cwd, ".agentkit", "feedback");
|
|
249
|
+
const slug = slugify(draft.summary);
|
|
250
|
+
const filename = `${new Date().toISOString().replace(/[:.]/g, "-")}-${slug}`;
|
|
251
|
+
const jsonPath = out && extname(out) === ".json" ? resolve(cwd, out) : join(out ? resolve(cwd, out) : defaultDir, `${filename}.json`);
|
|
252
|
+
const markdownPath = jsonPath.replace(/\.json$/, ".md");
|
|
253
|
+
|
|
254
|
+
await mkdir(dirname(jsonPath), { recursive: true });
|
|
255
|
+
await writeFile(jsonPath, `${JSON.stringify(draft, null, 2)}\n`, { mode: 0o600 });
|
|
256
|
+
await chmod(jsonPath, 0o600);
|
|
257
|
+
await writeFile(markdownPath, renderFeedbackMarkdown(draft), { mode: 0o600 });
|
|
258
|
+
await chmod(markdownPath, 0o600);
|
|
259
|
+
|
|
260
|
+
return { jsonPath, markdownPath };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function readFeedbackDraft(path: string): Promise<FeedbackDraft> {
|
|
264
|
+
const parsed = JSON.parse(await readFile(path, "utf8")) as unknown;
|
|
265
|
+
const draft = validateFeedbackDraft(parsed);
|
|
266
|
+
|
|
267
|
+
if (!draft) {
|
|
268
|
+
throw new AgentKitError("feedback_draft_invalid", "Feedback draft must be an agentkit.feedback.v1 JSON object.");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return redactJson(draft) as FeedbackDraft;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function validateFeedbackDraft(value: unknown): FeedbackDraft | null {
|
|
275
|
+
if (!isRecord(value) || value.schema_version !== "agentkit.feedback.v1") {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (typeof value.client_report_id !== "string" || !/^[A-Za-z0-9._:-]{1,128}$/.test(value.client_report_id)) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!isFeedbackKind(value.kind) || typeof value.summary !== "string" || !isRecord(value.source)) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
schema_version: "agentkit.feedback.v1",
|
|
289
|
+
client_report_id: value.client_report_id,
|
|
290
|
+
kind: value.kind,
|
|
291
|
+
summary: redactText(value.summary).slice(0, 200),
|
|
292
|
+
...(typeof value.message === "string" ? { message: redactText(value.message).slice(0, 5_000) } : {}),
|
|
293
|
+
source: redactJson(value.source) as Record<string, unknown>,
|
|
294
|
+
context: isRecord(value.context) ? redactJson(value.context) as Record<string, unknown> : {},
|
|
295
|
+
diagnostics: isRecord(value.diagnostics) ? redactJson(value.diagnostics) as Record<string, unknown> : {},
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function feedbackDraftToSubmission(draft: FeedbackDraft): Record<string, unknown> {
|
|
300
|
+
return {
|
|
301
|
+
kind: draft.kind,
|
|
302
|
+
summary: draft.summary,
|
|
303
|
+
...(draft.message ? { message: draft.message } : {}),
|
|
304
|
+
source: draft.source,
|
|
305
|
+
context: draft.context,
|
|
306
|
+
diagnostics: draft.diagnostics,
|
|
307
|
+
client_report_id: draft.client_report_id,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function renderFeedbackMarkdown(draft: FeedbackDraft): string {
|
|
312
|
+
const lines = [
|
|
313
|
+
"# AgentKit Feedback Draft",
|
|
314
|
+
"",
|
|
315
|
+
`Kind: ${draft.kind}`,
|
|
316
|
+
`Summary: ${draft.summary}`,
|
|
317
|
+
`Client report: ${draft.client_report_id}`,
|
|
318
|
+
"",
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
if (draft.message) {
|
|
322
|
+
lines.push("## Message", "", draft.message, "");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
lines.push("## Context", "", "```json", JSON.stringify(draft.context, null, 2), "```", "");
|
|
326
|
+
lines.push("Send with the JSON path printed by `agentkit feedback create`:", "", "```sh\nagentkit feedback send <draft.json>\n```", "");
|
|
327
|
+
|
|
328
|
+
return `${lines.join("\n")}\n`;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function parseFeedbackKind(value: string | boolean | undefined, fallback: FeedbackKind): FeedbackKind {
|
|
332
|
+
if (value === undefined || value === false) {
|
|
333
|
+
return fallback;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (typeof value === "string" && isFeedbackKind(value)) {
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
throw new Error("feedback --kind must be bug, confusion, missing_docs, feature_request, deploy_issue, runtime_issue, or other.");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function parseFeedbackAbout(value: string | boolean | undefined): FeedbackAbout {
|
|
344
|
+
if (value === undefined || value === false) {
|
|
345
|
+
return "last-run";
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (typeof value === "string" && feedbackAboutValues.has(value as FeedbackAbout)) {
|
|
349
|
+
return value as FeedbackAbout;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
throw new Error("feedback --about must be last-run, deploy, or manual.");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function summarizeMessage(message: string | undefined): string | undefined {
|
|
356
|
+
if (!message) {
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return message.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function isFeedbackKind(value: unknown): value is FeedbackKind {
|
|
364
|
+
return typeof value === "string" && feedbackKinds.has(value as FeedbackKind);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function redactJson(value: unknown, key = ""): unknown {
|
|
368
|
+
if (Array.isArray(value)) {
|
|
369
|
+
return value.map((item) => redactJson(item, key));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!isRecord(value)) {
|
|
373
|
+
return typeof value === "string" ? redactText(value) : value;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const out: Record<string, unknown> = {};
|
|
377
|
+
|
|
378
|
+
for (const [nestedKey, nestedValue] of Object.entries(value)) {
|
|
379
|
+
if (shouldRedactKey(nestedKey)) {
|
|
380
|
+
out[nestedKey] = "[redacted]";
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
out[nestedKey] = redactJson(nestedValue, nestedKey);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return out;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function shouldRedactKey(key: string): boolean {
|
|
391
|
+
return /token|password|credential|api[_-]?key|private[_-]?key/i.test(key) && key !== "client_report_id";
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function redactText(value: string): string {
|
|
395
|
+
return value
|
|
396
|
+
.replace(/Authorization:\s*Bearer\s+[A-Za-z0-9._:-]+/gi, "Authorization: Bearer [redacted]")
|
|
397
|
+
.replace(/\b(?:agk_user|agk_deploy|ak_live|bsec|whsec|sk|rk|pk)[-_A-Za-z0-9]{12,}\b/g, "[redacted-token]")
|
|
398
|
+
.replace(/\b[A-Za-z0-9_-]{24,}\.[A-Za-z0-9_-]{24,}\.[A-Za-z0-9_-]{24,}\b/g, "[redacted-token]");
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function slugify(value: string): string {
|
|
402
|
+
const slug = value
|
|
403
|
+
.toLowerCase()
|
|
404
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
405
|
+
.replace(/^-+|-+$/g, "")
|
|
406
|
+
.slice(0, 48);
|
|
407
|
+
|
|
408
|
+
return slug || "feedback";
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async function readAgentKitVersion(): Promise<string> {
|
|
412
|
+
try {
|
|
413
|
+
const packageJson = JSON.parse(await readFile(join(moduleDir, "..", "..", "..", "package.json"), "utf8")) as {
|
|
414
|
+
version?: unknown;
|
|
415
|
+
};
|
|
416
|
+
return typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
417
|
+
} catch {
|
|
418
|
+
return "unknown";
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
async function pathExists(path: string): Promise<boolean> {
|
|
423
|
+
try {
|
|
424
|
+
await stat(path);
|
|
425
|
+
return true;
|
|
426
|
+
} catch {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function relativePath(root: string, path: string): string {
|
|
432
|
+
const output = relative(root, path);
|
|
433
|
+
return output.startsWith("..") ? path : output || ".";
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
437
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
438
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addKnowledgeSourceFromCwd,
|
|
3
|
+
listKnowledgeSourcesFromCwd,
|
|
4
|
+
syncKnowledgeSourcesFromCwd,
|
|
5
|
+
} from "../../runtime/knowledge/ingest";
|
|
6
|
+
import { searchKnowledgeFromCwd } from "../../runtime/knowledge/retrieve";
|
|
7
|
+
import type { ParsedArgs } from "../args";
|
|
8
|
+
|
|
9
|
+
export async function handleKnowledgeCommand(args: ParsedArgs): Promise<void> {
|
|
10
|
+
const [subcommand, ...rest] = args.positional;
|
|
11
|
+
|
|
12
|
+
if (subcommand === "add") {
|
|
13
|
+
const [source] = rest;
|
|
14
|
+
|
|
15
|
+
if (!source) {
|
|
16
|
+
throw new Error("Usage: agentkit knowledge add <path-or-url>");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const summary = await addKnowledgeSourceFromCwd(process.cwd(), source);
|
|
20
|
+
printIngestSummary("Knowledge add", summary);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (subcommand === "sync") {
|
|
25
|
+
const summary = await syncKnowledgeSourcesFromCwd(process.cwd());
|
|
26
|
+
printIngestSummary("Knowledge sync", summary);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (subcommand === "inspect") {
|
|
31
|
+
const result = await listKnowledgeSourcesFromCwd(process.cwd());
|
|
32
|
+
|
|
33
|
+
console.log("Knowledge sources");
|
|
34
|
+
console.log(`Database: ${result.databasePath}`);
|
|
35
|
+
|
|
36
|
+
if (result.sources.length === 0) {
|
|
37
|
+
console.log("No knowledge sources indexed.");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log("");
|
|
42
|
+
console.log("source\tstatus\tchunks\tupdated_at\ttitle");
|
|
43
|
+
|
|
44
|
+
for (const source of result.sources) {
|
|
45
|
+
console.log(
|
|
46
|
+
[
|
|
47
|
+
source.source,
|
|
48
|
+
source.status,
|
|
49
|
+
String(source.chunkCount),
|
|
50
|
+
source.updatedAt,
|
|
51
|
+
source.title ?? "",
|
|
52
|
+
].join("\t"),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (subcommand === "search") {
|
|
60
|
+
const query = rest.join(" ").trim();
|
|
61
|
+
|
|
62
|
+
if (!query) {
|
|
63
|
+
throw new Error("Usage: agentkit knowledge search <query> [--top-k <number>]");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const topK = parseTopK(args.flags["top-k"] ?? args.flags.topK);
|
|
67
|
+
const result = await searchKnowledgeFromCwd(process.cwd(), query, { topK });
|
|
68
|
+
|
|
69
|
+
console.log("Knowledge search");
|
|
70
|
+
console.log(`Database: ${result.databasePath}`);
|
|
71
|
+
console.log(`Query: ${query}`);
|
|
72
|
+
|
|
73
|
+
if (result.results.length === 0) {
|
|
74
|
+
console.log("No matching knowledge chunks found.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log("");
|
|
79
|
+
|
|
80
|
+
for (const [index, item] of result.results.entries()) {
|
|
81
|
+
const source = [
|
|
82
|
+
item.source.path,
|
|
83
|
+
item.source.section ? `section: ${item.source.section}` : null,
|
|
84
|
+
item.source.locator,
|
|
85
|
+
].filter(Boolean).join(" | ");
|
|
86
|
+
|
|
87
|
+
console.log(`${index + 1}. ${source}`);
|
|
88
|
+
console.log(` score: ${item.score}`);
|
|
89
|
+
console.log(` ${oneLine(item.content)}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
throw new Error(
|
|
96
|
+
"Usage: agentkit knowledge add <path-or-url> | sync | inspect | search <query> [--top-k <number>]",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function printIngestSummary(label: string, summary: Awaited<ReturnType<typeof addKnowledgeSourceFromCwd>>): void {
|
|
101
|
+
console.log(label);
|
|
102
|
+
console.log(`Database: ${summary.databasePath}`);
|
|
103
|
+
console.log(`${summary.indexed} indexed, ${summary.skipped} skipped, ${summary.failed} failed, ${summary.chunksCreated} chunks`);
|
|
104
|
+
|
|
105
|
+
if (summary.results.length > 0) {
|
|
106
|
+
console.log("");
|
|
107
|
+
console.log("source\tstatus\tchunks\tmessage");
|
|
108
|
+
|
|
109
|
+
for (const result of summary.results) {
|
|
110
|
+
console.log([result.source, result.status, String(result.chunksCreated), result.message].join("\t"));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (summary.failed > 0) {
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function parseTopK(value: unknown): number | undefined {
|
|
120
|
+
if (value === undefined) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const topK = Number(value);
|
|
125
|
+
|
|
126
|
+
if (!Number.isInteger(topK) || topK <= 0 || topK > 50) {
|
|
127
|
+
throw new Error("knowledge search --top-k must be a positive integer no larger than 50.");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return topK;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function oneLine(value: string): string {
|
|
134
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
135
|
+
return compact.length <= 220 ? compact : `${compact.slice(0, 217)}...`;
|
|
136
|
+
}
|