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

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.23",
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> {
@@ -29,6 +29,16 @@ type DeployReadinessReport = {
29
29
  apiUrl: string;
30
30
  context: DeployReadinessContext;
31
31
  checks: DeployReadinessCheck[];
32
+ cloudAuth: {
33
+ source?: string;
34
+ accountEmail?: string;
35
+ };
36
+ onlineCapacity: {
37
+ accountUsed?: number;
38
+ accountLimit?: number;
39
+ globalUsed?: number;
40
+ globalLimit?: number;
41
+ };
32
42
  };
33
43
 
34
44
  export async function checkDeployReadiness(options: {
@@ -39,6 +49,12 @@ export async function checkDeployReadiness(options: {
39
49
  const auth = options.requireLogin ? await readCloudAuthForApiUrl(options.apiUrl) : null;
40
50
  const checks: DeployReadinessCheck[] = [];
41
51
  let cloudAuthUsable = false;
52
+ const cloudAuth: DeployReadinessReport["cloudAuth"] = {};
53
+ const onlineCapacity: DeployReadinessReport["onlineCapacity"] = {};
54
+
55
+ if (auth?.token) {
56
+ cloudAuth.source = formatCloudAuthSource(auth);
57
+ }
42
58
 
43
59
  if (context.runtime !== "edge") {
44
60
  checks.push({
@@ -84,6 +100,9 @@ export async function checkDeployReadiness(options: {
84
100
  try {
85
101
  const me = (await cloudApiRequest(options.apiUrl, "/v1/me", { method: "GET" })) as CloudMeResponse;
86
102
  const email = me.account?.email;
103
+ if (email) {
104
+ cloudAuth.accountEmail = email;
105
+ }
87
106
  checks.push({
88
107
  status: "pass",
89
108
  title: "Login",
@@ -164,6 +183,22 @@ export async function checkDeployReadiness(options: {
164
183
  const globalLimit = normalizeOptionalNumber(global?.limit);
165
184
  const globalUsed = normalizeOptionalNumber(global?.used);
166
185
 
186
+ if (accountLimit !== undefined) {
187
+ onlineCapacity.accountLimit = accountLimit;
188
+ }
189
+
190
+ if (accountUsed !== undefined) {
191
+ onlineCapacity.accountUsed = accountUsed;
192
+ }
193
+
194
+ if (globalLimit !== undefined) {
195
+ onlineCapacity.globalLimit = globalLimit;
196
+ }
197
+
198
+ if (globalUsed !== undefined) {
199
+ onlineCapacity.globalUsed = globalUsed;
200
+ }
201
+
167
202
  if (accountLimit !== undefined && accountUsed !== undefined && accountUsed >= accountLimit) {
168
203
  checks.push({
169
204
  status: "fail",
@@ -374,6 +409,8 @@ export async function checkDeployReadiness(options: {
374
409
  apiUrl: options.apiUrl,
375
410
  context,
376
411
  checks,
412
+ cloudAuth,
413
+ onlineCapacity,
377
414
  };
378
415
  }
379
416
 
@@ -383,6 +420,19 @@ export function printDeployReadinessReport(report: DeployReadinessReport): void
383
420
  console.log(`Project ID: ${report.context.projectId}`);
384
421
  console.log(`Target: ${report.context.target}`);
385
422
  console.log(`API: ${report.apiUrl}`);
423
+ const authSummary = formatDeployCloudAuthSummary(report);
424
+
425
+ if (authSummary) {
426
+ console.log(`Account: ${authSummary.account}`);
427
+ console.log(`Auth source: ${authSummary.source}`);
428
+ }
429
+
430
+ const capacitySummary = formatDeployOnlineCapacity(report, "current");
431
+
432
+ if (capacitySummary) {
433
+ console.log(`Capacity: ${capacitySummary}`);
434
+ }
435
+
386
436
  console.log("");
387
437
 
388
438
  for (const check of report.checks) {
@@ -394,6 +444,36 @@ export function printDeployReadinessReport(report: DeployReadinessReport): void
394
444
  }
395
445
  }
396
446
 
447
+ export function formatDeployCloudAuthSummary(
448
+ report: DeployReadinessReport,
449
+ ): { account: string; source: string } | null {
450
+ if (!report.cloudAuth.accountEmail || !report.cloudAuth.source) {
451
+ return null;
452
+ }
453
+
454
+ return {
455
+ account: report.cloudAuth.accountEmail,
456
+ source: report.cloudAuth.source,
457
+ };
458
+ }
459
+
460
+ export function formatDeployOnlineCapacity(
461
+ report: DeployReadinessReport,
462
+ timing: "current" | "before deploy",
463
+ ): string | null {
464
+ const suffix = timing === "before deploy" ? " before deploy" : "";
465
+
466
+ if (report.onlineCapacity.accountUsed !== undefined && report.onlineCapacity.accountLimit !== undefined) {
467
+ return `${report.onlineCapacity.accountUsed}/${report.onlineCapacity.accountLimit} account online deploy slots in use${suffix}`;
468
+ }
469
+
470
+ if (report.onlineCapacity.globalUsed !== undefined && report.onlineCapacity.globalLimit !== undefined) {
471
+ return `${report.onlineCapacity.globalUsed}/${report.onlineCapacity.globalLimit} global online deploy slots in use${suffix}`;
472
+ }
473
+
474
+ return null;
475
+ }
476
+
397
477
  export async function syncHostedSecretsFromLocal(options: { apiUrl: string; projectId: string }): Promise<void> {
398
478
  const context = await loadDeployReadinessContext(process.cwd(), process.env);
399
479
  const localEnv = await loadCapsuleEnv(context.root, {});