agentick 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +578 -231
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,82 +1,68 @@
|
|
|
1
1
|
# agentick
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**The component framework for AI.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://react.dev/)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
|
+
[](https://github.com/agenticklabs/agentick/pulls)
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
[](LICENSE)
|
|
11
|
+
A React reconciler where the render target is a language model. You build the context window with JSX — the same components, hooks, and composition you already know — and the framework compiles it into what the model sees.
|
|
10
12
|
|
|
11
13
|
```tsx
|
|
12
|
-
import { createApp, System, Timeline,
|
|
13
|
-
createTool, useContinuation } from "@agentick/core";
|
|
14
|
+
import { createApp, System, Timeline, createTool, useContinuation } from "@agentick/core";
|
|
14
15
|
import { openai } from "@agentick/openai";
|
|
15
16
|
import { z } from "zod";
|
|
16
17
|
|
|
17
|
-
// Tools are components — they render state into model context
|
|
18
18
|
const Search = createTool({
|
|
19
19
|
name: "search",
|
|
20
20
|
description: "Search the knowledge base",
|
|
21
21
|
input: z.object({ query: z.string() }),
|
|
22
|
-
handler: async ({ query }
|
|
22
|
+
handler: async ({ query }) => {
|
|
23
23
|
const results = await knowledgeBase.search(query);
|
|
24
|
-
const sources = com.getState("sources") ?? [];
|
|
25
|
-
com.setState("sources", [...sources, ...results.map((r) => r.title)]);
|
|
26
24
|
return [{ type: "text", text: JSON.stringify(results) }];
|
|
27
25
|
},
|
|
28
|
-
// render() injects live state into the context window every tick
|
|
29
|
-
render: (tickState, com) => {
|
|
30
|
-
const sources = com.getState("sources");
|
|
31
|
-
return sources?.length ? (
|
|
32
|
-
<Section id="sources" audience="model">
|
|
33
|
-
Sources found so far: {sources.join(", ")}
|
|
34
|
-
</Section>
|
|
35
|
-
) : null;
|
|
36
|
-
},
|
|
37
26
|
});
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// The model auto-continues when it makes tool calls.
|
|
42
|
-
// Hooks add your own stop conditions.
|
|
43
|
-
useContinuation((result) => {
|
|
44
|
-
if (result.tick >= 20) result.stop("too-many-ticks");
|
|
45
|
-
});
|
|
28
|
+
function ResearchAgent() {
|
|
29
|
+
useContinuation((result) => result.tick < 10);
|
|
46
30
|
|
|
47
31
|
return (
|
|
48
32
|
<>
|
|
49
|
-
<System>
|
|
50
|
-
|
|
51
|
-
</System>
|
|
52
|
-
|
|
53
|
-
{/* You control exactly how conversation history renders */}
|
|
54
|
-
<Timeline>
|
|
55
|
-
{(history, pending) => <>
|
|
56
|
-
{history.map((entry, i) =>
|
|
57
|
-
i < history.length - 4
|
|
58
|
-
? <CompactMessage key={i} entry={entry} />
|
|
59
|
-
: <Message key={i} {...entry.message} />
|
|
60
|
-
)}
|
|
61
|
-
{pending.map((msg, i) => <Message key={`p-${i}`} {...msg.message} />)}
|
|
62
|
-
</>}
|
|
63
|
-
</Timeline>
|
|
64
|
-
|
|
33
|
+
<System>Search thoroughly, then write a summary.</System>
|
|
34
|
+
<Timeline />
|
|
65
35
|
<Search />
|
|
66
36
|
</>
|
|
67
37
|
);
|
|
68
38
|
}
|
|
69
39
|
|
|
70
|
-
const
|
|
71
|
-
const app = createApp(ResearchAgent, { model });
|
|
40
|
+
const app = createApp(ResearchAgent, { model: openai({ model: "gpt-4o" }) });
|
|
72
41
|
const result = await app.run({
|
|
73
|
-
|
|
74
|
-
|
|
42
|
+
messages: [
|
|
43
|
+
{ role: "user", content: [{ type: "text", text: "What's new in quantum computing?" }] },
|
|
44
|
+
],
|
|
75
45
|
});
|
|
76
|
-
|
|
77
46
|
console.log(result.response);
|
|
78
47
|
```
|
|
79
48
|
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install agentick @agentick/openai zod
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Add to `tsconfig.json`:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"compilerOptions": {
|
|
60
|
+
"jsx": "react-jsx",
|
|
61
|
+
"jsxImportSource": "react"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
80
66
|
## Why Agentick
|
|
81
67
|
|
|
82
68
|
Every other AI framework gives you a pipeline. A chain. A graph. You slot your prompt into a template, bolt on some tools, and hope the model figures it out.
|
|
@@ -87,12 +73,120 @@ There are no prompt templates because JSX _is_ the template language. There are
|
|
|
87
73
|
|
|
88
74
|
This is application development, not chatbot configuration.
|
|
89
75
|
|
|
76
|
+
## Built-in Components
|
|
77
|
+
|
|
78
|
+
Everything in the component tree compiles to what the model sees. Components are the building blocks — compose them to construct the context window.
|
|
79
|
+
|
|
80
|
+
### Structure
|
|
81
|
+
|
|
82
|
+
| Component | Description |
|
|
83
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
84
|
+
| `<Timeline>` | Conversation history. Accepts a render function for full control, or renders with sensible defaults. Token budget compaction via `maxTokens`, `strategy`, `filter`, `limit`, `roles`. |
|
|
85
|
+
| `<Timeline.Provider>` | Context provider exposing timeline entries to descendants via `useTimelineContext()`. |
|
|
86
|
+
| `<Timeline.Messages>` | Renders messages from `Timeline.Provider` context. Optional `renderEntry` prop for custom rendering. |
|
|
87
|
+
| `<Section>` | Structured context block injected every tick. `audience` controls visibility: `"model"`, `"user"`, or `"all"`. |
|
|
88
|
+
| `<Model>` | Model configuration. Pass `engine` prop, or use adapter-specific components like `<OpenAIModel>` or `<GoogleModel>`. |
|
|
89
|
+
|
|
90
|
+
### Messages
|
|
91
|
+
|
|
92
|
+
| Component | Description |
|
|
93
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| `<System>` | System/instruction message. |
|
|
95
|
+
| `<User>` | User message. |
|
|
96
|
+
| `<Assistant>` | Assistant message. |
|
|
97
|
+
| `<Message>` | Generic message — takes `role` prop. The primitive underlying all role-specific components. |
|
|
98
|
+
| `<ToolResult>` | Tool execution result. Requires `toolCallId`. |
|
|
99
|
+
| `<Event>` | Persisted application event. Use for structured logging that survives in the timeline. |
|
|
100
|
+
| `<Ephemeral>` | Non-persisted context. Visible during compilation but not saved to history. `position`: `"start"`, `"before-user"`, `"end"`. |
|
|
101
|
+
| `<Grounding>` | Semantic wrapper for grounding context (ephemeral). `audience`: `"model"`, `"user"`, `"both"`. |
|
|
102
|
+
|
|
103
|
+
### Event Blocks
|
|
104
|
+
|
|
105
|
+
Use inside `<Event>` messages for structured event content:
|
|
106
|
+
|
|
107
|
+
| Component | Description |
|
|
108
|
+
| --------------- | ------------------------------------------------------------------------ |
|
|
109
|
+
| `<UserAction>` | User action block. Props: `action`, `actor?`, `target?`, `details?`. |
|
|
110
|
+
| `<SystemEvent>` | System event block. Props: `event`, `source?`, `data?`. |
|
|
111
|
+
| `<StateChange>` | State change block. Props: `entity`, `field?`, `from`, `to`, `trigger?`. |
|
|
112
|
+
|
|
113
|
+
### Semantic Formatting
|
|
114
|
+
|
|
115
|
+
Compile to renderer-appropriate output (markdown, XML, etc.):
|
|
116
|
+
|
|
117
|
+
| Component | Description |
|
|
118
|
+
| ---------------------- | ------------------------------------------------------------------------------------------- |
|
|
119
|
+
| `<H1>`, `<H2>`, `<H3>` | Heading levels 1–3. |
|
|
120
|
+
| `<Header level={n}>` | Generic heading (levels 1–6). |
|
|
121
|
+
| `<Paragraph>` | Text paragraph block. |
|
|
122
|
+
| `<List>` | List container. `ordered` for numbered, `task` for checkboxes. `title` for a heading. |
|
|
123
|
+
| `<ListItem>` | List item. `checked` prop for task lists. |
|
|
124
|
+
| `<Table>` | Table. `headers`/`rows` props for data, or `<Row>`/`<Column>` children for JSX composition. |
|
|
125
|
+
| `<Row>` | Table row. `header` prop for header rows. |
|
|
126
|
+
| `<Column>` | Table column. `align`: `"left"`, `"center"`, `"right"`. |
|
|
127
|
+
|
|
128
|
+
### Content Blocks
|
|
129
|
+
|
|
130
|
+
Typed content for composing rich message content:
|
|
131
|
+
|
|
132
|
+
| Component | Description |
|
|
133
|
+
| ------------ | ------------------------------------------------------------------------------------------- |
|
|
134
|
+
| `<Text>` | Text content. Children or `text` prop. Supports inline formatting: `<b>`, `<em>`, `<code>`. |
|
|
135
|
+
| `<Image>` | Image. `source: MediaSource` (URL or base64). |
|
|
136
|
+
| `<Document>` | Document attachment. `source: MediaSource`, `title?`. |
|
|
137
|
+
| `<Audio>` | Audio content. `source: MediaSource`, `transcript?`. |
|
|
138
|
+
| `<Video>` | Video content. `source: MediaSource`, `transcript?`. |
|
|
139
|
+
| `<Code>` | Code block. `language` prop (typescript, python, etc.). |
|
|
140
|
+
| `<Json>` | JSON data block. `data` prop for objects, or `text`/children for raw JSON strings. |
|
|
141
|
+
|
|
90
142
|
## The Context Is Yours
|
|
91
143
|
|
|
92
|
-
The core insight: **only what you render gets sent to the model.** `<Timeline>` isn't a magic black box — it accepts a render function
|
|
144
|
+
The core insight: **only what you render gets sent to the model.** `<Timeline>` isn't a magic black box — it accepts a render function, and you decide exactly how every message appears in the context window. Skip a message? The model never sees it. Rewrite it? That's what the model reads.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
<Timeline>
|
|
148
|
+
{(history, pending) => (
|
|
149
|
+
<>
|
|
150
|
+
{history.map((entry, i) => {
|
|
151
|
+
const msg = entry.message;
|
|
152
|
+
const isOld = i < history.length - 6;
|
|
153
|
+
|
|
154
|
+
if (isOld && msg.role === "user") {
|
|
155
|
+
const textOnly = msg.content
|
|
156
|
+
.filter((b) => b.type === "text")
|
|
157
|
+
.map((b) => b.text)
|
|
158
|
+
.join(" ");
|
|
159
|
+
return (
|
|
160
|
+
<Message key={i} role="user">
|
|
161
|
+
[Earlier: {textOnly.slice(0, 100)}...]
|
|
162
|
+
</Message>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (isOld && msg.role === "assistant") {
|
|
167
|
+
return (
|
|
168
|
+
<Message key={i} role="assistant">
|
|
169
|
+
[Previous response]
|
|
170
|
+
</Message>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return <Message key={i} {...msg} />;
|
|
175
|
+
})}
|
|
176
|
+
{pending.map((msg, i) => (
|
|
177
|
+
<Message key={`p-${i}`} {...msg.message} />
|
|
178
|
+
))}
|
|
179
|
+
</>
|
|
180
|
+
)}
|
|
181
|
+
</Timeline>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Images from 20 messages ago eating your context window? Render them as `[Image: beach sunset]`. Tool results from early in the conversation? Collapse them. Recent messages? Full detail. You write the function, you decide.
|
|
93
185
|
|
|
94
186
|
### Default — Just Works
|
|
95
187
|
|
|
188
|
+
With no children, `<Timeline />` renders conversation history with sensible defaults:
|
|
189
|
+
|
|
96
190
|
```tsx
|
|
97
191
|
function SimpleAgent() {
|
|
98
192
|
return (
|
|
@@ -104,80 +198,54 @@ function SimpleAgent() {
|
|
|
104
198
|
}
|
|
105
199
|
```
|
|
106
200
|
|
|
107
|
-
`<Timeline />` with no children renders conversation history with sensible defaults.
|
|
108
|
-
|
|
109
|
-
### Custom Rendering — Control What the Model Sees
|
|
110
|
-
|
|
111
|
-
The render function receives `history` (completed entries) and `pending` (messages queued this tick). Only what you return from this function enters the model's context:
|
|
112
|
-
|
|
113
|
-
```tsx
|
|
114
|
-
<Timeline>
|
|
115
|
-
{(history, pending) => <>
|
|
116
|
-
{history.map((entry, i) => {
|
|
117
|
-
const msg = entry.message;
|
|
118
|
-
const isOld = i < history.length - 6;
|
|
119
|
-
|
|
120
|
-
// Old user messages — drop images, keep text summaries
|
|
121
|
-
if (isOld && msg.role === "user") {
|
|
122
|
-
const textOnly = msg.content
|
|
123
|
-
.filter((b) => b.type === "text")
|
|
124
|
-
.map((b) => b.text)
|
|
125
|
-
.join(" ");
|
|
126
|
-
return <Message key={i} role="user">[Earlier: {textOnly.slice(0, 100)}...]</Message>;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Old assistant messages — collapse
|
|
130
|
-
if (isOld && msg.role === "assistant") {
|
|
131
|
-
return <Message key={i} role="assistant">[Previous response]</Message>;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Recent messages — full fidelity
|
|
135
|
-
return <Message key={i} {...msg} />;
|
|
136
|
-
})}
|
|
137
|
-
{pending.map((msg, i) => <Message key={`p-${i}`} {...msg.message} />)}
|
|
138
|
-
</>}
|
|
139
|
-
</Timeline>
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Images from 20 messages ago eating your context window? Render them as `[Image: beach sunset]`. Tool results from early in the conversation? Collapse them. Recent messages? Full detail. You write the function, you decide.
|
|
143
|
-
|
|
144
201
|
### Composability — It's React
|
|
145
202
|
|
|
146
|
-
That render logic getting complex? Extract it into a component
|
|
203
|
+
That render logic getting complex? Extract it into a component:
|
|
147
204
|
|
|
148
205
|
```tsx
|
|
149
|
-
// A reusable component for rendering older messages compactly
|
|
150
206
|
function CompactMessage({ entry }: { entry: COMTimelineEntry }) {
|
|
151
207
|
const msg = entry.message;
|
|
152
208
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
209
|
+
const summary = msg.content
|
|
210
|
+
.map((block) => {
|
|
211
|
+
switch (block.type) {
|
|
212
|
+
case "text":
|
|
213
|
+
return block.text.slice(0, 80);
|
|
214
|
+
case "image":
|
|
215
|
+
return `[Image: ${block.source?.description ?? "image"}]`;
|
|
216
|
+
case "tool_use":
|
|
217
|
+
return `[Called ${block.name}]`;
|
|
218
|
+
case "tool_result":
|
|
219
|
+
return `[Result from ${block.name}]`;
|
|
220
|
+
default:
|
|
221
|
+
return "";
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
.filter(Boolean)
|
|
225
|
+
.join(" | ");
|
|
163
226
|
|
|
164
227
|
return <Message role={msg.role}>{summary}</Message>;
|
|
165
228
|
}
|
|
166
229
|
|
|
167
|
-
// Use it in your Timeline
|
|
168
230
|
function Agent() {
|
|
169
231
|
return (
|
|
170
232
|
<>
|
|
171
233
|
<System>You are helpful.</System>
|
|
172
234
|
<Timeline>
|
|
173
|
-
{(history, pending) =>
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
235
|
+
{(history, pending) => (
|
|
236
|
+
<>
|
|
237
|
+
{history.map((entry, i) =>
|
|
238
|
+
i < history.length - 4 ? (
|
|
239
|
+
<CompactMessage key={i} entry={entry} />
|
|
240
|
+
) : (
|
|
241
|
+
<Message key={i} {...entry.message} />
|
|
242
|
+
),
|
|
243
|
+
)}
|
|
244
|
+
{pending.map((msg, i) => (
|
|
245
|
+
<Message key={`p-${i}`} {...msg.message} />
|
|
246
|
+
))}
|
|
247
|
+
</>
|
|
248
|
+
)}
|
|
181
249
|
</Timeline>
|
|
182
250
|
</>
|
|
183
251
|
);
|
|
@@ -206,7 +274,7 @@ function NarrativeAgent() {
|
|
|
206
274
|
|
|
207
275
|
The framework doesn't care how you structure the context. Multiple messages, one message, XML, prose — anything that compiles to content blocks gets sent.
|
|
208
276
|
|
|
209
|
-
### Sections — Structured Context
|
|
277
|
+
### Sections — Structured Context
|
|
210
278
|
|
|
211
279
|
```tsx
|
|
212
280
|
function AgentWithContext({ userId }: { userId: string }) {
|
|
@@ -215,11 +283,9 @@ function AgentWithContext({ userId }: { userId: string }) {
|
|
|
215
283
|
return (
|
|
216
284
|
<>
|
|
217
285
|
<System>You are a support agent.</System>
|
|
218
|
-
|
|
219
286
|
<Section id="user-context" audience="model">
|
|
220
287
|
Customer: {profile?.name}, Plan: {profile?.plan}, Since: {profile?.joinDate}
|
|
221
288
|
</Section>
|
|
222
|
-
|
|
223
289
|
<Timeline />
|
|
224
290
|
<TicketTool />
|
|
225
291
|
</>
|
|
@@ -229,43 +295,106 @@ function AgentWithContext({ userId }: { userId: string }) {
|
|
|
229
295
|
|
|
230
296
|
`<Section>` injects structured context that the model sees every tick — live data, computed state, whatever you need. The `audience` prop controls visibility (`"model"`, `"user"`, or `"all"`).
|
|
231
297
|
|
|
232
|
-
## Hooks
|
|
298
|
+
## Hooks
|
|
299
|
+
|
|
300
|
+
Hooks are real React hooks — `useState`, `useEffect`, `useMemo` — plus lifecycle hooks that fire at each phase of the agent execution loop.
|
|
301
|
+
|
|
302
|
+
### All Hooks
|
|
303
|
+
|
|
304
|
+
#### Lifecycle
|
|
305
|
+
|
|
306
|
+
| Hook | Description |
|
|
307
|
+
| --------------------- | -------------------------------------------------------------------------------------------------- |
|
|
308
|
+
| `useOnMount(cb)` | Run once when component first mounts. |
|
|
309
|
+
| `useOnUnmount(cb)` | Run once when component unmounts. |
|
|
310
|
+
| `useOnTickStart(cb)` | Run at start of each tick (tick 2+). Receives `(tickState, ctx)`. |
|
|
311
|
+
| `useOnTickEnd(cb)` | Run at end of each tick. Receives `(result, ctx)`. Return `false` or call `result.stop()` to halt. |
|
|
312
|
+
| `useAfterCompile(cb)` | Run after compilation completes. Receives `(compiled, ctx)`. |
|
|
313
|
+
| `useContinuation(cb)` | Control whether execution continues. Sugar for `useOnTickEnd`. |
|
|
314
|
+
|
|
315
|
+
#### State & Signals
|
|
316
|
+
|
|
317
|
+
| Hook | Description |
|
|
318
|
+
| ---------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
319
|
+
| `useSignal(initial)` | Reactive signal. `.set()`, `.update()`, `.subscribe()`. Reads outside render, triggers reconciliation on write. |
|
|
320
|
+
| `useComputed(fn, deps)` | Computed signal. Auto-updates when dependencies change. |
|
|
321
|
+
| `useComState(key, default?)` | Reactive COM state. Bidirectional sync with the context object model. |
|
|
322
|
+
|
|
323
|
+
Standalone signal factories (no hook rules — use anywhere):
|
|
324
|
+
|
|
325
|
+
| Function | Description |
|
|
326
|
+
| ----------------- | ------------------------------------------------------------------------------------------ |
|
|
327
|
+
| `signal(initial)` | Create a signal. |
|
|
328
|
+
| `computed(fn)` | Create a computed signal. |
|
|
329
|
+
| `effect(fn)` | Run side effect with automatic dependency tracking. Returns `EffectRef` with `.dispose()`. |
|
|
330
|
+
| `batch(fn)` | Batch signal updates — effects fire once after all updates. |
|
|
331
|
+
| `untracked(fn)` | Read signals without tracking as dependencies. |
|
|
332
|
+
|
|
333
|
+
#### Data
|
|
334
|
+
|
|
335
|
+
| Hook | Description |
|
|
336
|
+
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
|
|
337
|
+
| `useData(key, fetcher, deps?)` | Async data fetch with resolve-then-render. Throws promise on first render, returns cached value on re-render. |
|
|
338
|
+
| `useInvalidateData()` | Returns `(pattern: string \| RegExp) => void` to invalidate cached data. |
|
|
339
|
+
|
|
340
|
+
#### Knobs (Model-Visible State)
|
|
341
|
+
|
|
342
|
+
Knobs are reactive values the model can see _and set_ via tool calls:
|
|
343
|
+
|
|
344
|
+
| API | Description |
|
|
345
|
+
| --------------------------- | ------------------------------------------------------------------------------ |
|
|
346
|
+
| `knob(default, opts?)` | Create a knob descriptor at config level. |
|
|
347
|
+
| `useKnob(name, descriptor)` | Hook returning `[resolvedValue, setValue]`. |
|
|
348
|
+
| `<Knobs />` | Component that renders all knobs as a `<Section>` + registers `set_knob` tool. |
|
|
349
|
+
| `<Knobs.Provider>` | Context provider for custom knob rendering. |
|
|
350
|
+
| `<Knobs.Controls>` | Renders knob controls from `Knobs.Provider` context. |
|
|
351
|
+
| `isKnob(value)` | Type guard for knob descriptors. |
|
|
233
352
|
|
|
234
|
-
|
|
353
|
+
#### Context & Environment
|
|
235
354
|
|
|
236
|
-
|
|
355
|
+
| Hook | Description |
|
|
356
|
+
| -------------------------- | ------------------------------------------------------------------ |
|
|
357
|
+
| `useCom()` | Access the COM (context object model) — state, timeline, channels. |
|
|
358
|
+
| `useTickState()` | Current tick state: `{tick, previous, queuedMessages}`. |
|
|
359
|
+
| `useRuntimeStore()` | Runtime data store (hooks, knobs, lifecycle callbacks). |
|
|
360
|
+
| `useFormatter()` | Access message formatter context. |
|
|
361
|
+
| `useContextInfo()` | Real-time context utilization: token counts, utilization %. |
|
|
362
|
+
| `useTimelineContext()` | Timeline context (requires `Timeline.Provider` ancestor). |
|
|
363
|
+
| `useConversationHistory()` | Full conversation history from COM (no provider needed). |
|
|
237
364
|
|
|
238
|
-
|
|
365
|
+
#### React (re-exported)
|
|
366
|
+
|
|
367
|
+
All standard React hooks work in agent components: `useState`, `useEffect`, `useReducer`, `useMemo`, `useCallback`, `useRef`.
|
|
368
|
+
|
|
369
|
+
### Stop Conditions
|
|
370
|
+
|
|
371
|
+
The agent loop auto-continues when the model makes tool calls. `useContinuation` adds your own stop conditions:
|
|
239
372
|
|
|
240
373
|
```tsx
|
|
241
|
-
// Stop after a done marker
|
|
242
374
|
useContinuation((result) => !result.text?.includes("<DONE>"));
|
|
243
375
|
|
|
244
|
-
// Stop after too many ticks or too many tokens
|
|
245
376
|
useContinuation((result) => {
|
|
246
|
-
if (result.tick >= 10) {
|
|
377
|
+
if (result.tick >= 10) {
|
|
378
|
+
result.stop("max-ticks");
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
247
381
|
if (result.usage && result.usage.totalTokens > 100_000) {
|
|
248
|
-
result.stop("token-budget");
|
|
382
|
+
result.stop("token-budget");
|
|
383
|
+
return false;
|
|
249
384
|
}
|
|
250
385
|
});
|
|
251
386
|
```
|
|
252
387
|
|
|
253
|
-
###
|
|
388
|
+
### Between-Tick Logic
|
|
254
389
|
|
|
255
|
-
`useContinuation` is sugar for `useOnTickEnd`. Use the full version when you need to do real work
|
|
390
|
+
`useContinuation` is sugar for `useOnTickEnd`. Use the full version when you need to do real work:
|
|
256
391
|
|
|
257
392
|
```tsx
|
|
258
393
|
function VerifiedAgent() {
|
|
259
394
|
useOnTickEnd(async (result) => {
|
|
260
|
-
// Log every tick
|
|
261
|
-
analytics.track("tick", { tokens: result.usage?.totalTokens });
|
|
262
|
-
|
|
263
|
-
// When the model is done (no more tool calls), verify before accepting
|
|
264
395
|
if (result.text && !result.toolCalls.length) {
|
|
265
396
|
const quality = await verifyWithModel(result.text);
|
|
266
|
-
if (!quality.acceptable)
|
|
267
|
-
result.continue("failed-verification"); // force another tick
|
|
268
|
-
}
|
|
397
|
+
if (!quality.acceptable) result.continue("failed-verification");
|
|
269
398
|
}
|
|
270
399
|
});
|
|
271
400
|
|
|
@@ -278,7 +407,7 @@ function VerifiedAgent() {
|
|
|
278
407
|
}
|
|
279
408
|
```
|
|
280
409
|
|
|
281
|
-
###
|
|
410
|
+
### Custom Hooks
|
|
282
411
|
|
|
283
412
|
Custom hooks work exactly like React — they're just functions that call other hooks:
|
|
284
413
|
|
|
@@ -313,48 +442,18 @@ function CarefulAgent() {
|
|
|
313
442
|
return (
|
|
314
443
|
<>
|
|
315
444
|
<System>You have a token budget. Be concise.</System>
|
|
316
|
-
<Section id="budget" audience="model">
|
|
445
|
+
<Section id="budget" audience="model">
|
|
446
|
+
Tokens used: {spent}
|
|
447
|
+
</Section>
|
|
317
448
|
<Timeline />
|
|
318
449
|
</>
|
|
319
450
|
);
|
|
320
451
|
}
|
|
321
452
|
```
|
|
322
453
|
|
|
323
|
-
##
|
|
454
|
+
## Tools Render State
|
|
324
455
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
```tsx
|
|
328
|
-
const Search = createTool({ name: "search", ... });
|
|
329
|
-
const model = openai({ model: "gpt-4o" });
|
|
330
|
-
|
|
331
|
-
// As JSX — self-closing tags in the component tree
|
|
332
|
-
<model temperature={0.2} />
|
|
333
|
-
<Search />
|
|
334
|
-
|
|
335
|
-
// As direct calls — use programmatically
|
|
336
|
-
const handle = await model.generate(input);
|
|
337
|
-
const output = await Search.run({ query: "test" });
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
Context is maintained with AsyncLocalStorage, so tools and hooks can access session state from anywhere — no prop drilling required.
|
|
341
|
-
|
|
342
|
-
## More Examples
|
|
343
|
-
|
|
344
|
-
### One-Shot Run
|
|
345
|
-
|
|
346
|
-
```tsx
|
|
347
|
-
import { run, System, Timeline } from "@agentick/core";
|
|
348
|
-
import { openai } from "@agentick/openai";
|
|
349
|
-
|
|
350
|
-
const result = await run(
|
|
351
|
-
<><System>You are helpful.</System><Timeline /></>,
|
|
352
|
-
{ model: openai({ model: "gpt-4o" }), messages: [{ role: "user", content: [{ type: "text", text: "Hello!" }] }] },
|
|
353
|
-
);
|
|
354
|
-
console.log(result.response);
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### Stateful Tool with Render
|
|
456
|
+
Tools aren't just functions the model calls — they render their state back into the context window. The model sees the current state _every time it thinks_, not just in the tool response.
|
|
358
457
|
|
|
359
458
|
```tsx
|
|
360
459
|
const TodoTool = createTool({
|
|
@@ -365,12 +464,11 @@ const TodoTool = createTool({
|
|
|
365
464
|
text: z.string().optional(),
|
|
366
465
|
id: z.number().optional(),
|
|
367
466
|
}),
|
|
368
|
-
handler: async ({ action, text, id }) => {
|
|
467
|
+
handler: async ({ action, text, id }, ctx) => {
|
|
369
468
|
if (action === "add") todos.push({ id: todos.length, text, done: false });
|
|
370
469
|
if (action === "complete") todos[id!].done = true;
|
|
371
470
|
return [{ type: "text", text: "Done." }];
|
|
372
471
|
},
|
|
373
|
-
// render() injects live state into the model's context every tick
|
|
374
472
|
render: () => (
|
|
375
473
|
<Section id="todos" audience="model">
|
|
376
474
|
Current todos: {JSON.stringify(todos)}
|
|
@@ -379,15 +477,45 @@ const TodoTool = createTool({
|
|
|
379
477
|
});
|
|
380
478
|
```
|
|
381
479
|
|
|
382
|
-
|
|
480
|
+
Everything is dual-use — tools and models work as JSX components in the tree _and_ as direct function calls:
|
|
481
|
+
|
|
482
|
+
```tsx
|
|
483
|
+
// JSX — in the component tree
|
|
484
|
+
<Search />
|
|
485
|
+
<model temperature={0.2} />
|
|
486
|
+
|
|
487
|
+
// Direct calls — use programmatically
|
|
488
|
+
const output = await Search.run({ query: "test" });
|
|
489
|
+
const handle = await model.generate(input);
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Tool Types
|
|
493
|
+
|
|
494
|
+
Tools have execution types and intents that control routing and behavior:
|
|
383
495
|
|
|
384
|
-
|
|
496
|
+
| Execution Type | Description |
|
|
497
|
+
| -------------- | ---------------------------------------- |
|
|
498
|
+
| `SERVER` | Executes on server (default). |
|
|
499
|
+
| `CLIENT` | Executes in browser. |
|
|
500
|
+
| `MCP` | Routed to Model Context Protocol server. |
|
|
501
|
+
| `PROVIDER` | Handled by model provider natively. |
|
|
502
|
+
|
|
503
|
+
| Intent | Description |
|
|
504
|
+
| --------- | -------------------------- |
|
|
505
|
+
| `COMPUTE` | Returns data (default). |
|
|
506
|
+
| `ACTION` | Performs side effects. |
|
|
507
|
+
| `RENDER` | Produces UI/visualization. |
|
|
508
|
+
|
|
509
|
+
## Sessions
|
|
385
510
|
|
|
386
511
|
```tsx
|
|
387
512
|
const app = createApp(Agent, { model: openai({ model: "gpt-4o" }) });
|
|
388
513
|
const session = await app.session("conv-1");
|
|
389
514
|
|
|
390
|
-
const msg = (text: string) => ({
|
|
515
|
+
const msg = (text: string) => ({
|
|
516
|
+
role: "user" as const,
|
|
517
|
+
content: [{ type: "text" as const, text }],
|
|
518
|
+
});
|
|
391
519
|
|
|
392
520
|
await session.send({ messages: [msg("Hi there!")] });
|
|
393
521
|
await session.send({ messages: [msg("Tell me a joke")] });
|
|
@@ -397,23 +525,35 @@ for await (const event of session.send({ messages: [msg("Another one")] })) {
|
|
|
397
525
|
if (event.type === "content_delta") process.stdout.write(event.delta);
|
|
398
526
|
}
|
|
399
527
|
|
|
400
|
-
session.close();
|
|
528
|
+
await session.close();
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
Sessions are long-lived conversation contexts. Each `send()` creates an **execution** (one user message → model response cycle). Each model API call within an execution is a **tick**. Multi-tick executions happen automatically with tool use.
|
|
532
|
+
|
|
401
533
|
```
|
|
534
|
+
Session
|
|
535
|
+
├── Execution 1 (user: "Hello")
|
|
536
|
+
│ └── Tick 1 → model response
|
|
537
|
+
├── Execution 2 (user: "Use calculator")
|
|
538
|
+
│ ├── Tick 1 → tool_use (calculator)
|
|
539
|
+
│ └── Tick 2 → final response
|
|
540
|
+
└── Execution 3 ...
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
Session states: `idle` → `running` → `idle` (or `closed`).
|
|
402
544
|
|
|
403
545
|
### Dynamic Model Selection
|
|
404
546
|
|
|
405
|
-
Models are JSX components — conditionally render them
|
|
547
|
+
Models are JSX components — conditionally render them:
|
|
406
548
|
|
|
407
549
|
```tsx
|
|
408
550
|
const gpt = openai({ model: "gpt-4o" });
|
|
409
551
|
const gemini = google({ model: "gemini-2.5-pro" });
|
|
410
552
|
|
|
411
553
|
function AdaptiveAgent({ task }: { task: string }) {
|
|
412
|
-
const needsCreativity = task.includes("creative");
|
|
413
|
-
|
|
414
554
|
return (
|
|
415
555
|
<>
|
|
416
|
-
{
|
|
556
|
+
{task.includes("creative") ? <gemini temperature={0.9} /> : <gpt temperature={0.2} />}
|
|
417
557
|
<System>Handle this task: {task}</System>
|
|
418
558
|
<Timeline />
|
|
419
559
|
</>
|
|
@@ -421,50 +561,238 @@ function AdaptiveAgent({ task }: { task: string }) {
|
|
|
421
561
|
}
|
|
422
562
|
```
|
|
423
563
|
|
|
424
|
-
##
|
|
564
|
+
## Execution Environments
|
|
565
|
+
|
|
566
|
+
The context window is JSX. But what _consumes_ that context — and how tool calls _execute_ — is pluggable.
|
|
567
|
+
|
|
568
|
+
An `ExecutionEnvironment` is a swappable backend that sits between the compiled context and execution. It transforms what the model sees, intercepts how tools run, and manages its own lifecycle state. Your agent code doesn't change — the environment changes the execution model underneath it.
|
|
425
569
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
570
|
+
```tsx
|
|
571
|
+
import { type ExecutionEnvironment } from "@agentick/core";
|
|
572
|
+
|
|
573
|
+
const repl: ExecutionEnvironment = {
|
|
574
|
+
name: "repl",
|
|
575
|
+
|
|
576
|
+
// The model sees command descriptions instead of tool schemas
|
|
577
|
+
prepareModelInput(compiled, tools) {
|
|
578
|
+
return { ...compiled, tools: [executeTool] };
|
|
579
|
+
},
|
|
580
|
+
|
|
581
|
+
// "execute" calls go to a sandbox; everything else runs normally
|
|
582
|
+
async executeToolCall(call, tool, next) {
|
|
583
|
+
if (call.name === "execute") return sandbox.run(call.input.code);
|
|
584
|
+
return next();
|
|
585
|
+
},
|
|
586
|
+
|
|
587
|
+
onSessionInit(session) {
|
|
588
|
+
sandbox.create(session.id);
|
|
589
|
+
},
|
|
590
|
+
onDestroy(session) {
|
|
591
|
+
sandbox.destroy(session.id);
|
|
592
|
+
},
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
const app = createApp(Agent, { model, environment: repl });
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Same agent, same JSX, different execution model. Build once — run against standard tool_use in production, a sandboxed REPL for code execution, a human-approval gateway for sensitive operations.
|
|
599
|
+
|
|
600
|
+
All hooks are optional. Without an environment, standard model → tool_use behavior applies. Environments are inherited by spawned child sessions — override per-child via `SpawnOptions`:
|
|
601
|
+
|
|
602
|
+
```tsx
|
|
603
|
+
await session.spawn(CodeAgent, { messages }, { environment: sandboxEnv });
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
## Testing
|
|
607
|
+
|
|
608
|
+
Agentick includes a full testing toolkit. Render agents, compile context, mock models, and assert on behavior — all without making real API calls.
|
|
609
|
+
|
|
610
|
+
### `renderAgent` — Full Execution
|
|
611
|
+
|
|
612
|
+
Render an agent in a test environment with a mock model:
|
|
613
|
+
|
|
614
|
+
```tsx
|
|
615
|
+
import { renderAgent, cleanup } from "@agentick/core/testing";
|
|
616
|
+
import { afterEach, test, expect } from "vitest";
|
|
617
|
+
|
|
618
|
+
afterEach(cleanup);
|
|
619
|
+
|
|
620
|
+
test("research agent searches then summarizes", async () => {
|
|
621
|
+
const { send, model } = renderAgent(<ResearchAgent />);
|
|
622
|
+
|
|
623
|
+
// Queue model responses
|
|
624
|
+
model.addResponse({ text: "", toolCalls: [{ name: "search", input: { query: "quantum" } }] });
|
|
625
|
+
model.addResponse({ text: "Here's a summary of quantum computing..." });
|
|
626
|
+
|
|
627
|
+
const result = await send("What's new in quantum computing?");
|
|
628
|
+
|
|
629
|
+
expect(result.response).toContain("summary");
|
|
630
|
+
expect(model.calls).toHaveLength(2); // two ticks
|
|
631
|
+
});
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### `compileAgent` — Inspect Context
|
|
635
|
+
|
|
636
|
+
Compile an agent without executing to inspect what the model would see:
|
|
637
|
+
|
|
638
|
+
```tsx
|
|
639
|
+
test("agent includes user context in system prompt", async () => {
|
|
640
|
+
const { sections, tools } = compileAgent(<AgentWithContext userId="user-123" />);
|
|
641
|
+
|
|
642
|
+
expect(sections).toContainEqual(expect.objectContaining({ id: "user-context" }));
|
|
643
|
+
expect(tools.map((t) => t.name)).toContain("create_ticket");
|
|
644
|
+
});
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### Test Adapter
|
|
648
|
+
|
|
649
|
+
Create a mock model adapter for fine-grained control over streaming:
|
|
650
|
+
|
|
651
|
+
```tsx
|
|
652
|
+
import { createTestAdapter } from "@agentick/core/testing";
|
|
653
|
+
|
|
654
|
+
const adapter = createTestAdapter();
|
|
655
|
+
|
|
656
|
+
// Simulate streaming chunks
|
|
657
|
+
adapter.stream([
|
|
658
|
+
{ type: "text", text: "Hello " },
|
|
659
|
+
{ type: "text", text: "world!" },
|
|
660
|
+
{ type: "finish", stopReason: "end_turn" },
|
|
661
|
+
]);
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### Mocks & Helpers
|
|
665
|
+
|
|
666
|
+
| Utility | Description |
|
|
667
|
+
| ----------------------------- | ----------------------------------------------------- |
|
|
668
|
+
| `createMockApp()` | Mock app for client/transport tests. |
|
|
669
|
+
| `createMockSession()` | Mock session with send/close/abort. |
|
|
670
|
+
| `createMockExecutionHandle()` | Mock execution handle (async iterable + result). |
|
|
671
|
+
| `createTestEnvironment()` | Mock execution environment with call tracking. |
|
|
672
|
+
| `createMockCom()` | Mock COM for hook tests. |
|
|
673
|
+
| `createMockTickState()` | Mock tick state. |
|
|
674
|
+
| `createMockTickResult()` | Mock tick result for `useOnTickEnd` tests. |
|
|
675
|
+
| `makeTimelineEntry()` | Create timeline entries for assertions. |
|
|
676
|
+
| `act(fn)` / `actSync(fn)` | Execute in act context. |
|
|
677
|
+
| `waitFor(fn)` | Poll until condition is met. |
|
|
678
|
+
| `flushMicrotasks()` | Flush pending microtasks. |
|
|
679
|
+
| `createDeferred()` | Create deferred promise with external resolve/reject. |
|
|
680
|
+
|
|
681
|
+
## Terminal UI
|
|
682
|
+
|
|
683
|
+
`@agentick/tui` provides an Ink-based terminal interface for chatting with agents — locally or over the network.
|
|
684
|
+
|
|
685
|
+
```bash
|
|
686
|
+
npm install @agentick/tui
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Local — In-Process
|
|
690
|
+
|
|
691
|
+
Connect directly to an app. No server needed:
|
|
692
|
+
|
|
693
|
+
```tsx
|
|
694
|
+
import { createTUI } from "@agentick/tui";
|
|
695
|
+
import { createApp, System, Timeline } from "@agentick/core";
|
|
696
|
+
import { openai } from "@agentick/openai";
|
|
697
|
+
|
|
698
|
+
function Agent() {
|
|
699
|
+
return (
|
|
700
|
+
<>
|
|
701
|
+
<System>You are helpful.</System>
|
|
702
|
+
<Timeline />
|
|
703
|
+
</>
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const app = createApp(Agent, { model: openai({ model: "gpt-4o" }) });
|
|
708
|
+
const tui = createTUI({ app });
|
|
709
|
+
await tui.start();
|
|
466
710
|
```
|
|
467
711
|
|
|
712
|
+
### Remote — Over SSE
|
|
713
|
+
|
|
714
|
+
Connect to a running gateway or express server:
|
|
715
|
+
|
|
716
|
+
```tsx
|
|
717
|
+
const tui = createTUI({ url: "http://localhost:3000/api" });
|
|
718
|
+
await tui.start();
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### CLI
|
|
722
|
+
|
|
723
|
+
```bash
|
|
724
|
+
# Run a local agent file
|
|
725
|
+
agentick-tui --app ./my-agent.tsx
|
|
726
|
+
|
|
727
|
+
# Connect to a remote server
|
|
728
|
+
agentick-tui --url http://localhost:3000/api
|
|
729
|
+
|
|
730
|
+
# Custom export name
|
|
731
|
+
agentick-tui --app ./agents.tsx --export SalesAgent
|
|
732
|
+
|
|
733
|
+
# Custom UI component
|
|
734
|
+
agentick-tui --app ./my-agent.tsx --ui ./dashboard.tsx
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### Pluggable UI
|
|
738
|
+
|
|
739
|
+
The TUI ships with a default `Chat` component, but you can provide your own:
|
|
740
|
+
|
|
741
|
+
```tsx
|
|
742
|
+
import { createTUI, type TUIComponent } from "@agentick/tui";
|
|
743
|
+
import { useSession, useStreamingText } from "@agentick/react";
|
|
744
|
+
|
|
745
|
+
const Dashboard: TUIComponent = ({ sessionId }) => {
|
|
746
|
+
const { send } = useSession({ sessionId });
|
|
747
|
+
const { text, isStreaming } = useStreamingText({ sessionId });
|
|
748
|
+
// ... your Ink components
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
const tui = createTUI({ app, ui: Dashboard });
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
All building-block components are exported for custom UIs: `MessageList`, `StreamingMessage`, `ToolCallIndicator`, `ToolConfirmationPrompt`, `InputBar`, `ErrorDisplay`.
|
|
755
|
+
|
|
756
|
+
## React Hooks for UIs
|
|
757
|
+
|
|
758
|
+
`@agentick/react` provides hooks for building browser or terminal UIs over agent sessions. These are pure React — no browser APIs — so they work in both React DOM and Ink.
|
|
759
|
+
|
|
760
|
+
```bash
|
|
761
|
+
npm install @agentick/react
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
| Hook | Description |
|
|
765
|
+
| ------------------------- | -------------------------------------------------------------- |
|
|
766
|
+
| `useClient()` | Access the Agentick client from context. |
|
|
767
|
+
| `useConnection()` | SSE connection state: `{state, isConnected, isConnecting}`. |
|
|
768
|
+
| `useSession(opts?)` | Session accessor: `{send, abort, close, subscribe, accessor}`. |
|
|
769
|
+
| `useEvents(opts?)` | Subscribe to stream events. Returns `{event, clear()}`. |
|
|
770
|
+
| `useStreamingText(opts?)` | Accumulated streaming text: `{text, isStreaming, clear()}`. |
|
|
771
|
+
| `useContextInfo(opts?)` | Context utilization info (token counts, %). |
|
|
772
|
+
|
|
773
|
+
Wrap your app in `<AgentickProvider client={client}>` to provide the client context.
|
|
774
|
+
|
|
775
|
+
## Packages
|
|
776
|
+
|
|
777
|
+
| Package | Description |
|
|
778
|
+
| --------------------- | ----------------------------------------------------------------- |
|
|
779
|
+
| `@agentick/core` | Reconciler, components, hooks, tools, sessions, testing utilities |
|
|
780
|
+
| `@agentick/kernel` | Execution kernel — procedures, context, middleware, channels |
|
|
781
|
+
| `@agentick/shared` | Platform-independent types and utilities |
|
|
782
|
+
| `@agentick/openai` | OpenAI adapter (GPT-4o, o1, etc.) |
|
|
783
|
+
| `@agentick/google` | Google AI adapter (Gemini) |
|
|
784
|
+
| `@agentick/ai-sdk` | Vercel AI SDK adapter (any provider) |
|
|
785
|
+
| `@agentick/gateway` | Multi-app server with auth, routing, and channels |
|
|
786
|
+
| `@agentick/express` | Express.js integration |
|
|
787
|
+
| `@agentick/nestjs` | NestJS integration |
|
|
788
|
+
| `@agentick/client` | TypeScript client for gateway connections |
|
|
789
|
+
| `@agentick/react` | React hooks for building UIs over sessions |
|
|
790
|
+
| `@agentick/tui` | Terminal UI — Ink-based chat interface for local or remote agents |
|
|
791
|
+
| `@agentick/devtools` | Fiber tree inspector, tick scrubber, token tracker |
|
|
792
|
+
| `@agentick/cli` | CLI for running agents |
|
|
793
|
+
| `@agentick/server` | Server utilities |
|
|
794
|
+
| `@agentick/socket.io` | Socket.IO transport |
|
|
795
|
+
|
|
468
796
|
## Adapters
|
|
469
797
|
|
|
470
798
|
Three built-in, same interface. Or build your own — implement `prepareInput`, `mapChunk`, `execute`, and `executeStream`. See [`packages/adapters/README.md`](packages/adapters/README.md).
|
|
@@ -479,14 +807,50 @@ const gemini = google({ model: "gemini-2.5-pro" });
|
|
|
479
807
|
const sdk = aiSdk({ model: yourAiSdkModel });
|
|
480
808
|
```
|
|
481
809
|
|
|
810
|
+
Adapters return a `ModelClass` — callable _and_ a JSX component:
|
|
811
|
+
|
|
812
|
+
```tsx
|
|
813
|
+
// As JSX — configure model in the component tree
|
|
814
|
+
<gpt temperature={0.2} maxTokens={1000} />;
|
|
815
|
+
|
|
816
|
+
// As function — call programmatically
|
|
817
|
+
const handle = await gpt.generate(input);
|
|
818
|
+
```
|
|
819
|
+
|
|
482
820
|
## DevTools
|
|
483
821
|
|
|
822
|
+
### Agentick DevTools
|
|
823
|
+
|
|
484
824
|
```tsx
|
|
485
825
|
const app = createApp(Agent, { model, devTools: true });
|
|
486
826
|
```
|
|
487
827
|
|
|
488
828
|
Fiber tree inspector, tick-by-tick scrubber, token usage tracking, real-time execution timeline. Record full sessions for replay with `session({ recording: 'full' })`.
|
|
489
829
|
|
|
830
|
+
### React DevTools
|
|
831
|
+
|
|
832
|
+
Agentick is built on `react-reconciler` — the same foundation as React DOM and React Native. This means [React DevTools](https://github.com/facebook/react/tree/main/packages/react-devtools) works out of the box. You can inspect the component tree that compiles into the model's context window, live.
|
|
833
|
+
|
|
834
|
+
```sh
|
|
835
|
+
npm install --save-dev react-devtools-core
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
```tsx
|
|
839
|
+
import { enableReactDevTools } from "@agentick/core";
|
|
840
|
+
|
|
841
|
+
enableReactDevTools(); // connects to standalone DevTools on port 8097
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
```sh
|
|
845
|
+
# Terminal 1: start React DevTools
|
|
846
|
+
npx react-devtools
|
|
847
|
+
|
|
848
|
+
# Terminal 2: run your agent
|
|
849
|
+
node my-agent.js
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
You'll see the full component tree — `<System>`, `<Timeline>`, `<Section>`, your custom components, tools — in the same inspector you use for web and mobile apps. Inspect props, watch state changes between ticks, and see exactly what compiles into the context window.
|
|
853
|
+
|
|
490
854
|
## Gateway
|
|
491
855
|
|
|
492
856
|
Deploy multiple apps behind a single server with auth, routing, and channel adapters:
|
|
@@ -501,23 +865,6 @@ const gateway = createGateway({
|
|
|
501
865
|
});
|
|
502
866
|
```
|
|
503
867
|
|
|
504
|
-
## Quick Start
|
|
505
|
-
|
|
506
|
-
```bash
|
|
507
|
-
npm install agentick @agentick/openai zod
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
**TypeScript config** — add to `tsconfig.json`:
|
|
511
|
-
|
|
512
|
-
```json
|
|
513
|
-
{
|
|
514
|
-
"compilerOptions": {
|
|
515
|
-
"jsx": "react-jsx",
|
|
516
|
-
"jsxImportSource": "react"
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
```
|
|
520
|
-
|
|
521
868
|
## License
|
|
522
869
|
|
|
523
870
|
MIT
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentick",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "The component framework for AI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
7
7
|
"ai",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"jsx",
|
|
10
10
|
"react"
|
|
11
11
|
],
|
|
12
|
-
"license": "
|
|
12
|
+
"license": "MIT",
|
|
13
13
|
"author": "Ryan Lindgren",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@agentick/
|
|
35
|
-
"@agentick/
|
|
36
|
-
"@agentick/guardrails": "0.2.
|
|
34
|
+
"@agentick/agent": "0.3.0",
|
|
35
|
+
"@agentick/core": "0.3.0",
|
|
36
|
+
"@agentick/guardrails": "0.2.1"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsc -p tsconfig.build.json",
|