@andreprado/agentkit 0.1.0-alpha.21 → 0.1.0-alpha.22

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.
@@ -79,7 +79,7 @@ Expected readiness:
79
79
 
80
80
  ## Connect Apps
81
81
 
82
- After deploy, create a hosted Composio Connect Link:
82
+ The Composio connect link is deploy-scoped, so declare `composioManaged(...)` first, deploy, then connect. After deploy, create a hosted Composio Connect Link:
83
83
 
84
84
  ```sh
85
85
  agentkit integrations connect composio --toolkit gmail
@@ -90,6 +90,8 @@ AgentKit prints a URL. Send that URL to the person who owns the app account.
90
90
 
91
91
  If the deploy has only one toolkit, `--toolkit` can be omitted.
92
92
 
93
+ `agentkit deploy` prints the recommended `integrations connect composio --toolkit <slug>` command for each configured toolkit in its production handoff.
94
+
93
95
  ## Calendar Defaults
94
96
 
95
97
  For Google Calendar agents, include read and write actions together. Do not expose only `GOOGLECALENDAR_CREATE_EVENT`; users naturally ask to see availability before booking.
@@ -112,6 +112,8 @@ Direct tool test:
112
112
  npm run agentkit -- tool lookup_order --input '{"orderId":"A100"}'
113
113
  ```
114
114
 
115
+ When the tool touches live data, writes externally, or enforces business rules, also add deterministic checks. Use fixtures, eval-safe branches, seed data, or direct tool inputs for success and failure paths such as missing confirmation, validation errors, provider 429s/timeouts, unavailable records, and privacy/no-leak behavior. Add an eval when the agent should call the tool with specific inputs or refuse to call it until required intake or confirmation is complete.
116
+
115
117
  Expected output:
116
118
 
117
119
  ```json
@@ -129,9 +131,10 @@ Use this when a tool needs agent-owned tables and must work locally and after de
129
131
 
130
132
  Recommended contract:
131
133
 
132
- - Put all agent-owned tables in `schema.sql`.
134
+ - Put the idempotent bootstrap view of agent-owned tables in `schema.sql`.
135
+ - For production-shaped schema evolution, add ordered `migrations/*.sql` files such as `migrations/0001_clients.sql`.
133
136
  - Keep deploy-ready capsules on `storage.driver: "agentkit"`.
134
- - Local `agentkit chat`, `agentkit tool`, `agentkit dev`, and eval runs apply `schema.sql` to local development storage.
137
+ - Local `agentkit chat`, `agentkit tool`, `agentkit dev`, and eval runs apply ordered local migrations before `schema.sql`.
135
138
  - Use `agentkit db migrate`, `agentkit db reset --yes`, `agentkit db seed [--file seed.sql]`, and `agentkit db shell` for local database setup and inspection.
136
139
  - Hosted deploy migrates/provisions the managed database internally and applies the same `schema.sql`.
137
140
  - Tools use `ctx.db` as the canonical helper. `ctx.database` and `ctx.storage.sql` are supported aliases.
@@ -176,7 +179,7 @@ CREATE TABLE IF NOT EXISTS appointments (
176
179
  );
177
180
  ```
178
181
 
179
- `schema.sql` is an idempotent bootstrap file in v1. Prefer `CREATE TABLE IF NOT EXISTS`, `CREATE INDEX IF NOT EXISTS`, and safe additive `ALTER TABLE` statements. AgentKit does not run destructive schema changes or ordered `migrations/*.sql` automatically yet.
182
+ `schema.sql` is an idempotent bootstrap file. Prefer `CREATE TABLE IF NOT EXISTS`, `CREATE INDEX IF NOT EXISTS`, and safe additive `ALTER TABLE` statements. Use ordered `migrations/*.sql` for production-shaped schema evolution; `agentkit db migrate` applies unapplied local migrations before `schema.sql`.
180
183
 
181
184
  `tools/schedule-appointment.ts`:
182
185
 
@@ -89,7 +89,7 @@ Runtime files created after chat:
89
89
 
90
90
  ## Handoff To A Coding Agent
91
91
 
92
- The owner does not need to fill a separate brief file. The natural-language request they type into Codex, Claude Code, or another coding agent is the brief.
92
+ The owner does not need to run a brief wizard or fill a separate brief file. The natural-language request they type into Codex, Claude Code, or another coding agent is the brief.
93
93
 
94
94
  Primary flow:
95
95
 
@@ -97,9 +97,9 @@ Primary flow:
97
97
  Develop an appointment and intake agent for an ophthalmology office.
98
98
  ```
99
99
 
100
- The generated `AGENTS.md`, `AGENTKIT.md`, `CLAUDE.md`, and `skills/` pack tell the coding agent which files to edit, which task skill to load, and which verification commands to run. There is no wizard or recipe layer: the coding agent edits the capsule directly from the scaffold, contract, and owner request. The default router is `skills/agentkit-capsule/SKILL.md`; `llms-full.txt` is reserved for complete-contract checks.
100
+ The generated `AGENTS.md`, `AGENTKIT.md`, `CLAUDE.md`, and `skills/` pack tell the coding agent which files to edit, which task skill to load, and which verification commands to run. There is no AgentKit CLI wizard in the normal flow: the coding agent edits the capsule directly from the scaffold, contract, and owner request. The default router is `skills/agentkit-capsule/SKILL.md`; `llms-full.txt` is reserved for complete-contract checks.
101
101
 
102
- After the owner gives the general idea, the coding agent should create or update the implementation contract itself:
102
+ After the owner gives the general idea inside the coding-agent chat, the coding agent should create or update the implementation contract itself:
103
103
 
104
104
  ```sh
105
105
  npm run agentkit -- spec init --brief "Develop an appointment and intake agent for an ophthalmology office."
@@ -108,6 +108,8 @@ npm run agentkit -- spec check
108
108
 
109
109
  `AGENT_SPEC.md` is an internal working contract for the coding agent. It is not a form the owner must fill before work starts.
110
110
 
111
+ The coding agent should build a testable capsule, not only a prompt. For each meaningful requirement in the brief or `AGENT_SPEC.md`, decide whether it needs a prompt instruction, tool, schema/migration, fixture, eval, direct tool check, or deploy/readiness check. Privacy, confirmation, external writes, bookings, customer data, business hours, dates, and integration failures should have evals or deterministic checks before the capsule is called done.
112
+
111
113
  Optional shortcut when copying a prompt into another coding agent:
112
114
 
113
115
  ```sh
@@ -176,7 +178,7 @@ npm run agentkit -- chat-ui --deploy
176
178
 
177
179
  Open the printed `Chat:` URL and tell the owner this local UI is connected to the hosted deploy.
178
180
 
179
- `test/fake` is deterministic. It validates the scaffold, direct tool checks, and fake-provider evals, but it does not validate natural conversation quality. Before claiming real conversation behavior is tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, or another supported provider.
181
+ `test/fake` is deterministic. It validates the scaffold, direct tool checks, and fake-provider evals, but it does not validate natural conversation quality. Before claiming real conversation behavior is tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, OpenCode Zen, OpenCode Go, or another supported provider.
180
182
 
181
183
  ## Safety Rules
182
184
 
@@ -154,7 +154,7 @@ npm run agentkit -- chat-ui --deploy
154
154
  npm run agentkit -- access token list
155
155
  ```
156
156
 
157
- For hosted deploys, `npm run agentkit -- deploy` writes the local chat/UI access token to `.agentkit/chat-access-token.json`. Use `npm run agentkit -- deploy --smoke "hello"` for the official hosted chat smoke, and use `npm run agentkit -- chat-ui --deploy` for hosted UI testing. The hosted Chat UI shows the conversation id, tool calls, tool errors, and a new-conversation control; use `npm run agentkit -- conversations trace <conversation-id> --deploy` to pull the hosted trace from the last deploy. Use `npm run agentkit -- access token create <name> --out <path>` only for additional clients.
157
+ For hosted deploys, `npm run agentkit -- deploy` writes the local chat/UI access token to `.agentkit/chat-access-token.json` and prints a production handoff with the deploy URL, UI command, secret status, database/schema artifact, integration connect commands, smoke status, and next recommended command. Use `npm run agentkit -- deploy --smoke "hello"` for the official hosted chat smoke, and use `npm run agentkit -- chat-ui --deploy` for hosted UI testing. The hosted Chat UI shows the conversation id, tool calls, tool errors, and a new-conversation control; use `npm run agentkit -- conversations trace <conversation-id> --deploy` to pull the hosted trace from the last deploy. Use `npm run agentkit -- access token create <name> --out <path>` only for additional clients.
158
158
 
159
159
  When the UI is running, open the printed `Chat:` URL and tell the owner the exact URL. If the capsule is still on `test/fake`, say the UI was tested only with the deterministic fake provider.
160
160
 
@@ -22,6 +22,8 @@ Run evals:
22
22
  agentkit eval run
23
23
  ```
24
24
 
25
+ `agentkit eval run` uses temporary local SQLite storage for eval execution. This keeps eval conversations and tool calls isolated from `.agentkit/agentkit.db`, so evals can run while a local chat or dev server is using the normal development database.
26
+
25
27
  If the capsule uses npm scripts and Windows PowerShell blocks `npm.ps1`, use:
26
28
 
27
29
  ```sh
@@ -54,6 +56,8 @@ Create or edit:
54
56
  evals/<name>.eval.ts
55
57
  ```
56
58
 
59
+ Create evals proactively from the agent brief and `AGENT_SPEC.md`. High-value evals cover identity and scope, required intake fields, confirmation before writes, no-leak/privacy rules, timezone and business-hour behavior, default durations or limits, tool-call payloads, unavailable slots, empty results, missing auth, rate limits, and timeouts. If a real conversation reveals a bug, add the smallest regression eval that would have failed before the fix.
60
+
57
61
  Use conversations as source material:
58
62
 
59
63
  ```txt
@@ -152,7 +156,7 @@ export default defineEval({
152
156
  });
153
157
  ```
154
158
 
155
- `tools.persisted` validates the tool call saved in local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `rendered`, `status`, and/or `visibility`.
159
+ `tools.persisted` validates the tool call saved in the eval run's local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `rendered`, `status`, and/or `visibility`.
156
160
 
157
161
  Use `tools.count` for the exact number of persisted calls in that turn, `tools.calledOnce` for exactly one call by name, and `tools.order` for required relative order. `tools.order` allows extra calls before, between, or after the named calls; pair it with `tools.count` when the exact call set matters.
158
162
 
@@ -6,7 +6,7 @@ Switch a capsule from the offline `test/fake` provider to a Pi-backed provider.
6
6
 
7
7
  ## When To Use This
8
8
 
9
- Use this when local fake responses are no longer enough and the agent needs model behavior from OpenRouter, OpenAI, Anthropic, or another supported provider.
9
+ Use this when local fake responses are no longer enough and the agent needs model behavior from OpenRouter, OpenAI, Anthropic, OpenCode Zen, OpenCode Go, or another supported provider.
10
10
 
11
11
  The coding agent should not choose a real provider automatically. Ask the owner which provider to use, then update the capsule.
12
12
 
@@ -87,6 +87,30 @@ secrets: ["OPENROUTER_API_KEY"],
87
87
 
88
88
  Prefer model ids or aliases listed by the installed Pi SDK when available, such as `~google/gemini-flash-latest`. If an OpenRouter model id is newer than the Pi model registry, AgentKit passes the id through to OpenRouter using Pi's OpenAI-compatible transport with conservative unknown-model metadata. The provider may still reject the request if the id is invalid, inaccessible, or does not support the tools/features the agent uses.
89
89
 
90
+ OpenCode Zen:
91
+
92
+ ```ts
93
+ provider: {
94
+ name: "opencode",
95
+ model: "big-pickle",
96
+ },
97
+ secrets: ["OPENCODE_API_KEY"],
98
+ ```
99
+
100
+ Use an OpenCode Zen model id listed by the installed Pi SDK, such as `big-pickle`, `deepseek-v4-flash-free`, `claude-sonnet-4-5`, or `gpt-5.4-mini`. OpenCode Zen uses `OPENCODE_API_KEY`.
101
+
102
+ OpenCode Go:
103
+
104
+ ```ts
105
+ provider: {
106
+ name: "opencode-go",
107
+ model: "deepseek-v4-flash",
108
+ },
109
+ secrets: ["OPENCODE_API_KEY"],
110
+ ```
111
+
112
+ Use an OpenCode Go model id listed by the installed Pi SDK, such as `deepseek-v4-flash`, `deepseek-v4-pro`, `glm-5.1`, `kimi-k2.6`, `minimax-m2.7`, or `qwen3.6-plus`. OpenCode Go also uses `OPENCODE_API_KEY`.
113
+
90
114
  ## UI Verification
91
115
 
92
116
  After the provider is configured and the local secret is set, test through chat and UI:
@@ -135,7 +159,7 @@ npm run agentkit -- inspect
135
159
 
136
160
  `provider_model_unsupported`:
137
161
 
138
- For OpenAI or Anthropic, use a model id known to the installed Pi SDK for that provider. For OpenRouter, prefer a known Pi alias when possible; otherwise a raw OpenRouter model id is passed through and any remaining model error comes from OpenRouter.
162
+ For OpenAI, Anthropic, OpenCode Zen, or OpenCode Go, use a model id known to the installed Pi SDK for that provider. For OpenRouter, prefer a known Pi alias when possible; otherwise a raw OpenRouter model id is passed through and any remaining model error comes from OpenRouter.
139
163
 
140
164
  Provider returns auth failure:
141
165
 
@@ -161,7 +161,7 @@ Then open the folder in Codex, Claude Code, or another coding agent and ask dire
161
161
  Develop an appointment and intake agent for an ophthalmology office.
162
162
  ```
163
163
 
164
- The coding agent should infer the first useful version, edit `prompts/instructions.md`, `agentkit.config.ts`, `schema.sql`, `tools/`, and `evals/`, then run the verification commands before finishing. Do not wait for a wizard or recipe. AgentKit provides the scaffold and contract; the coding agent implements directly in the capsule.
164
+ The coding agent should infer the first useful version, edit `prompts/instructions.md`, `agentkit.config.ts`, `schema.sql`, `tools/`, and `evals/`, then run the verification commands before finishing. Do not send the owner back to a CLI brief wizard. AgentKit provides the scaffold, contract, and skills; the coding agent implements directly in the capsule from the owner's chat message.
165
165
 
166
166
  Optional handoff shortcut:
167
167
 
@@ -217,6 +217,8 @@ test
217
217
  openai
218
218
  anthropic
219
219
  openrouter
220
+ opencode
221
+ opencode-go
220
222
  custom
221
223
  ```
222
224
 
@@ -227,6 +229,8 @@ test/fake
227
229
  openai via Pi SDK
228
230
  anthropic via Pi SDK
229
231
  openrouter via Pi SDK
232
+ opencode via Pi SDK
233
+ opencode-go via Pi SDK
230
234
  ```
231
235
 
232
236
  `custom` is a reserved config value. It is not implemented as a local provider adapter yet.
@@ -245,7 +249,7 @@ secrets: [],
245
249
 
246
250
  `test/fake` is deterministic. It is useful for scaffold checks, direct tool checks, and fake-provider evals, but it does not validate natural conversation quality.
247
251
 
248
- Before claiming real conversation behavior has been tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, or another supported provider. Do not choose for them. After the owner chooses, update `agentkit.config.ts`, `.env.schema`, local secrets, hosted secrets if deploying, then rerun chat/UI checks.
252
+ Before claiming real conversation behavior has been tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, OpenCode Zen, OpenCode Go, or another supported provider. Do not choose for them. After the owner chooses, update `agentkit.config.ts`, `.env.schema`, local secrets, hosted secrets if deploying, then rerun chat/UI checks.
249
253
 
250
254
  OpenAI example:
251
255
 
@@ -270,6 +274,30 @@ If a provider key is missing, the runtime returns `secret_not_found`.
270
274
 
271
275
  For OpenRouter, prefer model ids or aliases known to the installed Pi SDK, such as `~google/gemini-flash-latest`. If an OpenRouter id is newer than Pi's registry, AgentKit passes the raw id through to OpenRouter with conservative unknown-model metadata. OpenRouter can still reject invalid, inaccessible, or unsupported models, and unknown-model cost/capability metadata is not authoritative.
272
276
 
277
+ OpenCode Zen example:
278
+
279
+ ```ts
280
+ provider: {
281
+ name: "opencode",
282
+ model: "big-pickle",
283
+ },
284
+ secrets: ["OPENCODE_API_KEY"],
285
+ ```
286
+
287
+ Use an OpenCode Zen model id listed by the installed Pi SDK, such as `big-pickle`, `deepseek-v4-flash-free`, `claude-sonnet-4-5`, or `gpt-5.4-mini`.
288
+
289
+ OpenCode Go example:
290
+
291
+ ```ts
292
+ provider: {
293
+ name: "opencode-go",
294
+ model: "deepseek-v4-flash",
295
+ },
296
+ secrets: ["OPENCODE_API_KEY"],
297
+ ```
298
+
299
+ Use an OpenCode Go model id listed by the installed Pi SDK, such as `deepseek-v4-flash`, `deepseek-v4-pro`, `glm-5.1`, `kimi-k2.6`, `minimax-m2.7`, or `qwen3.6-plus`.
300
+
273
301
  ## Knowledge Contract
274
302
 
275
303
  Knowledge is AgentKit's native retrieval layer for facts the agent should ground in source files. Use it for FAQs, prices, policies, service descriptions, procedures, CSV tables, and reference docs. Do not put secrets, credentials, `.env` contents, or live customer/payment records in Knowledge. Use tools for live or authorization-sensitive data.
@@ -602,7 +630,7 @@ CREATE TABLE IF NOT EXISTS appointments (
602
630
  );
603
631
  ```
604
632
 
605
- `schema.sql` is an idempotent bootstrap file in v1. Use `CREATE TABLE IF NOT EXISTS`, `CREATE INDEX IF NOT EXISTS`, and only safe additive `ALTER TABLE` statements. AgentKit does not automatically run destructive changes or ordered `migrations/*.sql` yet.
633
+ `schema.sql` is an idempotent bootstrap file. Use `CREATE TABLE IF NOT EXISTS`, `CREATE INDEX IF NOT EXISTS`, and only safe additive `ALTER TABLE` statements. Use ordered `migrations/*.sql` for production-shaped schema evolution; `agentkit db migrate` applies unapplied local migrations before `schema.sql`.
606
634
 
607
635
  Example tool:
608
636
 
@@ -831,7 +859,7 @@ tools.order
831
859
  tools.persisted
832
860
  ```
833
861
 
834
- Import `defineEval` from `@andreprado/agentkit` when writing new evals. `tools.persisted` validates the tool call saved in local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `rendered`, `status`, and/or `visibility`. `tool_call` and `persisted_tool_call` remain accepted as backwards-compatible aliases, but new evals should use `tools.persisted`.
862
+ Import `defineEval` from `@andreprado/agentkit` when writing new evals. `tools.persisted` validates the tool call saved in the eval run's local SQLite `tool_calls`, not a provider-specific raw response shape. It can be a tool name string or an object with `name`, `input`, `output`, `rendered`, `status`, and/or `visibility`. `tool_call` and `persisted_tool_call` remain accepted as backwards-compatible aliases, but new evals should use `tools.persisted`.
835
863
 
836
864
  For date-sensitive evals, set top-level `now` to an ISO timestamp with an explicit timezone designator such as `Z` or `-05:00`. AgentKit uses that fixed clock for every turn and tool call in the eval so "today", "tomorrow", and weekdays remain deterministic while normal chat continues to use the real current date.
837
865
 
@@ -939,7 +967,7 @@ agentkit deploy smoke --message "hello"
939
967
  agentkit chat-ui --deploy
940
968
  ```
941
969
 
942
- `agentkit deploy doctor` checks AgentKit Cloud login, hosted deploy entitlement, online deploy capacity, hosted secrets, local `.env` names that still need `agentkit secret set`, managed Composio API/auth-config readiness by toolkit, and private-access runtime token handling. `agentkit deploy` sends the capsule to AgentKit Cloud, runs the same readiness check automatically before building and uploading, updates the current project deploy slot by default, and writes the local chat/UI deploy access token to `.agentkit/chat-access-token.json` for hosted deploys. `agentkit deploy --smoke "hello"` deploys and then tests `/v1/chat` with the deploy access token. `agentkit deploy smoke --message "hello"` repeats that smoke against the last local deploy. `agentkit chat-ui --deploy` serves a local UI pointed at the hosted deploy using that token without exposing it to browser code, shows the conversation id and tool calls, and supports starting a new conversation. Use `agentkit conversations trace <conversation-id> --deploy` to pull hosted conversation messages and tool calls from the last deploy. Production deploys require an account with `cloudflare_deploy_alpha` or purchased/manual deploy slots; local commands and dry-run builds do not require login. AgentKit owns infrastructure selection, backend migration, managed secrets, and public URL creation.
970
+ `agentkit deploy doctor` checks AgentKit Cloud login, hosted deploy entitlement, online deploy capacity, hosted secrets, local `.env` names that still need `agentkit secret set`, managed Composio API/auth-config readiness by toolkit, and private-access runtime token handling. `agentkit deploy` sends the capsule to AgentKit Cloud, runs the same readiness check automatically before building and uploading, updates the current project deploy slot by default, writes the local chat/UI deploy access token to `.agentkit/chat-access-token.json`, and prints a production handoff with URL, UI command, secret status, database/schema artifact, integration connect commands, smoke status, and the next recommended command. `agentkit deploy --smoke "hello"` deploys and then tests `/v1/chat` with the deploy access token. `agentkit deploy smoke --message "hello"` repeats that smoke against the last local deploy. `agentkit chat-ui --deploy` serves a local UI pointed at the hosted deploy using that token without exposing it to browser code, shows the conversation id and tool calls, and supports starting a new conversation. Use `agentkit conversations trace <conversation-id> --deploy` to pull hosted conversation messages and tool calls from the last deploy. Production deploys require an account with `cloudflare_deploy_alpha` or purchased/manual deploy slots; local commands and dry-run builds do not require login. AgentKit owns infrastructure selection, backend migration, managed secrets, and public URL creation.
943
971
 
944
972
  The CLI defaults to the hosted AgentKit Cloud API at `https://agentkit-cloud.aibuilders.com.br`. Use `AGENTKIT_CLOUD_API_URL` or `agentkit deploy --api <url>` only when the owner gives you a non-default AgentKit Cloud API URL.
945
973
 
package/docs/llms.txt CHANGED
@@ -95,16 +95,18 @@ agentkit open
95
95
 
96
96
  On Windows PowerShell, if `npm.ps1` or `npx.ps1` is blocked with `PSSecurityException`, run capsule scripts through the `.cmd` shims, for example `npm.cmd run agentkit -- inspect`, `npm.cmd run agentkit -- knowledge sync`, or `npm.cmd run eval`.
97
97
 
98
- Generated capsules include `AGENTKIT.md`, `AGENTS.md`, and a repo-local `skills/` pack so Codex, Claude Code, or another coding agent can treat the owner's natural-language request as the brief and start building immediately without loading the full contract by default. Start with `skills/agentkit-capsule/SKILL.md`, then load the task skill for the current work. `agentkit handoff codex "Develop an ophthalmology office intake agent"` is an optional prompt-printing shortcut for users who are not already inside a coding-agent workspace. There is no wizard or recipe layer: the coding agent edits the capsule directly from the scaffold and contract.
98
+ Generated capsules include `AGENTKIT.md`, `AGENTS.md`, and a repo-local `skills/` pack so Codex, Claude Code, or another coding agent can treat the owner's natural-language request as the brief and start building immediately without loading the full contract by default. Start with `skills/agentkit-capsule/SKILL.md`, then load the task skill for the current work. `agentkit handoff codex "Develop an ophthalmology office intake agent"` is an optional prompt-printing shortcut for users who are not already inside a coding-agent workspace. There is no AgentKit CLI wizard in the normal flow: the coding agent edits the capsule directly from the scaffold and contract.
99
99
 
100
100
  UI testing is part of the handoff. For local UI testing, run `agentkit dev`, open the printed `Chat:` URL, and tell the owner the exact URL. After hosted deploy, run `agentkit chat-ui --deploy`, open the printed `Chat:` URL, and tell the owner it is connected to the deploy.
101
101
 
102
102
  Hosted Chat UI shows the current conversation id, tool calls, tool errors, and a new-conversation control. Use `agentkit conversations trace <conversation-id> --deploy` to pull the hosted trace from the last deploy.
103
103
 
104
- `test/fake` is deterministic and validates scaffold, direct tool calls, and fake-provider evals. It does not validate natural conversation quality. Before claiming real conversation behavior has been tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, or another supported provider. Do not choose for them.
104
+ `test/fake` is deterministic and validates scaffold, direct tool calls, and fake-provider evals. It does not validate natural conversation quality. Before claiming real conversation behavior has been tested, ask the owner which provider to use: OpenRouter, OpenAI, Anthropic, OpenCode Zen, OpenCode Go, or another supported provider. Do not choose for them.
105
105
 
106
106
  For OpenRouter, prefer model ids or aliases known to the installed Pi SDK, such as `~google/gemini-flash-latest`. If an OpenRouter id is newer than Pi's registry, AgentKit passes the raw id through to OpenRouter with conservative unknown-model metadata; OpenRouter can still reject invalid, inaccessible, or unsupported models.
107
107
 
108
+ For OpenCode Zen, use `provider: { name: "opencode", model: "big-pickle" }`. For OpenCode Go, use `provider: { name: "opencode-go", model: "deepseek-v4-flash" }`. Both use `OPENCODE_API_KEY` in `secrets` and `.env.schema`.
109
+
108
110
  AgentKit injects the current ISO timestamp, local date, weekday, local date/time, and timezone dynamically into every chat run. Set `timeZone` in `agentkit.config.ts` for scheduling agents so "today", "tomorrow", and weekdays resolve in the business/user timezone; otherwise AgentKit falls back to `AGENTKIT_TIME_ZONE`, valid `TZ`, then the runtime default. Do not hardcode today's date in prompts.
109
111
 
110
112
  Current local endpoints from `agentkit dev`:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andreprado/agentkit",
3
- "version": "0.1.0-alpha.21",
3
+ "version": "0.1.0-alpha.22",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,5 +1,5 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
2
+ import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
3
3
  import { join, relative, resolve } from "node:path";
4
4
 
5
5
  import { findAgentCapsuleRoot } from "../runtime/config";
@@ -9,8 +9,15 @@ import { defaultDeployChatAccessTokenName, defaultDeployChatAccessTokenPath } fr
9
9
 
10
10
  export async function startDeployChatUiServer(
11
11
  cwd: string,
12
- options: { port: number; tokenFile?: string },
13
- ): Promise<{ url: string; deployUrl: string; tokenSource: string; stop(): Promise<void> }> {
12
+ options: { port: number; tokenFile?: string; autoPort?: boolean },
13
+ ): Promise<{
14
+ url: string;
15
+ deployUrl: string;
16
+ tokenSource: string;
17
+ port: number;
18
+ portConflict: { port: number } | null;
19
+ stop(): Promise<void>;
20
+ }> {
14
21
  const root = await findAgentCapsuleRoot(cwd);
15
22
  const state = await readLocalDeployStateIfExists(root);
16
23
 
@@ -23,9 +30,37 @@ export async function startDeployChatUiServer(
23
30
  : join(root, defaultDeployChatAccessTokenPath);
24
31
  const hostname = "localhost";
25
32
  const deployUrl = state.url;
33
+ const start = await listenForDeployChatUiServer({
34
+ hostname,
35
+ requestedPort: options.port,
36
+ autoPort: options.autoPort === true,
37
+ create: () => createDeployChatUiHttpServer({ root, tokenPath, hostname, requestedPort: options.port, deployUrl }),
38
+ });
39
+
40
+ return {
41
+ url: `http://${hostname}:${start.port}`,
42
+ deployUrl,
43
+ tokenSource: process.env.AGENTKIT_DEPLOY_ACCESS_TOKEN ? "AGENTKIT_DEPLOY_ACCESS_TOKEN" : relativePath(root, tokenPath),
44
+ port: start.port,
45
+ portConflict: start.portConflict,
46
+ stop: () =>
47
+ new Promise<void>((resolvePromise, reject) => {
48
+ start.server.close((error) => (error ? reject(error) : resolvePromise()));
49
+ }),
50
+ };
51
+ }
52
+
53
+ function createDeployChatUiHttpServer(input: {
54
+ root: string;
55
+ tokenPath: string;
56
+ hostname: string;
57
+ requestedPort: number;
58
+ deployUrl: string;
59
+ }): Server {
60
+ const { root, tokenPath, hostname, requestedPort, deployUrl } = input;
26
61
  const server = createServer(async (request, response) => {
27
62
  try {
28
- const requestUrl = new URL(request.url ?? "/", `http://${request.headers.host ?? `${hostname}:${options.port}`}`);
63
+ const requestUrl = new URL(request.url ?? "/", `http://${request.headers.host ?? `${hostname}:${requestedPort}`}`);
29
64
 
30
65
  if (request.method === "GET" && requestUrl.pathname === "/") {
31
66
  sendHtml(response, renderDeployChatUi({ deployUrl }));
@@ -102,23 +137,59 @@ export async function startDeployChatUiServer(
102
137
  }
103
138
  });
104
139
 
105
- await new Promise<void>((resolvePromise, reject) => {
140
+ return server;
141
+ }
142
+
143
+ async function listenForDeployChatUiServer(input: {
144
+ hostname: string;
145
+ requestedPort: number;
146
+ autoPort: boolean;
147
+ create: () => Server;
148
+ }): Promise<{ server: Server; port: number; portConflict: { port: number } | null }> {
149
+ let firstConflict: { port: number } | null = null;
150
+ const lastPort = input.autoPort ? input.requestedPort + 50 : input.requestedPort;
151
+
152
+ for (let port = input.requestedPort; port <= lastPort; port += 1) {
153
+ const server = input.create();
154
+
155
+ try {
156
+ await listen(server, input.hostname, port);
157
+ return { server, port, portConflict: port === input.requestedPort ? null : firstConflict };
158
+ } catch (error) {
159
+ await closeServerAfterListenError(server);
160
+
161
+ if (!input.autoPort || !isAddressInUseError(error)) {
162
+ throw error;
163
+ }
164
+
165
+ firstConflict ??= { port };
166
+ }
167
+ }
168
+
169
+ throw new AgentKitError(
170
+ "runtime_error",
171
+ `Could not allocate a local AgentKit hosted chat UI port after ${input.requestedPort}-${lastPort}.`,
172
+ );
173
+ }
174
+
175
+ function listen(server: Server, hostname: string, port: number): Promise<void> {
176
+ return new Promise((resolvePromise, reject) => {
106
177
  server.once("error", reject);
107
- server.listen(options.port, hostname, () => {
178
+ server.listen(port, hostname, () => {
108
179
  server.off("error", reject);
109
180
  resolvePromise();
110
181
  });
111
182
  });
183
+ }
112
184
 
113
- return {
114
- url: `http://${hostname}:${options.port}`,
115
- deployUrl,
116
- tokenSource: process.env.AGENTKIT_DEPLOY_ACCESS_TOKEN ? "AGENTKIT_DEPLOY_ACCESS_TOKEN" : relativePath(root, tokenPath),
117
- stop: () =>
118
- new Promise<void>((resolvePromise, reject) => {
119
- server.close((error) => (error ? reject(error) : resolvePromise()));
120
- }),
121
- };
185
+ async function closeServerAfterListenError(server: Server): Promise<void> {
186
+ await new Promise<void>((resolvePromise) => {
187
+ server.close(() => resolvePromise());
188
+ });
189
+ }
190
+
191
+ function isAddressInUseError(error: unknown): boolean {
192
+ return Boolean(error && typeof error === "object" && "code" in error && error.code === "EADDRINUSE");
122
193
  }
123
194
 
124
195
  export async function readDeployAccessTokenForUi(tokenPath: string): Promise<string> {
package/src/cli/index.ts CHANGED
@@ -109,6 +109,8 @@ async function main() {
109
109
  throw new Error("Usage: agentkit help [commands]");
110
110
  }
111
111
 
112
+ warnIfUnsupportedNode();
113
+
112
114
  if (args.command === "new") {
113
115
  const [name] = args.positional;
114
116
  const template = String(args.flags.template ?? "blank");
@@ -136,8 +138,8 @@ async function main() {
136
138
  console.log(" npm install");
137
139
  }
138
140
  console.log(" open this folder in Codex or Claude Code");
139
- console.log(" coding agents start at skills/agentkit-capsule/SKILL.md");
140
- console.log(' ask: "Develop an appointment and intake agent for an ophthalmology office"');
141
+ console.log(' ask Codex: "Develop an appointment and intake agent for an ophthalmology office"');
142
+ console.log(" Codex starts at skills/agentkit-capsule/SKILL.md and treats your request as the brief");
141
143
  console.log(' npm run chat -- --message "hello"');
142
144
  console.log(" npm run dev");
143
145
  console.log(" npm run agentkit -- deploy");
@@ -187,16 +189,25 @@ async function main() {
187
189
  throw new Error("Usage: agentkit chat-ui --deploy [--port <number>] [--token-file <path>]");
188
190
  }
189
191
 
190
- const port = parsePort(args.flags.port, "agentkit chat-ui --deploy --port <number>") ?? 4124;
192
+ const requestedPort = parsePort(args.flags.port, "agentkit chat-ui --deploy --port <number>");
193
+ const port = requestedPort ?? 4124;
191
194
  const tokenFile = parseOptionalStringFlag(
192
195
  args.flags["token-file"],
193
196
  "token-file",
194
197
  "agentkit chat-ui --deploy --token-file <path>",
195
198
  );
196
- const server = await startDeployChatUiServer(process.cwd(), { port, tokenFile });
199
+ const server = await startDeployChatUiServer(process.cwd(), {
200
+ port,
201
+ tokenFile,
202
+ autoPort: requestedPort === undefined,
203
+ });
197
204
 
198
205
  console.log("AgentKit hosted chat UI running");
199
206
  console.log("");
207
+ if (server.portConflict) {
208
+ console.log(`Port ${server.portConflict.port} is already used. Started hosted chat UI at ${server.url}.`);
209
+ console.log("");
210
+ }
200
211
  console.log(`Chat: ${server.url}`);
201
212
  console.log(`Production: ${server.deployUrl}`);
202
213
  console.log(`Token: ${server.tokenSource}`);
@@ -936,14 +947,16 @@ async function main() {
936
947
  return;
937
948
  }
938
949
 
950
+ let readinessReport: Awaited<ReturnType<typeof checkDeployReadiness>> | null = null;
951
+
939
952
  if (!args.flags["dry-run"] && !args.flags["local-wrangler"]) {
940
- const report = await checkDeployReadiness({
953
+ readinessReport = await checkDeployReadiness({
941
954
  apiUrl: await resolveCloudApiUrl(args.flags.api),
942
955
  requireLogin: !anonymous,
943
956
  });
944
957
 
945
- if (!report.ok) {
946
- printDeployReadinessReport(report);
958
+ if (!readinessReport.ok) {
959
+ printDeployReadinessReport(readinessReport);
947
960
  throw new AgentKitError(
948
961
  "deploy_readiness_failed",
949
962
  "Fix deploy readiness failures before running agentkit deploy.",
@@ -1016,6 +1029,14 @@ async function main() {
1016
1029
  : undefined,
1017
1030
  });
1018
1031
  }
1032
+ printDeployProductionHandoff({
1033
+ deployUrl: deploy.deploy.url,
1034
+ statePath,
1035
+ chatAccess,
1036
+ databaseSchemaPath: result.tursoSchemaPath,
1037
+ readinessReport,
1038
+ smokeMessage,
1039
+ });
1019
1040
  return;
1020
1041
  }
1021
1042
 
@@ -1362,7 +1383,7 @@ Rules:
1362
1383
  - Edit the agent contract in agentkit.config.ts.
1363
1384
  - Add TypeScript tools under tools/.
1364
1385
  - Keep the first useful version runnable with test/fake unless the owner has already chosen a real provider.
1365
- - Before testing real conversation behavior, ask the owner to choose OpenRouter, OpenAI, Anthropic, or another supported provider. Do not choose for them.
1386
+ - Before testing real conversation behavior, ask the owner to choose OpenRouter, OpenAI, Anthropic, OpenCode Zen, OpenCode Go, or another supported provider. Do not choose for them.
1366
1387
  - Keep local secrets in .env only.
1367
1388
  - Do not commit or print secret values.
1368
1389
  - Do not edit .agentkit/ directly.
@@ -1402,6 +1423,84 @@ function isRecord(value: unknown): value is Record<string, unknown> {
1402
1423
  return typeof value === "object" && value !== null && !Array.isArray(value);
1403
1424
  }
1404
1425
 
1426
+ function warnIfUnsupportedNode(): void {
1427
+ const major = majorNodeVersion(process.versions.node);
1428
+
1429
+ if (major !== null && major < 24) {
1430
+ console.warn(
1431
+ `Warning: @andreprado/agentkit requires Node >=24; current Node is ${process.versions.node}. Use Node 24+ before relying on local runtime or deploy commands.`,
1432
+ );
1433
+ }
1434
+ }
1435
+
1436
+ function majorNodeVersion(version: string): number | null {
1437
+ const [major] = version.split(".");
1438
+ const parsed = Number(major);
1439
+ return Number.isInteger(parsed) ? parsed : null;
1440
+ }
1441
+
1442
+ function printDeployProductionHandoff(input: {
1443
+ deployUrl: string;
1444
+ statePath: string;
1445
+ chatAccess: Awaited<ReturnType<typeof ensureDeployChatAccessToken>>;
1446
+ databaseSchemaPath: string | null;
1447
+ readinessReport: Awaited<ReturnType<typeof checkDeployReadiness>> | null;
1448
+ smokeMessage?: string;
1449
+ }): void {
1450
+ const context = input.readinessReport?.context;
1451
+ const userSecrets = context?.userManagedSecrets ?? [];
1452
+ const managedSecrets = context?.agentKitManagedSecrets ?? [];
1453
+ const composio = context?.integrations.managedComposio;
1454
+
1455
+ console.log("");
1456
+ console.log("Production handoff");
1457
+ console.log(`URL: ${input.deployUrl}`);
1458
+ console.log(`UI: npm run agentkit -- chat-ui --deploy`);
1459
+ console.log(`State: ${input.statePath}`);
1460
+ console.log(
1461
+ `Secrets: ${
1462
+ userSecrets.length > 0
1463
+ ? `user-managed set (${userSecrets.join(", ")})`
1464
+ : "no user-managed hosted secrets required"
1465
+ }${managedSecrets.length > 0 ? `; AgentKit-managed: ${managedSecrets.join(", ")}` : ""}`,
1466
+ );
1467
+ console.log(
1468
+ `Database: ${
1469
+ input.databaseSchemaPath
1470
+ ? `AgentKit-managed; schema artifact ${input.databaseSchemaPath}`
1471
+ : "AgentKit-managed; no application schema artifact"
1472
+ }`,
1473
+ );
1474
+
1475
+ if (composio?.enabled) {
1476
+ console.log(`Integrations: managed Composio configured for ${composio.toolkits.join(", ")}`);
1477
+ for (const toolkit of composio.toolkits) {
1478
+ console.log(`Connect: npm run agentkit -- integrations connect composio --toolkit ${toolkit}`);
1479
+ }
1480
+ } else {
1481
+ console.log("Integrations: none configured");
1482
+ }
1483
+
1484
+ if (input.smokeMessage) {
1485
+ console.log(`Smoke: verified with ${JSON.stringify(input.smokeMessage)}`);
1486
+ } else {
1487
+ console.log('Smoke: not run; next check: npm run agentkit -- deploy smoke --message "hello"');
1488
+ }
1489
+
1490
+ if (input.chatAccess.status === "ready") {
1491
+ console.log(`Chat token: ${relativePath(process.cwd(), input.chatAccess.path)}`);
1492
+ } else if (input.chatAccess.status === "skipped") {
1493
+ console.log("Chat token: not created for anonymous deploy");
1494
+ } else {
1495
+ console.log(`Chat token: failed; run npm run agentkit -- access token create ${defaultDeployChatAccessTokenName} --out ${defaultDeployChatAccessTokenPath}`);
1496
+ }
1497
+
1498
+ const nextCommand = composio?.enabled && composio.toolkits.length > 0
1499
+ ? `npm run agentkit -- integrations connect composio --toolkit ${composio.toolkits[0]}`
1500
+ : "npm run agentkit -- chat-ui --deploy";
1501
+ console.log(`Next: ${nextCommand}`);
1502
+ }
1503
+
1405
1504
  async function ensureDeployChatAccessToken(options: {
1406
1505
  apiUrl: string;
1407
1506
  root: string;
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type AgentRuntime = "local" | "edge";
2
- export type AgentProviderName = "test" | "openai" | "anthropic" | "openrouter" | "custom";
2
+ export type AgentProviderName = "test" | "openai" | "anthropic" | "openrouter" | "opencode" | "opencode-go" | "custom";
3
3
  export type AccessMode = "private" | "token" | "public";
4
4
  export type ToolVisibility = "user" | "internal";
5
5
  export type ChannelType = "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook";