create-theokit 1.4.0 → 1.5.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 +1 -1
- package/templates/default/agents/chat.ts +18 -16
- package/templates/default/agents/prompts/instructions.ts +21 -0
- package/templates/default/agents/skills/daily-briefing.ts +31 -0
- package/templates/default/agents/tools/current-time.ts +35 -0
- package/templates/default/agents/{_tools → tools}/weather.ts +2 -2
- package/templates/default/docs/ARCHITECTURE.md +28 -20
- package/templates/default/docs/CUSTOMIZATION.md +6 -6
- package/templates/default/package.json.tmpl +2 -2
- package/templates/default/agents/_lib/instructions.ts +0 -15
- package/templates/default/agents/skills/getting-started.md +0 -20
package/package.json
CHANGED
|
@@ -1,29 +1,31 @@
|
|
|
1
1
|
import { agent } from '@theokit/agents'
|
|
2
|
+
import { defineSkillReadTool } from '@theokit/sdk'
|
|
2
3
|
import { z } from 'zod'
|
|
3
4
|
|
|
4
|
-
import { BASE_INSTRUCTIONS } from './
|
|
5
|
-
import {
|
|
5
|
+
import { BASE_INSTRUCTIONS } from './prompts/instructions.js'
|
|
6
|
+
import { dailyBriefingSkill } from './skills/daily-briefing.js'
|
|
7
|
+
import { currentTimeTool } from './tools/current-time.js'
|
|
8
|
+
import { weatherTool } from './tools/weather.js'
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
|
-
*
|
|
11
|
+
* The `chat` agent — served at `POST /api/agents/chat` and bound by `useAgent('chat')`. This file IS the
|
|
12
|
+
* agent; it composes its neighbours under `agents/`: the persona in `prompts/`, capabilities in `tools/`,
|
|
13
|
+
* procedures in `skills/`. Those folders are that concern, NOT extra routes — the framework's scanner
|
|
14
|
+
* treats `prompts/ tools/ skills/ lib/ …` as semantic folders, so `agents/tools/weather.ts` never becomes
|
|
15
|
+
* a `/api/agents/tools/weather` endpoint. Add a second agent as another `agents/<name>.ts`. See
|
|
16
|
+
* `docs/ARCHITECTURE.md`.
|
|
9
17
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* session (the SDK owns storage).
|
|
14
|
-
*
|
|
15
|
-
* Provider: resolved from the environment — OPENROUTER_API_KEY (preferred — gateway to many
|
|
16
|
-
* models) OR ANTHROPIC_API_KEY / OPENAI_API_KEY. The `model` id is prefixed with the provider
|
|
17
|
-
* namespace so OpenRouter routes it upstream (see https://openrouter.ai/models).
|
|
18
|
-
*
|
|
19
|
-
* The agent is composed from its neighbours (see `docs/ARCHITECTURE.md`): the persona lives in
|
|
20
|
-
* `agents/_lib/instructions.ts` and tools in `agents/_tools/` (both underscore-prefixed so the
|
|
21
|
-
* route scanner skips them). Add a tool with `tool('name')…build()` and chain it via `.tool(...)`;
|
|
22
|
-
* add a skill as `agents/skills/<name>.md`.
|
|
18
|
+
* `@theokit/sdk` runs the agent; conversation turns auto-persist per session. Provider is resolved from
|
|
19
|
+
* the environment — OPENROUTER_API_KEY (preferred) OR ANTHROPIC_API_KEY / OPENAI_API_KEY; the model id is
|
|
20
|
+
* provider-prefixed so OpenRouter routes it upstream (https://openrouter.ai/models).
|
|
23
21
|
*/
|
|
24
22
|
export default agent()
|
|
25
23
|
.input(z.object({ message: z.string() }))
|
|
26
24
|
.model('openai/gpt-4o-mini')
|
|
27
25
|
.system(BASE_INSTRUCTIONS)
|
|
28
26
|
.tool(weatherTool)
|
|
27
|
+
.tool(currentTimeTool)
|
|
28
|
+
// `defineSkillReadTool` exposes the skills to the model as an on-demand `skill_read` tool — the model
|
|
29
|
+
// sees each skill's name + description and loads the full body only when it needs it.
|
|
30
|
+
.tool(defineSkillReadTool([dailyBriefingSkill]))
|
|
29
31
|
.build()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `chat` agent's system prompt (persona + rules). Lives in `prompts/` — a semantic sub-folder the
|
|
3
|
+
* framework's agent scanner skips, so it never becomes a route. Grow the persona here instead of inlining
|
|
4
|
+
* a long string in `index.ts`.
|
|
5
|
+
*/
|
|
6
|
+
export const BASE_INSTRUCTIONS = `You are a helpful assistant living inside a TheoKit app.
|
|
7
|
+
|
|
8
|
+
## Voice
|
|
9
|
+
- Answer clearly and concisely. Prefer a direct answer over a preamble.
|
|
10
|
+
- If you are unsure, say so rather than inventing an answer.
|
|
11
|
+
|
|
12
|
+
## Tools — call them, don't guess
|
|
13
|
+
- **Current weather** for a place → call the \`weather\` tool.
|
|
14
|
+
- **Current date / time** (optionally for a timezone) → call the \`current_time\` tool.
|
|
15
|
+
- Never state a time or the weather from memory; always call the tool. If a tool errors, tell the user
|
|
16
|
+
what failed and what you need (e.g. a valid city or IANA timezone).
|
|
17
|
+
|
|
18
|
+
## Skills
|
|
19
|
+
- You have a \`skill_read\` tool that lists documented procedures (skills). When a request matches a
|
|
20
|
+
skill's description — e.g. the user asks for a "briefing" / "morning update" / "what's my day looking
|
|
21
|
+
like" (the **daily-briefing** skill) — call \`skill_read\` to load its steps, then follow them.`
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createSkill } from '@theokit/sdk'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A real, code-defined skill (`createSkill`) — a documented procedure the MODEL can pull in on demand.
|
|
5
|
+
*
|
|
6
|
+
* Unlike a loose Markdown note, this is wired into the agent: `agents/chat.ts` passes it to
|
|
7
|
+
* `defineSkillReadTool([...])`, which gives the model a `skill_read` tool. The model sees the skill's
|
|
8
|
+
* name + description in every turn (cheap) and calls `skill_read('daily-briefing')` to load the full
|
|
9
|
+
* `instructions` only when it actually needs them — so long procedures don't bloat every prompt.
|
|
10
|
+
*
|
|
11
|
+
* Lives in `agents/skills/` — a semantic folder the route scanner skips, so it never becomes an endpoint.
|
|
12
|
+
*/
|
|
13
|
+
export const dailyBriefingSkill = createSkill({
|
|
14
|
+
name: 'daily-briefing',
|
|
15
|
+
description:
|
|
16
|
+
"Produce a short 'good morning' briefing (today's date, the weather, and a one-line nudge).",
|
|
17
|
+
instructions: `# Daily briefing
|
|
18
|
+
|
|
19
|
+
Trigger: the user asks for a "briefing", "morning update", or "what's my day looking like".
|
|
20
|
+
|
|
21
|
+
Steps:
|
|
22
|
+
1. Ask for (or reuse) the user's city and IANA timezone if you don't already have them.
|
|
23
|
+
2. Call the \`current_time\` tool with their timezone → the local date and time.
|
|
24
|
+
3. Call the \`weather\` tool with their city → the current conditions.
|
|
25
|
+
4. Reply in exactly three lines:
|
|
26
|
+
- **Today** — weekday + date (from current_time).
|
|
27
|
+
- **Weather** — conditions + temperature (from weather).
|
|
28
|
+
- **Nudge** — one short, friendly suggestion based on the weather.
|
|
29
|
+
|
|
30
|
+
Never invent the time or weather — always call the tools. Keep it to three lines unless asked for more.`,
|
|
31
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { tool } from 'theokit/server'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A local, deterministic tool — the current date + time, optionally in an IANA timezone. No network,
|
|
6
|
+
* always works: the counterpart to `weather.ts` (which hits an HTTP API), so the scaffold shows both a
|
|
7
|
+
* remote-call tool and a pure local one. Imported into `chat.ts` and chained with `.tool(currentTimeTool)`.
|
|
8
|
+
*/
|
|
9
|
+
export const currentTimeTool = tool('current_time')
|
|
10
|
+
.describe('Get the current date and time, optionally for a specific IANA timezone.')
|
|
11
|
+
.input(
|
|
12
|
+
z.object({
|
|
13
|
+
timezone: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('IANA timezone, e.g. "Europe/Lisbon" or "America/Sao_Paulo". Defaults to UTC.'),
|
|
17
|
+
}),
|
|
18
|
+
)
|
|
19
|
+
.execute(async ({ timezone }) => {
|
|
20
|
+
const tz = timezone ?? 'UTC'
|
|
21
|
+
try {
|
|
22
|
+
const formatted = new Intl.DateTimeFormat('en-US', {
|
|
23
|
+
timeZone: tz,
|
|
24
|
+
dateStyle: 'full',
|
|
25
|
+
timeStyle: 'long',
|
|
26
|
+
}).format(new Date())
|
|
27
|
+
return `Current time (${tz}): ${formatted}`
|
|
28
|
+
} catch {
|
|
29
|
+
// fail-fast with a clear, actionable message (Rule 8) rather than a cryptic RangeError.
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Unknown timezone "${tz}". Use an IANA name like "Europe/Lisbon" or "America/Sao_Paulo".`,
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
.build()
|
|
@@ -5,8 +5,8 @@ import { z } from 'zod'
|
|
|
5
5
|
* An example agent tool — current weather for a place, via the keyless open-meteo API.
|
|
6
6
|
*
|
|
7
7
|
* A tool is PURE metadata + a handler: it describes a capability and runs local/HTTP work; it NEVER calls
|
|
8
|
-
* an LLM (the agent decides when to invoke it). Lives in `
|
|
9
|
-
*
|
|
8
|
+
* an LLM (the agent decides when to invoke it). Lives in `tools/` — a semantic sub-folder the agent
|
|
9
|
+
* scanner skips — and is imported into `chat.ts` and chained with `.tool(weatherTool)`.
|
|
10
10
|
*
|
|
11
11
|
* Add your own the same way: `tool('name').describe(...).input(z.object({...})).execute(async (input) => …).build()`.
|
|
12
12
|
*/
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
-
How this TheoKit app is organized. The layout puts the **agent at the center
|
|
4
|
-
|
|
3
|
+
How this TheoKit app is organized. The layout puts the **agent at the center**: the agent file and the
|
|
4
|
+
folders it composes (prompts, tools, skills) live together under `agents/`, with clean names — the
|
|
5
|
+
"file = identity" convention agent frameworks like [Eve](https://eve.dev) use.
|
|
5
6
|
|
|
6
7
|
## Project structure
|
|
7
8
|
|
|
8
9
|
```
|
|
9
10
|
.
|
|
10
|
-
├── agents/ # The agent
|
|
11
|
-
│ ├── chat.ts # the agent
|
|
12
|
-
│ ├──
|
|
13
|
-
│ │ └── instructions.ts
|
|
14
|
-
│ ├──
|
|
15
|
-
│ │
|
|
16
|
-
│ └──
|
|
17
|
-
│
|
|
11
|
+
├── agents/ # The agent, and what composes it
|
|
12
|
+
│ ├── chat.ts # the agent → POST /api/agents/chat, useAgent('chat')
|
|
13
|
+
│ ├── prompts/ # system prompts / personas
|
|
14
|
+
│ │ └── instructions.ts
|
|
15
|
+
│ ├── tools/ # tools the agent can call
|
|
16
|
+
│ │ ├── weather.ts # remote — current weather via open-meteo (HTTP)
|
|
17
|
+
│ │ └── current-time.ts # local — date/time in an IANA timezone (no network)
|
|
18
|
+
│ └── skills/ # procedures the model loads on demand (createSkill)
|
|
19
|
+
│ └── daily-briefing.ts # a real skill: time → weather → a one-line nudge
|
|
18
20
|
├── app/ # Frontend (web surface). tui → tui/, desktop → frontend/
|
|
19
21
|
├── server/ # Backend routes / actions (POST/GET handlers, jobs)
|
|
20
22
|
├── shared/ # Code imported by more than one layer
|
|
@@ -24,13 +26,14 @@ between the agent, the backend, the frontend, and cross-layer code.
|
|
|
24
26
|
└── theo.config.ts # App config (name, dirs, plugins)
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
##
|
|
29
|
+
## Clean names, no phantom routes
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
An agent is a file: `agents/<name>.ts` → `POST /api/agents/<name>`. But the framework's scanner is
|
|
32
|
+
**folder-semantic** — the conventional sub-folders under `agents/` (`prompts/`, `tools/`, `skills/`,
|
|
33
|
+
`lib/`, `hooks/`, `channels/`, `connections/`, `subagents/`, `schedules/`) are **that concern, not routes**.
|
|
34
|
+
So the names stay clean (`tools/`, not `_tools/`) and `agents/tools/weather.ts` never becomes a phantom
|
|
35
|
+
`/api/agents/tools/weather` endpoint. Markdown (`skills/*.md`) is never scanned either way. The
|
|
36
|
+
prompts/tools/skills are **shared** across every agent in `agents/`.
|
|
34
37
|
|
|
35
38
|
## Composition
|
|
36
39
|
|
|
@@ -40,13 +43,18 @@ folders). `skills/` needs no underscore because Markdown is never scanned.
|
|
|
40
43
|
export default agent()
|
|
41
44
|
.input(z.object({ message: z.string() }))
|
|
42
45
|
.model('openai/gpt-4o-mini')
|
|
43
|
-
.system(BASE_INSTRUCTIONS) // agents/
|
|
44
|
-
.tool(weatherTool) // agents/
|
|
46
|
+
.system(BASE_INSTRUCTIONS) // agents/prompts/instructions.ts
|
|
47
|
+
.tool(weatherTool) // agents/tools/weather.ts
|
|
48
|
+
.tool(currentTimeTool) // agents/tools/current-time.ts
|
|
49
|
+
.tool(defineSkillReadTool([dailyBriefingSkill])) // agents/skills/daily-briefing.ts
|
|
45
50
|
.build()
|
|
46
51
|
```
|
|
47
52
|
|
|
48
|
-
Grow the agent by editing its neighbours, not by inflating `chat.ts`: persona → `
|
|
49
|
-
a new capability → `
|
|
53
|
+
Grow the agent by editing its neighbours, not by inflating `chat.ts`: persona → `prompts/instructions.ts`,
|
|
54
|
+
a new capability → `tools/<name>.ts` (then `.tool(<name>Tool)`), a documented procedure → a
|
|
55
|
+
`createSkill(...)` in `skills/<name>.ts` (add it to the `defineSkillReadTool([...])` list). A **skill** is
|
|
56
|
+
loaded by the model on demand via the `skill_read` tool, so long procedures don't bloat every prompt. Add a
|
|
57
|
+
**second agent** as another `agents/<name>.ts`.
|
|
50
58
|
|
|
51
59
|
## Surfaces
|
|
52
60
|
|
|
@@ -5,18 +5,18 @@ Common changes, and where they go. See [ARCHITECTURE](./ARCHITECTURE.md) for the
|
|
|
5
5
|
| I want to… | Edit |
|
|
6
6
|
|------------|------|
|
|
7
7
|
| Change the model | `agents/chat.ts` (`.model(...)`) + the label in `shared/agent.ts` |
|
|
8
|
-
| Change the persona / rules | `agents/
|
|
9
|
-
| Add a tool (an action the agent can take) | new `agents/
|
|
10
|
-
| Add a skill (a
|
|
11
|
-
| Change the greeting / app name | `shared/agent.ts` |
|
|
8
|
+
| Change the persona / rules | `agents/prompts/instructions.ts` |
|
|
9
|
+
| Add a tool (an action the agent can take) | new `agents/tools/<name>.ts`, then `.tool(<name>Tool)` in `agents/chat.ts` |
|
|
10
|
+
| Add a skill (a procedure the model loads on demand) | `createSkill(...)` in `agents/skills/<name>.ts`, then add it to `defineSkillReadTool([...])` in `agents/chat.ts` |
|
|
12
11
|
| Add a second agent | new `agents/<name>.ts` → auto-served at `/api/agents/<name>`, bind with `useAgent('<name>')` |
|
|
12
|
+
| Change the greeting / app name | `shared/agent.ts` |
|
|
13
13
|
| Add a backend route | `server/routes/<name>.ts` |
|
|
14
14
|
| Change the provider key | `.env` — `OPENROUTER_API_KEY` (or `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`). See [ENVIRONMENT](./ENVIRONMENT.md) |
|
|
15
15
|
|
|
16
16
|
## Adding a tool
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
|
-
// agents/
|
|
19
|
+
// agents/tools/echo.ts
|
|
20
20
|
import { tool } from 'theokit/server'
|
|
21
21
|
import { z } from 'zod'
|
|
22
22
|
|
|
@@ -29,7 +29,7 @@ export const echoTool = tool('echo')
|
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
31
|
// agents/chat.ts
|
|
32
|
-
import { echoTool } from './
|
|
32
|
+
import { echoTool } from './tools/echo.js'
|
|
33
33
|
// …
|
|
34
34
|
.tool(weatherTool)
|
|
35
35
|
.tool(echoTool) // chain as many as you need
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"theokit": "^0.
|
|
18
|
+
"theokit": "^0.31.0",
|
|
19
19
|
"@theokit/agents": "^0.35.2",
|
|
20
|
-
"@theokit/sdk": "^2.
|
|
20
|
+
"@theokit/sdk": "^2.25.0",
|
|
21
21
|
"@theokit/ui": "^1.0.0",
|
|
22
22
|
"@usetheo/ui": "^0.14.0",
|
|
23
23
|
"ai": "^7.0.0",
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The agent's base system prompt.
|
|
3
|
-
*
|
|
4
|
-
* Kept in `agents/_lib/` (an underscore-prefixed folder) so the framework's `agents/*` route scanner
|
|
5
|
-
* skips it — only real agents like `agents/chat.ts` become `POST /api/agents/<name>` endpoints. Import
|
|
6
|
-
* this into an agent's `.system(...)`; grow it here (persona, guardrails, tool guidance) instead of
|
|
7
|
-
* inlining a long string in the agent file.
|
|
8
|
-
*/
|
|
9
|
-
import { AGENT } from '../../shared/agent.js'
|
|
10
|
-
|
|
11
|
-
export const BASE_INSTRUCTIONS = `You are ${AGENT.name}, a helpful assistant living inside a TheoKit app.
|
|
12
|
-
|
|
13
|
-
- Answer clearly and concisely.
|
|
14
|
-
- When the user asks about current weather, call the \`weather\` tool instead of guessing.
|
|
15
|
-
- If you are unsure, say so rather than inventing an answer.`
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# Getting started
|
|
2
|
-
|
|
3
|
-
A **skill** is a Markdown file that documents a repeatable task for your agent — a recipe it (and you) can
|
|
4
|
-
follow. Skills live in `agents/skills/` and are plain `.md`, so the route scanner ignores them; reference
|
|
5
|
-
them from your instructions or load them into a prompt as your app grows.
|
|
6
|
-
|
|
7
|
-
This example just documents the scaffold itself:
|
|
8
|
-
|
|
9
|
-
## Answer a question
|
|
10
|
-
|
|
11
|
-
1. Read the user's message.
|
|
12
|
-
2. If it needs live weather, call the `weather` tool (see `agents/_tools/weather.ts`).
|
|
13
|
-
3. Otherwise answer directly, concisely.
|
|
14
|
-
|
|
15
|
-
## Add a capability
|
|
16
|
-
|
|
17
|
-
- **A tool** (an action the agent can take) → `agents/_tools/<name>.ts`, then `.tool(<name>Tool)` in
|
|
18
|
-
`agents/chat.ts`.
|
|
19
|
-
- **A skill** (a documented procedure) → a new `agents/skills/<name>.md` like this one.
|
|
20
|
-
- **The persona / rules** → edit `agents/_lib/instructions.ts`.
|