create-theokit 1.25.0 → 1.25.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-theokit",
3
- "version": "1.25.0",
3
+ "version": "1.25.1",
4
4
  "type": "module",
5
5
  "description": "Scaffold a new TheoKit project",
6
6
  "repository": {
@@ -20,7 +20,7 @@ export const policy = 'public'
20
20
 
21
21
  export default AgentBuilder.create()
22
22
  .input(z.object({ instruction: z.string() }))
23
- .model(process.env.LLM_MODEL ?? 'openai/gpt-4o-mini')
23
+ .model(process.env.LLM_MODEL ?? 'openrouter/openai/gpt-4o-mini')
24
24
  .system(
25
25
  'You turn research notes into published output. Read notes with read_notes, then publish. ' +
26
26
  'Publishing is gated: a human approves it before it happens.',
@@ -19,7 +19,7 @@ export const policy = 'public'
19
19
 
20
20
  export default AgentBuilder.create()
21
21
  .input(z.object({ topic: z.string() }))
22
- .model(process.env.LLM_MODEL ?? 'openai/gpt-4o-mini')
22
+ .model(process.env.LLM_MODEL ?? 'openrouter/openai/gpt-4o-mini')
23
23
  .system(
24
24
  'You research a topic and leave notes for the publisher bot. ' +
25
25
  'Write what you find with write_note; read what you already know with read_notes. ' +
@@ -2,4 +2,4 @@
2
2
  OPENROUTER_API_KEY=sk-or-v1-your-key-here
3
3
 
4
4
  # Optional: override the model declared in agents/chat.ts
5
- # LLM_MODEL=openai/gpt-4o-mini
5
+ # LLM_MODEL=openrouter/openai/gpt-4o-mini
@@ -56,7 +56,7 @@ your environment.
56
56
  ```ts
57
57
  export default AgentBuilder.create()
58
58
  .input(z.object({ message: z.string() }))
59
- .model('openai/gpt-4o-mini')
59
+ .model('openrouter/openai/gpt-4o-mini')
60
60
  .system(BASE_INSTRUCTIONS)
61
61
  .tool(weatherTool)
62
62
  .approval('send_notification', { question: 'Send this notification?' })
@@ -14,9 +14,17 @@ import { weatherTool } from './tools/weather.js'
14
14
  * treats `prompts/ tools/ skills/ lib/ …` as semantic folders, so `agents/tools/weather.ts` never becomes
15
15
  * a `/api/agents/tools/weather` endpoint. Add a second agent as another `agents/<name>.ts`.
16
16
  *
17
- * `@theokit/sdk` runs the agent; conversation turns auto-persist per session. Provider is resolved from
18
- * the environment — OPENROUTER_API_KEY (preferred) OR ANTHROPIC_API_KEY / OPENAI_API_KEY; the model id is
19
- * provider-prefixed so OpenRouter routes it upstream (https://openrouter.ai/models).
17
+ * `@theokit/sdk` runs the agent; conversation turns auto-persist per session.
18
+ *
19
+ * The FIRST segment of the model id picks the provider, and the key it needs follows from that:
20
+ * `openrouter/…` needs `OPENROUTER_API_KEY`, `anthropic/…` needs `ANTHROPIC_API_KEY`, `openai/…`
21
+ * needs `OPENAI_API_KEY`. There is no magic routing — a bare `openai/gpt-4o-mini` goes to OpenAI,
22
+ * not through a gateway, even with an OpenRouter key present. Reaching OpenAI's catalog THROUGH
23
+ * OpenRouter means naming the gateway: `openrouter/openai/gpt-4o-mini`, which is what this file
24
+ * declares, because `.env.example` asks for `OPENROUTER_API_KEY` (one key, many models —
25
+ * https://openrouter.ai/models).
26
+ *
27
+ * Change the id and the key changes with it. Both live in `.env` and here; nothing else to wire.
20
28
  */
21
29
  /**
22
30
  * Who may run this agent, and against which conversation (ADR 0001).
@@ -48,7 +56,7 @@ export default AgentBuilder.create()
48
56
  // so setting it changed the model to exactly what it already was (#398, #408). One expression is
49
57
  // cheaper than an override path through the framework, and it keeps the value visible in the file
50
58
  // that decides it. The literal stays as the fallback: a scaffold has to run with no environment.
51
- .model(process.env.LLM_MODEL ?? 'openai/gpt-4o-mini')
59
+ .model(process.env.LLM_MODEL ?? 'openrouter/openai/gpt-4o-mini')
52
60
  .system(BASE_INSTRUCTIONS)
53
61
  .tool(weatherTool)
54
62
  .tool(currentTimeTool)
@@ -59,7 +59,7 @@ An agent that outgrows one file becomes a folder that co-locates its own composi
59
59
  ```ts
60
60
  export default AgentBuilder.create()
61
61
  .input(z.object({ message: z.string() }))
62
- .model('openai/gpt-4o-mini')
62
+ .model('openrouter/openai/gpt-4o-mini')
63
63
  .system(BASE_INSTRUCTIONS) // agents/prompts/instructions.ts
64
64
  .tool(weatherTool) // agents/tools/weather.ts
65
65
  .tool(currentTimeTool) // agents/tools/current-time.ts
@@ -8,9 +8,17 @@ Configuration lives in `.env` (copy `.env.example`). Nothing here is committed
8
8
  | `ANTHROPIC_API_KEY` | one of these | Use Anthropic directly instead of OpenRouter. |
9
9
  | `OPENAI_API_KEY` | one of these | Use OpenAI directly. |
10
10
 
11
- The agent resolves the key from the environment at runtime (OpenRouter preferred). The model id in
12
- `agents/chat.ts` is provider-prefixed (e.g. `openai/gpt-4o-mini`) so OpenRouter routes it upstream see
13
- <https://openrouter.ai/models>.
11
+ The FIRST segment of the model id in `agents/chat.ts` picks the provider, and that decides which key
12
+ is needed. It is not a hint: `openai/gpt-4o-mini` goes to OpenAI and needs `OPENAI_API_KEY`, even
13
+ with an OpenRouter key present. Reaching another vendor's catalog THROUGH OpenRouter means naming
14
+ the gateway first — `openrouter/openai/gpt-4o-mini`, which is what the scaffold declares, matching
15
+ the key above. See <https://openrouter.ai/models> for the ids OpenRouter serves.
16
+
17
+ | Model id in `agents/chat.ts` | Key it needs |
18
+ | ------------------------------- | -------------------- |
19
+ | `openrouter/openai/gpt-4o-mini` | `OPENROUTER_API_KEY` |
20
+ | `anthropic/claude-sonnet-4-6` | `ANTHROPIC_API_KEY` |
21
+ | `openai/gpt-4o-mini` | `OPENAI_API_KEY` |
14
22
 
15
23
  ```bash
16
24
  cp .env.example .env
@@ -25,7 +25,7 @@ import { z } from 'zod'
25
25
 
26
26
  export default defineAgent({
27
27
  input: z.object({ message: z.string() }),
28
- model: 'openai/gpt-4o-mini',
28
+ model: 'openrouter/openai/gpt-4o-mini',
29
29
  system: 'You are a helpful assistant.',
30
30
  })
31
31
  ```
@@ -33,8 +33,11 @@ export default defineAgent({
33
33
  The endpoint streams the ai-sdk `UIMessageStream` that `useAgent` (client hook) consumes.
34
34
  `@theokit/sdk` runs the agent; conversation turns auto-persist per session — the SDK owns storage.
35
35
 
36
- **Provider resolution:** `OPENROUTER_API_KEY` (preferred routes to many models) OR
37
- `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`. Set one in `.env`.
36
+ **Provider resolution:** the FIRST segment of the model id picks the provider, and that decides
37
+ which key is needed — `openrouter/…` needs `OPENROUTER_API_KEY`, `anthropic/…` needs
38
+ `ANTHROPIC_API_KEY`, `openai/…` needs `OPENAI_API_KEY`. A bare vendor prefix is a selection of that
39
+ vendor, not a hint: reaching OpenAI's catalog through OpenRouter means naming the gateway first
40
+ (`openrouter/openai/gpt-4o-mini`). Set the matching key in `.env`.
38
41
 
39
42
  ## Advanced Surface — @Agent Decorator (DI / class-based)
40
43
 
@@ -46,7 +49,7 @@ When you need dependency injection or composition, build the agent from **capabi
46
49
  import { applyCapabilities, AgentConfigCapability, ModelCapability } from '@theokit/agents'
47
50
 
48
51
  export const assistantAgent = applyCapabilities([
49
- new ModelCapability('openai/gpt-4o-mini'),
52
+ new ModelCapability('openrouter/openai/gpt-4o-mini'),
50
53
  new AgentConfigCapability({
51
54
  systemPrompt: 'You are a helpful assistant.',
52
55
  maxIterations: 5,
@@ -75,7 +78,7 @@ const currentTimeTool = defineAgentTool({
75
78
 
76
79
  export default defineAgent({
77
80
  input: z.object({ message: z.string() }),
78
- model: 'openai/gpt-4o-mini',
81
+ model: 'openrouter/openai/gpt-4o-mini',
79
82
  system: 'You are a helpful assistant.',
80
83
  tools: [currentTimeTool],
81
84
  })
@@ -26,7 +26,7 @@ returns the `Response` your route must return.
26
26
 
27
27
  ```typescript
28
28
  import { handleChannelWebhook } from 'theokit/server/agent'
29
- import { telegram } from 'theokit/server/webhook' // also: discord, slack, github, stripe
29
+ import { telegram } from 'theokit/server/webhook' // also: discord, slack, github, stripe, whatsapp
30
30
  import { parseInbound } from '@theokit/gateway-telegram'
31
31
 
32
32
  const response = await handleChannelWebhook(request, new URL(request.url).pathname, {
@@ -42,6 +42,34 @@ const response = await handleChannelWebhook(request, new URL(request.url).pathna
42
42
  The path it expects is `POST /api/agents/<name>/channels/<platform>/webhook`; `<name>` and
43
43
  `<platform>` arrive in `onMessage` as `agent` and `platform`.
44
44
 
45
+ ## Platforms that verify the endpoint first (WhatsApp, Instagram, Messenger)
46
+
47
+ Meta will not deliver anything until it has verified the URL with a `GET` carrying
48
+ `hub.mode=subscribe`, `hub.verify_token` and `hub.challenge`, and it requires the challenge echoed
49
+ back as `text/plain`. Declare a responder per platform and mount the route for `GET` as well as
50
+ `POST`:
51
+
52
+ ```typescript
53
+ import { whatsapp, whatsappSubscribe } from 'theokit/server/webhook'
54
+
55
+ const response = await handleChannelWebhook(request, new URL(request.url).pathname, {
56
+ validators: { whatsapp: whatsapp({ appSecret: process.env.META_APP_SECRET! }) },
57
+ subscribe: { whatsapp: whatsappSubscribe({ verifyToken: process.env.META_VERIFY_TOKEN! }) },
58
+ onMessage: async ({ payload }) => {
59
+ /* … */
60
+ },
61
+ })
62
+ ```
63
+
64
+ `appSecret` is the Meta **app secret**, not the access token — the signature is HMAC-SHA256 of it
65
+ over the raw body. `verifyToken` is the string you typed into the Meta app when registering the URL;
66
+ comparing it is the only thing standing between an arbitrary caller and a subscription. A `GET` for
67
+ a platform with no `subscribe` entry answers `405`, not `404`: the platform is configured, it just
68
+ does not do handshakes.
69
+
70
+ Developing against any of this needs a public URL. `theo.config.ts` has `allowedHosts` for exactly
71
+ that — see the framework README.
72
+
45
73
  **Give it a `Request` whose body has not been read.** It calls `request.json()` itself, so a wrapper
46
74
  that has already parsed the body — `defineRoute` offers a parsed `body` in its handler context —
47
75
  leaves nothing for it to read. Mount it where you still hold the original request, or pass a clone.
@@ -16,7 +16,7 @@
16
16
  "typecheck": "tsc --noEmit"
17
17
  },
18
18
  "dependencies": {
19
- "theokit": "^0.56.0",
19
+ "theokit": "^0.60.0",
20
20
  "@theokit/agents": "^10.1.0",
21
21
  "@theokit/sdk": "^4.52.1",
22
22
  "@theokit/ui": "^1.1.0",