@andreprado/agentkit 0.1.0-alpha.3 → 0.1.0-alpha.4
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 +9 -3
- package/docs/guides/add-tool.md +1 -1
- package/docs/guides/channels-implementation-map.md +243 -0
- package/docs/guides/create-agent.md +22 -1
- package/docs/guides/prepare-deploy.md +17 -6
- package/docs/guides/run-evals.md +28 -2
- package/docs/guides/security-rules.md +1 -1
- package/docs/guides/use-provider.md +16 -2
- package/docs/llms-full.txt +67 -36
- package/docs/llms.txt +13 -3
- package/package.json +1 -1
- package/src/cli/index.ts +318 -14
- package/src/providers/test.ts +51 -0
- package/src/runtime/chat.ts +59 -4
- package/src/runtime/dev-server.ts +154 -16
- package/src/runtime/runtime-contract.ts +16 -2
- package/src/runtime/targets/cloudflare/build.ts +79 -4
- package/src/storage/sqlite.ts +2 -2
- package/src/templates/blank.ts +41 -0
- package/src/templates/dentista.ts +988 -0
- package/src/templates/index.ts +2 -0
- package/src/templates/support.ts +39 -0
package/src/cli/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { AgentKitError, isAgentKitError } from "../runtime/errors";
|
|
|
29
29
|
import { prepareVpsDeploy } from "../runtime/targets/vps/deploy";
|
|
30
30
|
|
|
31
31
|
const defaultAgentKitCloudApiUrl = "https://agentkit-cloud.aibuilders.com.br";
|
|
32
|
+
const defaultAgentKitDevPort = 4123;
|
|
32
33
|
const defaultDeployChatAccessTokenName = "agentkit-chat-ui";
|
|
33
34
|
const defaultDeployChatAccessTokenPath = ".agentkit/chat-access-token.json";
|
|
34
35
|
|
|
@@ -47,10 +48,11 @@ Usage:
|
|
|
47
48
|
agentkit <command> [options]
|
|
48
49
|
|
|
49
50
|
Start:
|
|
50
|
-
agentkit new <name> [--template blank|support] [--no-install]
|
|
51
|
+
agentkit new <name> [--template blank|support|dentista] [--no-install]
|
|
51
52
|
agentkit dev
|
|
53
|
+
agentkit open
|
|
52
54
|
agentkit chat-ui --deploy
|
|
53
|
-
agentkit chat --message <text>
|
|
55
|
+
agentkit chat --message <text> [--conversation-id <id>]
|
|
54
56
|
agentkit inspect
|
|
55
57
|
|
|
56
58
|
Build:
|
|
@@ -63,7 +65,8 @@ Deploy:
|
|
|
63
65
|
agentkit login --token <token>
|
|
64
66
|
agentkit deploy doctor
|
|
65
67
|
agentkit secret set <NAME> --stdin
|
|
66
|
-
agentkit
|
|
68
|
+
agentkit secret sync --from-local
|
|
69
|
+
agentkit deploy --smoke "hello"
|
|
67
70
|
agentkit deploy status
|
|
68
71
|
|
|
69
72
|
More:
|
|
@@ -79,10 +82,11 @@ function renderCommandReference(): string {
|
|
|
79
82
|
return `AgentKit Command Reference
|
|
80
83
|
|
|
81
84
|
Usage:
|
|
82
|
-
agentkit new <name> [--template blank|support] [--no-install]
|
|
83
|
-
agentkit dev
|
|
85
|
+
agentkit new <name> [--template blank|support|dentista] [--no-install]
|
|
86
|
+
agentkit dev [--port <number>]
|
|
87
|
+
agentkit open
|
|
84
88
|
agentkit chat-ui --deploy [--port <number>] [--token-file <path>]
|
|
85
|
-
agentkit chat --message <text>
|
|
89
|
+
agentkit chat --message <text> [--conversation-id <id>]
|
|
86
90
|
agentkit tool <name> [--input <path-or-json>]
|
|
87
91
|
agentkit db migrate
|
|
88
92
|
agentkit db reset --yes
|
|
@@ -99,11 +103,12 @@ Usage:
|
|
|
99
103
|
agentkit channels deliveries list <name> [--api <url>]
|
|
100
104
|
agentkit channels deliveries show <delivery-id> [--api <url>]
|
|
101
105
|
agentkit inspect
|
|
102
|
-
agentkit build [--target cloudflare]
|
|
106
|
+
agentkit build [--target cloudflare|container]
|
|
103
107
|
agentkit login --token <token> [--api <url>]
|
|
104
108
|
agentkit logout
|
|
105
|
-
agentkit deploy [--target cloudflare|vps] [--host <host>] [--api <url>] [--dry-run] [--anonymous] [--local-wrangler]
|
|
109
|
+
agentkit deploy [--target cloudflare|vps] [--host <host>] [--api <url>] [--dry-run] [--anonymous] [--local-wrangler] [--smoke <message>]
|
|
106
110
|
agentkit deploy doctor [--api <url>] [--anonymous]
|
|
111
|
+
agentkit deploy smoke [--message <text>] [--api <url>]
|
|
107
112
|
agentkit deploy status [--api <url>]
|
|
108
113
|
agentkit deploy pause [--api <url>]
|
|
109
114
|
agentkit deploy resume [--api <url>]
|
|
@@ -111,6 +116,7 @@ Usage:
|
|
|
111
116
|
agentkit secret set <NAME> --stdin [--api <url>]
|
|
112
117
|
agentkit secret set <NAME> --from-env [ENV_NAME] [--api <url>]
|
|
113
118
|
agentkit secret set <NAME> --from-local-env [--api <url>]
|
|
119
|
+
agentkit secret sync --from-local [--api <url>]
|
|
114
120
|
agentkit secret list [--api <url>]
|
|
115
121
|
agentkit secret unset <NAME> [--api <url>]
|
|
116
122
|
agentkit access token create <name> [--api <url>] [--out <path>]
|
|
@@ -131,6 +137,7 @@ Usage:
|
|
|
131
137
|
Commands:
|
|
132
138
|
new Create a new Agent Capsule
|
|
133
139
|
dev Run the Agent Capsule locally
|
|
140
|
+
open Open the local Agent Capsule UI if it is already running
|
|
134
141
|
chat-ui Run a local chat UI, optionally pointed at a hosted deploy
|
|
135
142
|
chat Send one message to the local Agent Capsule
|
|
136
143
|
tool Run one registered tool directly
|
|
@@ -154,8 +161,11 @@ Examples:
|
|
|
154
161
|
agentkit new support-agent --template support
|
|
155
162
|
cd support-agent
|
|
156
163
|
agentkit dev
|
|
164
|
+
agentkit open
|
|
157
165
|
agentkit tool lookup_order --input '{"orderId":"A100"}'
|
|
158
166
|
agentkit eval run
|
|
167
|
+
|
|
168
|
+
agentkit new clara-dentista --template dentista
|
|
159
169
|
`;
|
|
160
170
|
}
|
|
161
171
|
|
|
@@ -192,6 +202,33 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
192
202
|
return { command, positional, flags };
|
|
193
203
|
}
|
|
194
204
|
|
|
205
|
+
function isHelpRequested(args: ParsedArgs): boolean {
|
|
206
|
+
return args.flags.help === true || args.flags.h === true || args.positional.includes("--help") || args.positional.includes("-h");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function renderCommandHelp(command: string): string {
|
|
210
|
+
const usage: Record<string, string> = {
|
|
211
|
+
new: "agentkit new <name> [--template blank|support|dentista] [--no-install]",
|
|
212
|
+
dev: "agentkit dev [--port <number>]",
|
|
213
|
+
open: "agentkit open",
|
|
214
|
+
"chat-ui": "agentkit chat-ui --deploy [--port <number>] [--token-file <path>]",
|
|
215
|
+
chat: 'agentkit chat --message "hello" [--conversation-id <id>]',
|
|
216
|
+
tool: "agentkit tool <name> [--input <path-or-json>]",
|
|
217
|
+
deploy:
|
|
218
|
+
'agentkit deploy [--smoke "hello"] | agentkit deploy smoke [--message "hello"] | agentkit deploy doctor | status | pause | resume',
|
|
219
|
+
secret:
|
|
220
|
+
"agentkit secret set <NAME> --stdin | --from-env [ENV_NAME] | --from-local-env | agentkit secret sync --from-local | list | unset <NAME>",
|
|
221
|
+
access: "agentkit access token create <name> [--out <path>] | list | revoke <token-id>",
|
|
222
|
+
env: "agentkit env set <NAME> --stdin | --from-env [ENV_NAME] | list | unset <NAME>",
|
|
223
|
+
docs: "agentkit docs path | llms | full",
|
|
224
|
+
handoff: "agentkit handoff codex [goal] | agentkit handoff claude [goal]",
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return `Usage: ${usage[command] ?? "agentkit <command> [options]"}
|
|
228
|
+
|
|
229
|
+
Run \`agentkit help commands\` for the full command reference.`;
|
|
230
|
+
}
|
|
231
|
+
|
|
195
232
|
async function main() {
|
|
196
233
|
const args = parseArgs(process.argv.slice(2));
|
|
197
234
|
|
|
@@ -200,6 +237,11 @@ async function main() {
|
|
|
200
237
|
return;
|
|
201
238
|
}
|
|
202
239
|
|
|
240
|
+
if (isHelpRequested(args)) {
|
|
241
|
+
console.log(renderCommandHelp(args.command));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
203
245
|
if (args.command === "help") {
|
|
204
246
|
const [topic] = args.positional;
|
|
205
247
|
|
|
@@ -222,7 +264,7 @@ async function main() {
|
|
|
222
264
|
const shouldInstall = args.flags["no-install"] !== true;
|
|
223
265
|
|
|
224
266
|
if (!name) {
|
|
225
|
-
throw new Error("Missing project name. Usage: agentkit new <name> [--template blank|support] [--no-install]");
|
|
267
|
+
throw new Error("Missing project name. Usage: agentkit new <name> [--template blank|support|dentista] [--no-install]");
|
|
226
268
|
}
|
|
227
269
|
|
|
228
270
|
const result = await createProject({ name, template, cwd: process.cwd() });
|
|
@@ -254,10 +296,17 @@ async function main() {
|
|
|
254
296
|
|
|
255
297
|
if (args.command === "dev") {
|
|
256
298
|
const port = parsePort(args.flags.port, "agentkit dev --port <number>");
|
|
257
|
-
const devServer = await startAgentDevServer(process.cwd(), { port });
|
|
299
|
+
const devServer = await startAgentDevServer(process.cwd(), { port, basePort: localDevBasePort() });
|
|
258
300
|
|
|
259
301
|
console.log("Agent Capsule running");
|
|
260
302
|
console.log("");
|
|
303
|
+
if (devServer.portConflict) {
|
|
304
|
+
const owner = devServer.portConflict.agent ? ` by ${devServer.portConflict.agent}` : "";
|
|
305
|
+
console.log(
|
|
306
|
+
`Port ${devServer.portConflict.port} is already used${owner}. Started ${devServer.capsule.config.name} at ${devServer.urls.chat}.`,
|
|
307
|
+
);
|
|
308
|
+
console.log("");
|
|
309
|
+
}
|
|
261
310
|
console.log(`Chat: ${devServer.urls.chat}`);
|
|
262
311
|
console.log(`API: ${devServer.urls.api}`);
|
|
263
312
|
console.log(`Inspect: ${devServer.urls.inspect}`);
|
|
@@ -266,6 +315,20 @@ async function main() {
|
|
|
266
315
|
return;
|
|
267
316
|
}
|
|
268
317
|
|
|
318
|
+
if (args.command === "open") {
|
|
319
|
+
const server = await findRunningLocalDevServer(process.cwd());
|
|
320
|
+
|
|
321
|
+
if (!server) {
|
|
322
|
+
console.log("No running AgentKit dev server found for this capsule.");
|
|
323
|
+
console.log("Run: npm run dev");
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
await openLocalUrl(server.url);
|
|
328
|
+
console.log(`Opened ${server.url}`);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
269
332
|
if (args.command === "chat-ui") {
|
|
270
333
|
if (!args.flags.deploy) {
|
|
271
334
|
throw new Error("Usage: agentkit chat-ui --deploy [--port <number>] [--token-file <path>]");
|
|
@@ -290,12 +353,17 @@ async function main() {
|
|
|
290
353
|
|
|
291
354
|
if (args.command === "chat") {
|
|
292
355
|
const message = args.flags.message;
|
|
356
|
+
const conversationId = parseOptionalStringFlag(
|
|
357
|
+
args.flags["conversation-id"] ?? args.flags.conversation,
|
|
358
|
+
"conversation id",
|
|
359
|
+
'agentkit chat --message "hello" [--conversation-id <id>]',
|
|
360
|
+
);
|
|
293
361
|
|
|
294
362
|
if (typeof message !== "string") {
|
|
295
|
-
throw new Error('Missing message. Usage: agentkit chat --message "hello"');
|
|
363
|
+
throw new Error('Missing message. Usage: agentkit chat --message "hello" [--conversation-id <id>]');
|
|
296
364
|
}
|
|
297
365
|
|
|
298
|
-
const result = await runAgentMessageFromCwd(process.cwd(), { message });
|
|
366
|
+
const result = await runAgentMessageFromCwd(process.cwd(), { message, conversationId });
|
|
299
367
|
console.log(result.message.content);
|
|
300
368
|
return;
|
|
301
369
|
}
|
|
@@ -485,6 +553,20 @@ async function main() {
|
|
|
485
553
|
if (args.command === "deploy") {
|
|
486
554
|
const [deploySubcommand] = args.positional;
|
|
487
555
|
|
|
556
|
+
if (deploySubcommand === "smoke") {
|
|
557
|
+
const message =
|
|
558
|
+
parseOptionalStringFlag(args.flags.message ?? args.flags.smoke, "message", 'agentkit deploy smoke --message "hello"') ??
|
|
559
|
+
"hello";
|
|
560
|
+
const state = await requireLocalDeployState();
|
|
561
|
+
const token = await readDeployAccessTokenForSmoke(process.cwd(), state);
|
|
562
|
+
await runDeploySmoke({
|
|
563
|
+
deployUrl: requireDeployUrl(state),
|
|
564
|
+
message,
|
|
565
|
+
token,
|
|
566
|
+
});
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
|
|
488
570
|
if (deploySubcommand === "doctor") {
|
|
489
571
|
const report = await checkDeployReadiness({
|
|
490
572
|
apiUrl: await resolveCloudApiUrl(args.flags.api),
|
|
@@ -520,6 +602,7 @@ async function main() {
|
|
|
520
602
|
|
|
521
603
|
const target = resolveDeployTarget(parseTargetFlag(args.flags.target, "deploy"));
|
|
522
604
|
const anonymous = Boolean(args.flags.anonymous);
|
|
605
|
+
const smokeMessage = parseOptionalStringFlag(args.flags.smoke, "smoke", 'agentkit deploy --smoke "hello"');
|
|
523
606
|
|
|
524
607
|
if (target.id === "vps") {
|
|
525
608
|
const host = parseRequiredStringFlag(args.flags.host, "host", "agentkit deploy --target vps --host <host>");
|
|
@@ -592,6 +675,24 @@ async function main() {
|
|
|
592
675
|
console.log(`Chat auth: could not create local chat token. ${formatReadinessError(chatAccess.error)}`);
|
|
593
676
|
console.log(`Run: agentkit access token create ${defaultDeployChatAccessTokenName} --out ${defaultDeployChatAccessTokenPath}`);
|
|
594
677
|
}
|
|
678
|
+
|
|
679
|
+
if (smokeMessage) {
|
|
680
|
+
if (chatAccess?.status === "failed") {
|
|
681
|
+
throw new AgentKitError("deploy_smoke_failed", "Could not run deploy smoke because chat access token creation failed.");
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
await runDeploySmoke({
|
|
685
|
+
deployUrl: requireDeployUrl({
|
|
686
|
+
deploy_id: deploy.deploy.id,
|
|
687
|
+
url: deploy.deploy.url,
|
|
688
|
+
}),
|
|
689
|
+
message: smokeMessage,
|
|
690
|
+
token:
|
|
691
|
+
chatAccess?.status === "ready"
|
|
692
|
+
? await readDeployAccessTokenForUi(chatAccess.path)
|
|
693
|
+
: undefined,
|
|
694
|
+
});
|
|
695
|
+
}
|
|
595
696
|
return;
|
|
596
697
|
}
|
|
597
698
|
|
|
@@ -617,6 +718,7 @@ async function main() {
|
|
|
617
718
|
fromLocalEnvFlag: args.flags["from-local-env"],
|
|
618
719
|
localEnvRoot: args.flags["from-local-env"] !== undefined ? await findAgentCapsuleRoot(process.cwd()) : undefined,
|
|
619
720
|
usage: "agentkit secret set <NAME> <VALUE> | agentkit secret set <NAME> --stdin | agentkit secret set <NAME> --from-env [ENV_NAME] | agentkit secret set <NAME> --from-local-env [--api <url>]",
|
|
721
|
+
inlineSensitiveHelp: "Prefer --stdin, --from-env, or --from-local-env so the value does not enter shell history.",
|
|
620
722
|
});
|
|
621
723
|
|
|
622
724
|
if (!name) {
|
|
@@ -631,6 +733,15 @@ async function main() {
|
|
|
631
733
|
return;
|
|
632
734
|
}
|
|
633
735
|
|
|
736
|
+
if (subcommand === "sync") {
|
|
737
|
+
if (args.flags["from-local"] !== true) {
|
|
738
|
+
throw new Error("Usage: agentkit secret sync --from-local [--api <url>]");
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
await syncHostedSecretsFromLocal({ apiUrl, projectId });
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
634
745
|
if (subcommand === "unset") {
|
|
635
746
|
if (!name) {
|
|
636
747
|
throw new Error("Usage: agentkit secret unset <NAME> [--api <url>]");
|
|
@@ -643,7 +754,7 @@ async function main() {
|
|
|
643
754
|
return;
|
|
644
755
|
}
|
|
645
756
|
|
|
646
|
-
throw new Error("Usage: agentkit secret set <NAME> <VALUE> | agentkit secret set <NAME> --stdin | agentkit secret set <NAME> --from-env [ENV_NAME] | agentkit secret set <NAME> --from-local-env | agentkit secret list | agentkit secret unset <NAME>");
|
|
757
|
+
throw new Error("Usage: agentkit secret set <NAME> <VALUE> | agentkit secret set <NAME> --stdin | agentkit secret set <NAME> --from-env [ENV_NAME] | agentkit secret set <NAME> --from-local-env | agentkit secret sync --from-local | agentkit secret list | agentkit secret unset <NAME>");
|
|
647
758
|
}
|
|
648
759
|
|
|
649
760
|
if (args.command === "access") {
|
|
@@ -718,6 +829,7 @@ async function main() {
|
|
|
718
829
|
fromEnvFlag: args.flags["from-env"],
|
|
719
830
|
fromLocalEnvFlag: undefined,
|
|
720
831
|
usage: "agentkit env set <NAME> <VALUE> | agentkit env set <NAME> --stdin | agentkit env set <NAME> --from-env [ENV_NAME]",
|
|
832
|
+
inlineSensitiveHelp: "Prefer --stdin or --from-env so the value does not enter shell history.",
|
|
721
833
|
});
|
|
722
834
|
|
|
723
835
|
if (!name) {
|
|
@@ -803,9 +915,12 @@ Rules:
|
|
|
803
915
|
- Follow the AgentKit contract in llms-full.txt.
|
|
804
916
|
- Treat the owner's request as the brief.
|
|
805
917
|
- Build or update the agent inside this capsule as far as you can without asking for obvious restatements.
|
|
918
|
+
- Do not wait for a wizard or recipe; implement directly in the capsule files.
|
|
806
919
|
- Edit agent behavior in prompts/instructions.md.
|
|
807
920
|
- Edit the agent contract in agentkit.config.ts.
|
|
808
921
|
- Add TypeScript tools under tools/.
|
|
922
|
+
- Keep the first useful version runnable with test/fake unless the owner has already chosen a real provider.
|
|
923
|
+
- Before testing real conversation behavior, ask the owner to choose OpenRouter, OpenAI, Anthropic, or another supported provider. Do not choose for them.
|
|
809
924
|
- Keep local secrets in .env only.
|
|
810
925
|
- Do not commit or print secret values.
|
|
811
926
|
- Do not edit .agentkit/ directly.
|
|
@@ -827,10 +942,16 @@ If you add a tool, also run a tool smoke test with the fake provider, for exampl
|
|
|
827
942
|
npm run agentkit -- tool tool_name --input '{}'
|
|
828
943
|
\`\`\`
|
|
829
944
|
|
|
945
|
+
UI testing:
|
|
946
|
+
- For local UI testing, run \`npm run dev\`, open the printed \`Chat:\` URL, and tell the owner the exact URL.
|
|
947
|
+
- For hosted UI testing after deploy, run \`npm run agentkit -- chat-ui --deploy\`, open the printed \`Chat:\` URL, and tell the owner it is connected to the hosted deploy.
|
|
948
|
+
- If the provider is still \`test/fake\`, say the UI was tested only with the deterministic fake provider.
|
|
949
|
+
|
|
830
950
|
Final response:
|
|
831
951
|
- Summarize what changed.
|
|
832
952
|
- List files changed.
|
|
833
953
|
- Paste the verification commands and results.
|
|
954
|
+
- State whether testing used \`test/fake\` or a real provider selected by the owner.
|
|
834
955
|
- Call out any missing user details or blocked checks.
|
|
835
956
|
`;
|
|
836
957
|
}
|
|
@@ -1142,6 +1263,38 @@ function secretSetCommand(secret: string, source: DeployReadinessContext["localS
|
|
|
1142
1263
|
return `agentkit secret set ${secret} --stdin`;
|
|
1143
1264
|
}
|
|
1144
1265
|
|
|
1266
|
+
async function syncHostedSecretsFromLocal(options: { apiUrl: string; projectId: string }): Promise<void> {
|
|
1267
|
+
const context = await loadDeployReadinessContext(process.cwd(), process.env);
|
|
1268
|
+
const localEnv = await loadCapsuleEnv(context.root, {});
|
|
1269
|
+
let synced = 0;
|
|
1270
|
+
let skipped = 0;
|
|
1271
|
+
|
|
1272
|
+
for (const secret of context.userManagedSecrets) {
|
|
1273
|
+
const value = localEnv[secret];
|
|
1274
|
+
|
|
1275
|
+
if (value === undefined) {
|
|
1276
|
+
skipped += 1;
|
|
1277
|
+
console.log(`${secret}\tskipped_missing_local`);
|
|
1278
|
+
continue;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
await cloudApiRequest(options.apiUrl, `/v1/projects/${encodeURIComponent(options.projectId)}/secrets/${encodeURIComponent(secret)}`, {
|
|
1282
|
+
method: "PUT",
|
|
1283
|
+
body: JSON.stringify({ value }),
|
|
1284
|
+
});
|
|
1285
|
+
synced += 1;
|
|
1286
|
+
console.log(`${secret}\tsynced`);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
if (context.userManagedSecrets.length === 0) {
|
|
1290
|
+
console.log("No user-managed hosted secrets are declared by this capsule.");
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
console.log("");
|
|
1295
|
+
console.log(`${synced} synced, ${skipped} skipped`);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1145
1298
|
function normalizeOptionalNumber(value: number | null | undefined): number | undefined {
|
|
1146
1299
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
1147
1300
|
}
|
|
@@ -1158,6 +1311,10 @@ function isString(value: unknown): value is string {
|
|
|
1158
1311
|
return typeof value === "string";
|
|
1159
1312
|
}
|
|
1160
1313
|
|
|
1314
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
1315
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1161
1318
|
async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
1162
1319
|
const [subcommand, first, second, third] = args.positional;
|
|
1163
1320
|
|
|
@@ -1756,6 +1913,65 @@ async function requireLocalDeployState(): Promise<LocalDeployState> {
|
|
|
1756
1913
|
return state;
|
|
1757
1914
|
}
|
|
1758
1915
|
|
|
1916
|
+
function requireDeployUrl(state: Pick<LocalDeployState, "deploy_id" | "url">): string {
|
|
1917
|
+
if (!state.url) {
|
|
1918
|
+
throw new Error(`Deploy ${state.deploy_id} does not have a URL. Run agentkit deploy again.`);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
return state.url;
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
async function readDeployAccessTokenForSmoke(cwd: string, state: LocalDeployState): Promise<string | undefined> {
|
|
1925
|
+
if (state.access?.mode === "public") {
|
|
1926
|
+
return undefined;
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
const root = await findAgentCapsuleRoot(cwd);
|
|
1930
|
+
return readDeployAccessTokenForUi(join(root, defaultDeployChatAccessTokenPath));
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
async function runDeploySmoke(input: { deployUrl: string; message: string; token?: string }): Promise<void> {
|
|
1934
|
+
const headers = new Headers({ "Content-Type": "application/json" });
|
|
1935
|
+
|
|
1936
|
+
if (input.token) {
|
|
1937
|
+
headers.set("Authorization", `Bearer ${input.token}`);
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
let response: Response;
|
|
1941
|
+
|
|
1942
|
+
try {
|
|
1943
|
+
response = await fetch(new URL("/v1/chat", input.deployUrl).href, {
|
|
1944
|
+
method: "POST",
|
|
1945
|
+
headers,
|
|
1946
|
+
body: JSON.stringify({ message: input.message }),
|
|
1947
|
+
});
|
|
1948
|
+
} catch (error) {
|
|
1949
|
+
throw new AgentKitError(
|
|
1950
|
+
"deploy_smoke_failed",
|
|
1951
|
+
`Deploy smoke request failed before a response: ${error instanceof Error ? error.message : String(error)}`,
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
const payload = await response.json().catch(() => null);
|
|
1956
|
+
|
|
1957
|
+
if (!response.ok) {
|
|
1958
|
+
throw new AgentKitError("deploy_smoke_failed", readCloudError(payload, response.status));
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
const content =
|
|
1962
|
+
payload && typeof payload === "object" && "message" in payload && isRecord((payload as { message?: unknown }).message)
|
|
1963
|
+
? (payload as { message: { content?: unknown } }).message.content
|
|
1964
|
+
: undefined;
|
|
1965
|
+
|
|
1966
|
+
console.log("");
|
|
1967
|
+
console.log("Deploy smoke verified");
|
|
1968
|
+
console.log(`URL: ${input.deployUrl}`);
|
|
1969
|
+
console.log(`Message: ${input.message}`);
|
|
1970
|
+
if (typeof content === "string") {
|
|
1971
|
+
console.log(`Response: ${content}`);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1759
1975
|
async function resolveProjectIdForSecretCommand(cwd: string): Promise<string> {
|
|
1760
1976
|
const state = await readLocalDeployStateIfExists(cwd);
|
|
1761
1977
|
|
|
@@ -1951,8 +2167,9 @@ async function resolveSecretInputValue(options: {
|
|
|
1951
2167
|
fromLocalEnvFlag: string | boolean | undefined;
|
|
1952
2168
|
localEnvRoot?: string;
|
|
1953
2169
|
usage: string;
|
|
2170
|
+
inlineSensitiveHelp?: string;
|
|
1954
2171
|
}): Promise<string> {
|
|
1955
|
-
const { name, valueParts, stdinFlag, fromEnvFlag, fromLocalEnvFlag, localEnvRoot, usage } = options;
|
|
2172
|
+
const { name, valueParts, stdinFlag, fromEnvFlag, fromLocalEnvFlag, localEnvRoot, usage, inlineSensitiveHelp } = options;
|
|
1956
2173
|
|
|
1957
2174
|
if (!name) {
|
|
1958
2175
|
throw new Error(`Missing name. Usage: ${usage}`);
|
|
@@ -1972,6 +2189,11 @@ async function resolveSecretInputValue(options: {
|
|
|
1972
2189
|
|
|
1973
2190
|
if (hasInlineValue) {
|
|
1974
2191
|
value = valueParts.join(" ");
|
|
2192
|
+
if (looksLikeSecretName(name)) {
|
|
2193
|
+
console.error(
|
|
2194
|
+
`Warning: ${name} looks sensitive. ${inlineSensitiveHelp ?? "Prefer --stdin or --from-env so the value does not enter shell history."}`,
|
|
2195
|
+
);
|
|
2196
|
+
}
|
|
1975
2197
|
} else if (hasStdin) {
|
|
1976
2198
|
if (stdinFlag !== true) {
|
|
1977
2199
|
throw new Error(`--stdin does not take a value. Usage: ${usage}`);
|
|
@@ -2013,6 +2235,10 @@ async function resolveSecretInputValue(options: {
|
|
|
2013
2235
|
return value;
|
|
2014
2236
|
}
|
|
2015
2237
|
|
|
2238
|
+
function looksLikeSecretName(name: string): boolean {
|
|
2239
|
+
return /(?:SECRET|TOKEN|KEY|PASSWORD|CREDENTIAL|PRIVATE)/i.test(name);
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2016
2242
|
async function readStdinText(): Promise<string> {
|
|
2017
2243
|
const chunks: Buffer[] = [];
|
|
2018
2244
|
|
|
@@ -2147,6 +2373,84 @@ function parseJsonInput(source: string, label: string): unknown {
|
|
|
2147
2373
|
}
|
|
2148
2374
|
}
|
|
2149
2375
|
|
|
2376
|
+
async function findRunningLocalDevServer(cwd: string): Promise<{ url: string; agent: string } | null> {
|
|
2377
|
+
const capsule = await loadAgentCapsule(cwd);
|
|
2378
|
+
const basePort = localDevBasePort();
|
|
2379
|
+
|
|
2380
|
+
for (let port = basePort; port <= basePort + 50; port += 1) {
|
|
2381
|
+
const state = await fetchLocalAgentKitState(`http://localhost:${port}/_agentkit`);
|
|
2382
|
+
|
|
2383
|
+
if (state?.agent === capsule.config.name) {
|
|
2384
|
+
return {
|
|
2385
|
+
url: `http://localhost:${port}`,
|
|
2386
|
+
agent: state.agent,
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
return null;
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
function localDevBasePort(): number {
|
|
2395
|
+
const value = process.env.AGENTKIT_DEV_BASE_PORT;
|
|
2396
|
+
|
|
2397
|
+
if (!value) {
|
|
2398
|
+
return defaultAgentKitDevPort;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
const parsed = Number(value);
|
|
2402
|
+
return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : defaultAgentKitDevPort;
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
async function fetchLocalAgentKitState(url: string): Promise<{ agent?: string } | null> {
|
|
2406
|
+
const controller = new AbortController();
|
|
2407
|
+
const timeout = setTimeout(() => controller.abort(), 300);
|
|
2408
|
+
|
|
2409
|
+
try {
|
|
2410
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
2411
|
+
const payload = await response.json().catch(() => null);
|
|
2412
|
+
|
|
2413
|
+
if (payload && typeof payload === "object") {
|
|
2414
|
+
return {
|
|
2415
|
+
agent: typeof (payload as { agent?: unknown }).agent === "string" ? (payload as { agent: string }).agent : undefined,
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
} catch {
|
|
2419
|
+
return null;
|
|
2420
|
+
} finally {
|
|
2421
|
+
clearTimeout(timeout);
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
return null;
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
async function openLocalUrl(url: string): Promise<void> {
|
|
2428
|
+
if (process.env.AGENTKIT_OPEN_COMMAND === "none") {
|
|
2429
|
+
return;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
const command =
|
|
2433
|
+
process.env.AGENTKIT_OPEN_COMMAND ??
|
|
2434
|
+
(process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open");
|
|
2435
|
+
const args = process.env.AGENTKIT_OPEN_COMMAND
|
|
2436
|
+
? [url]
|
|
2437
|
+
: process.platform === "win32"
|
|
2438
|
+
? ["/c", "start", "", url]
|
|
2439
|
+
: [url];
|
|
2440
|
+
|
|
2441
|
+
await new Promise<void>((resolvePromise, reject) => {
|
|
2442
|
+
const child = spawn(command, args, {
|
|
2443
|
+
detached: true,
|
|
2444
|
+
stdio: "ignore",
|
|
2445
|
+
});
|
|
2446
|
+
child.once("error", reject);
|
|
2447
|
+
child.once("spawn", () => {
|
|
2448
|
+
child.unref();
|
|
2449
|
+
resolvePromise();
|
|
2450
|
+
});
|
|
2451
|
+
});
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2150
2454
|
async function startDeployChatUiServer(
|
|
2151
2455
|
cwd: string,
|
|
2152
2456
|
options: { port: number; tokenFile?: string },
|
package/src/providers/test.ts
CHANGED
|
@@ -37,6 +37,17 @@ export const testProviderAdapter: ProviderAdapter = {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
const rememberedName = answerRememberedName(input.messages);
|
|
41
|
+
|
|
42
|
+
if (rememberedName) {
|
|
43
|
+
return {
|
|
44
|
+
message: {
|
|
45
|
+
role: "assistant",
|
|
46
|
+
content: rememberedName,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
40
51
|
return {
|
|
41
52
|
message: {
|
|
42
53
|
role: "assistant",
|
|
@@ -58,6 +69,46 @@ function findLastUserMessage(messages: AgentMessage[]): AgentMessage | undefined
|
|
|
58
69
|
return undefined;
|
|
59
70
|
}
|
|
60
71
|
|
|
72
|
+
function answerRememberedName(messages: AgentMessage[]): string | null {
|
|
73
|
+
const lastUserMessage = findLastUserMessage(messages);
|
|
74
|
+
|
|
75
|
+
if (!lastUserMessage || !asksForName(lastUserMessage.content)) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (let index = messages.length - 2; index >= 0; index -= 1) {
|
|
80
|
+
const message = messages[index];
|
|
81
|
+
|
|
82
|
+
if (message.role !== "user") {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const name = extractName(message.content);
|
|
87
|
+
|
|
88
|
+
if (name) {
|
|
89
|
+
return /meu nome|qual\s+(é|e)\s*meu nome|como\s+(é|e)\s*meu nome/i.test(lastUserMessage.content)
|
|
90
|
+
? `Seu nome é ${name}.`
|
|
91
|
+
: `Your name is ${name}.`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function asksForName(content: string): boolean {
|
|
99
|
+
return /what(?:'s| is) my name\??/i.test(content) || /(como|qual)\s+(é|e)\s*(o\s+)?meu nome\??/i.test(content);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function extractName(content: string): string | null {
|
|
103
|
+
const match = content.match(/\bmy name is\s+(.+)$/i) ?? content.match(/\bmeu nome (?:é|e)\s+(.+)$/i);
|
|
104
|
+
|
|
105
|
+
if (!match) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return match[1].trim().replace(/[.!?]+$/g, "") || null;
|
|
110
|
+
}
|
|
111
|
+
|
|
61
112
|
function parseToolRequest(content: string): { name: string; input?: unknown } | null {
|
|
62
113
|
let parsed: unknown;
|
|
63
114
|
|