@agentick/core 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 +270 -64
- package/dist/.tsbuildinfo.build +1 -1
- package/dist/agentick-instance.d.ts.map +1 -1
- package/dist/agentick-instance.js +125 -119
- package/dist/agentick-instance.js.map +1 -1
- package/dist/app/session-store.d.ts +1 -1
- package/dist/app/session-store.js +1 -1
- package/dist/app/session.d.ts +26 -17
- package/dist/app/session.d.ts.map +1 -1
- package/dist/app/session.js +222 -204
- package/dist/app/session.js.map +1 -1
- package/dist/app/types.d.ts +230 -149
- package/dist/app/types.d.ts.map +1 -1
- package/dist/com/object-model.d.ts +7 -4
- package/dist/com/object-model.d.ts.map +1 -1
- package/dist/com/object-model.js +13 -4
- package/dist/com/object-model.js.map +1 -1
- package/dist/compiler/collector.d.ts +1 -1
- package/dist/compiler/collector.js +1 -1
- package/dist/compiler/fiber-compiler.d.ts +16 -30
- package/dist/compiler/fiber-compiler.d.ts.map +1 -1
- package/dist/compiler/fiber-compiler.js +32 -72
- package/dist/compiler/fiber-compiler.js.map +1 -1
- package/dist/compiler/index.d.ts +1 -1
- package/dist/compiler/index.js +1 -1
- package/dist/compiler/scheduler.d.ts +3 -3
- package/dist/compiler/scheduler.js +4 -4
- package/dist/compiler/scheduler.js.map +1 -1
- package/dist/component/component.d.ts +6 -6
- package/dist/component/component.d.ts.map +1 -1
- package/dist/hooks/com-state.d.ts +18 -4
- package/dist/hooks/com-state.d.ts.map +1 -1
- package/dist/hooks/com-state.js +44 -15
- package/dist/hooks/com-state.js.map +1 -1
- package/dist/hooks/context-info.d.ts +2 -35
- package/dist/hooks/context-info.d.ts.map +1 -1
- package/dist/hooks/context-info.js +8 -0
- package/dist/hooks/context-info.js.map +1 -1
- package/dist/hooks/context.d.ts +2 -3
- package/dist/hooks/context.d.ts.map +1 -1
- package/dist/hooks/context.js +2 -3
- package/dist/hooks/context.js.map +1 -1
- package/dist/hooks/data.d.ts +19 -2
- package/dist/hooks/data.d.ts.map +1 -1
- package/dist/hooks/data.js +14 -3
- package/dist/hooks/data.js.map +1 -1
- package/dist/hooks/formatter-context.d.ts +1 -2
- package/dist/hooks/formatter-context.d.ts.map +1 -1
- package/dist/hooks/formatter-context.js +1 -2
- package/dist/hooks/formatter-context.js.map +1 -1
- package/dist/hooks/index.d.ts +6 -4
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +6 -2
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/message-context.d.ts +1 -1
- package/dist/hooks/message-context.js +1 -1
- package/dist/hooks/resolved.d.ts +2 -0
- package/dist/hooks/resolved.d.ts.map +1 -0
- package/dist/hooks/resolved.js +6 -0
- package/dist/hooks/resolved.js.map +1 -0
- package/dist/hooks/runtime-context.d.ts +46 -1
- package/dist/hooks/runtime-context.d.ts.map +1 -1
- package/dist/hooks/runtime-context.js +36 -1
- package/dist/hooks/runtime-context.js.map +1 -1
- package/dist/hooks/timeline.d.ts +10 -0
- package/dist/hooks/timeline.d.ts.map +1 -0
- package/dist/hooks/timeline.js +13 -0
- package/dist/hooks/timeline.js.map +1 -0
- package/dist/hooks/types.d.ts +1 -1
- package/dist/hooks/types.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/jsx/components/timeline.d.ts.map +1 -1
- package/dist/jsx/components/timeline.js +11 -11
- package/dist/jsx/components/timeline.js.map +1 -1
- package/dist/jsx/jsx-runtime.d.ts +1 -3
- package/dist/jsx/jsx-runtime.d.ts.map +1 -1
- package/dist/local-transport.d.ts +31 -0
- package/dist/local-transport.d.ts.map +1 -0
- package/dist/local-transport.js +119 -0
- package/dist/local-transport.js.map +1 -0
- package/dist/model/model.d.ts +0 -2
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js.map +1 -1
- package/dist/procedure/index.d.ts.map +1 -1
- package/dist/reconciler/host-config.d.ts +6 -5
- package/dist/reconciler/host-config.d.ts.map +1 -1
- package/dist/reconciler/host-config.js +56 -27
- package/dist/reconciler/host-config.js.map +1 -1
- package/dist/reconciler/index.d.ts +1 -1
- package/dist/reconciler/index.js +1 -1
- package/dist/reconciler/reconciler.d.ts +12 -11
- package/dist/reconciler/reconciler.d.ts.map +1 -1
- package/dist/reconciler/reconciler.js +23 -22
- package/dist/reconciler/reconciler.js.map +1 -1
- package/dist/reconciler/types.d.ts +2 -8
- package/dist/reconciler/types.d.ts.map +1 -1
- package/dist/reconciler/types.js +2 -2
- package/dist/reconciler/types.js.map +1 -1
- package/dist/renderers/types.d.ts +1 -1
- package/dist/renderers/types.js +1 -1
- package/dist/testing/act.d.ts.map +1 -1
- package/dist/testing/act.js +2 -3
- package/dist/testing/act.js.map +1 -1
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +2 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock-app.d.ts.map +1 -1
- package/dist/testing/mock-app.js +5 -15
- package/dist/testing/mock-app.js.map +1 -1
- package/dist/testing/mocks.d.ts +2 -3
- package/dist/testing/mocks.d.ts.map +1 -1
- package/dist/testing/mocks.js +2 -3
- package/dist/testing/mocks.js.map +1 -1
- package/dist/testing/render-agent.d.ts +1 -1
- package/dist/testing/render-agent.d.ts.map +1 -1
- package/dist/testing/render-agent.js +5 -5
- package/dist/testing/render-agent.js.map +1 -1
- package/dist/testing/test-environment.d.ts +122 -0
- package/dist/testing/test-environment.d.ts.map +1 -0
- package/dist/testing/test-environment.js +126 -0
- package/dist/testing/test-environment.js.map +1 -0
- package/package.json +15 -15
- package/dist/hibernation/index.d.ts +0 -126
- package/dist/hibernation/index.d.ts.map +0 -1
- package/dist/hibernation/index.js +0 -127
- package/dist/hibernation/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -40,7 +40,9 @@ function MyApp() {
|
|
|
40
40
|
// Create and run
|
|
41
41
|
const app = createApp(MyApp, { model: createOpenAIModel() });
|
|
42
42
|
const session = await app.session();
|
|
43
|
-
await session.send({
|
|
43
|
+
await session.send({
|
|
44
|
+
messages: [{ role: "user", content: [{ type: "text", text: "What is 2 + 2?" }] }],
|
|
45
|
+
}).result;
|
|
44
46
|
```
|
|
45
47
|
|
|
46
48
|
## Level 0: `createAgent` (No JSX Required)
|
|
@@ -151,11 +153,11 @@ When `maxTokens` is set, Timeline automatically compacts entries that exceed the
|
|
|
151
153
|
|
|
152
154
|
```typescript
|
|
153
155
|
interface TokenBudgetInfo {
|
|
154
|
-
maxTokens: number;
|
|
156
|
+
maxTokens: number; // configured budget
|
|
155
157
|
effectiveBudget: number; // maxTokens - headroom
|
|
156
|
-
currentTokens: number;
|
|
157
|
-
evictedCount: number;
|
|
158
|
-
isCompacted: boolean;
|
|
158
|
+
currentTokens: number; // tokens in kept entries
|
|
159
|
+
evictedCount: number; // entries dropped
|
|
160
|
+
isCompacted: boolean; // whether compaction fired
|
|
159
161
|
}
|
|
160
162
|
```
|
|
161
163
|
|
|
@@ -180,8 +182,7 @@ Group content with semantic meaning:
|
|
|
180
182
|
|
|
181
183
|
```tsx
|
|
182
184
|
<Section id="context" title="Current Context">
|
|
183
|
-
Today is {new Date().toDateString()}.
|
|
184
|
-
User is logged in as {user.name}.
|
|
185
|
+
Today is {new Date().toDateString()}. User is logged in as {user.name}.
|
|
185
186
|
</Section>
|
|
186
187
|
```
|
|
187
188
|
|
|
@@ -254,15 +255,15 @@ function MyComponent() {
|
|
|
254
255
|
const counter = useSignal(0);
|
|
255
256
|
const doubled = useComputed(() => counter() * 2, [counter]);
|
|
256
257
|
|
|
257
|
-
counter();
|
|
258
|
-
counter.set(5);
|
|
259
|
-
counter.update(v => v + 1); // update with function
|
|
260
|
-
doubled();
|
|
258
|
+
counter(); // read: 0
|
|
259
|
+
counter.set(5); // write
|
|
260
|
+
counter.update((v) => v + 1); // update with function
|
|
261
|
+
doubled(); // read: 12
|
|
261
262
|
|
|
262
263
|
// COM state (persisted across ticks, shared between components)
|
|
263
264
|
// Returns Signal<T>, NOT a tuple
|
|
264
265
|
const notes = useComState<string[]>("notes", []);
|
|
265
|
-
notes();
|
|
266
|
+
notes(); // read current value
|
|
266
267
|
notes.set(["a", "b"]); // write new value
|
|
267
268
|
}
|
|
268
269
|
```
|
|
@@ -272,7 +273,14 @@ function MyComponent() {
|
|
|
272
273
|
All lifecycle hooks follow the pattern: data first, COM (context) last.
|
|
273
274
|
|
|
274
275
|
```tsx
|
|
275
|
-
import {
|
|
276
|
+
import {
|
|
277
|
+
useOnMount,
|
|
278
|
+
useOnUnmount,
|
|
279
|
+
useOnTickStart,
|
|
280
|
+
useOnTickEnd,
|
|
281
|
+
useAfterCompile,
|
|
282
|
+
useContinuation,
|
|
283
|
+
} from "@agentick/core";
|
|
276
284
|
|
|
277
285
|
function MyComponent() {
|
|
278
286
|
// Called when component mounts
|
|
@@ -304,7 +312,7 @@ function MyComponent() {
|
|
|
304
312
|
useContinuation((result) => {
|
|
305
313
|
// Return true to continue, false to stop
|
|
306
314
|
if (result.text?.includes("<DONE>")) return false;
|
|
307
|
-
if (result.tick >= 10) return false;
|
|
315
|
+
if (result.tick >= 10) return false; // Safety limit
|
|
308
316
|
return true;
|
|
309
317
|
});
|
|
310
318
|
|
|
@@ -376,7 +384,9 @@ function Agent() {
|
|
|
376
384
|
const [temp] = useKnob("temp", 0.7, {
|
|
377
385
|
description: "Temperature",
|
|
378
386
|
group: "Model",
|
|
379
|
-
min: 0,
|
|
387
|
+
min: 0,
|
|
388
|
+
max: 2,
|
|
389
|
+
step: 0.1,
|
|
380
390
|
});
|
|
381
391
|
|
|
382
392
|
// Boolean → model sees [toggle] type
|
|
@@ -496,13 +506,13 @@ function ContextAwareComponent() {
|
|
|
496
506
|
```typescript
|
|
497
507
|
interface ContextInfo {
|
|
498
508
|
// Model identification
|
|
499
|
-
modelId: string;
|
|
500
|
-
modelName?: string;
|
|
501
|
-
provider?: string;
|
|
509
|
+
modelId: string; // "gpt-4o", "claude-3-5-sonnet", etc.
|
|
510
|
+
modelName?: string; // Human-readable name
|
|
511
|
+
provider?: string; // "openai", "anthropic", etc.
|
|
502
512
|
|
|
503
513
|
// Context limits
|
|
504
|
-
contextWindow?: number;
|
|
505
|
-
maxOutputTokens?: number;
|
|
514
|
+
contextWindow?: number; // Total context window size
|
|
515
|
+
maxOutputTokens?: number; // Max output tokens for model
|
|
506
516
|
|
|
507
517
|
// Token usage (current tick)
|
|
508
518
|
inputTokens: number;
|
|
@@ -510,7 +520,7 @@ interface ContextInfo {
|
|
|
510
520
|
totalTokens: number;
|
|
511
521
|
|
|
512
522
|
// Utilization
|
|
513
|
-
utilization?: number;
|
|
523
|
+
utilization?: number; // Percentage (0-100)
|
|
514
524
|
|
|
515
525
|
// Model capabilities
|
|
516
526
|
supportsVision?: boolean;
|
|
@@ -518,7 +528,7 @@ interface ContextInfo {
|
|
|
518
528
|
isReasoningModel?: boolean;
|
|
519
529
|
|
|
520
530
|
// Execution info
|
|
521
|
-
tick: number;
|
|
531
|
+
tick: number; // Current tick number
|
|
522
532
|
|
|
523
533
|
// Cumulative usage across all ticks
|
|
524
534
|
cumulativeUsage?: {
|
|
@@ -543,7 +553,7 @@ const contextInfoStore = createContextInfoStore();
|
|
|
543
553
|
// Provide to components
|
|
544
554
|
<ContextInfoProvider store={contextInfoStore}>
|
|
545
555
|
<MyApp />
|
|
546
|
-
</ContextInfoProvider
|
|
556
|
+
</ContextInfoProvider>;
|
|
547
557
|
|
|
548
558
|
// Update the store
|
|
549
559
|
contextInfoStore.update({
|
|
@@ -579,11 +589,7 @@ const WeatherTool = createTool({
|
|
|
579
589
|
return [{ type: "text", text: JSON.stringify(weather) }];
|
|
580
590
|
},
|
|
581
591
|
// Optional: render state to model context (receives tickState, ctx)
|
|
582
|
-
render: (tickState, ctx) =>
|
|
583
|
-
<Section id="weather-info">
|
|
584
|
-
Last checked: {lastChecked}
|
|
585
|
-
</Section>
|
|
586
|
-
),
|
|
592
|
+
render: (tickState, ctx) => <Section id="weather-info">Last checked: {lastChecked}</Section>,
|
|
587
593
|
});
|
|
588
594
|
|
|
589
595
|
// Use in your app
|
|
@@ -607,7 +613,7 @@ import { createApp } from "@agentick/core";
|
|
|
607
613
|
|
|
608
614
|
const app = createApp(MyApp, {
|
|
609
615
|
model: myModel,
|
|
610
|
-
devTools: true,
|
|
616
|
+
devTools: true, // Enable DevTools
|
|
611
617
|
});
|
|
612
618
|
```
|
|
613
619
|
|
|
@@ -620,6 +626,7 @@ const app = createApp(MyApp, {
|
|
|
620
626
|
devTools: true, // Enable DevTools emission
|
|
621
627
|
tools: [ExternalTool], // Additional tools (merged with JSX <Tool>s)
|
|
622
628
|
mcpServers: { ... }, // MCP server configs
|
|
629
|
+
environment: myEnv, // Execution environment (see below)
|
|
623
630
|
});
|
|
624
631
|
```
|
|
625
632
|
|
|
@@ -638,11 +645,17 @@ const app = createApp(MyApp, {
|
|
|
638
645
|
onError: (error) => console.error(error),
|
|
639
646
|
|
|
640
647
|
// All events (fine-grained)
|
|
641
|
-
onEvent: (event) => {
|
|
648
|
+
onEvent: (event) => {
|
|
649
|
+
/* handle any stream event */
|
|
650
|
+
},
|
|
642
651
|
|
|
643
652
|
// Send lifecycle
|
|
644
|
-
onBeforeSend: (session, input) => {
|
|
645
|
-
|
|
653
|
+
onBeforeSend: (session, input) => {
|
|
654
|
+
/* modify input */
|
|
655
|
+
},
|
|
656
|
+
onAfterSend: (session, result) => {
|
|
657
|
+
/* post-processing */
|
|
658
|
+
},
|
|
646
659
|
|
|
647
660
|
// Tool confirmation
|
|
648
661
|
onToolConfirmation: async (call, message) => {
|
|
@@ -689,18 +702,17 @@ const handle = await session.spawn(
|
|
|
689
702
|
);
|
|
690
703
|
|
|
691
704
|
// Spawn with a JSX element (props from element + input.props are merged)
|
|
692
|
-
const handle = await session.spawn(
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
);
|
|
705
|
+
const handle = await session.spawn(<Researcher query="quantum computing" />, {
|
|
706
|
+
messages: [{ role: "user", content: [{ type: "text", text: "Go" }] }],
|
|
707
|
+
});
|
|
696
708
|
```
|
|
697
709
|
|
|
698
710
|
**Parallel spawns** work with `Promise.all`:
|
|
699
711
|
|
|
700
712
|
```tsx
|
|
701
713
|
const [researchResult, factCheckResult] = await Promise.all([
|
|
702
|
-
session.spawn(Researcher, { messages }).then(h => h.result),
|
|
703
|
-
session.spawn(FactChecker, { messages }).then(h => h.result),
|
|
714
|
+
session.spawn(Researcher, { messages }).then((h) => h.result),
|
|
715
|
+
session.spawn(FactChecker, { messages }).then((h) => h.result),
|
|
704
716
|
]);
|
|
705
717
|
```
|
|
706
718
|
|
|
@@ -730,34 +742,138 @@ const DelegateTool = createTool({
|
|
|
730
742
|
- **Depth limit**: Maximum 10 levels of nesting (throws if exceeded).
|
|
731
743
|
- **Cleanup**: Children are removed from `session.children` when they complete.
|
|
732
744
|
|
|
733
|
-
### Session Persistence
|
|
745
|
+
### Session Persistence
|
|
734
746
|
|
|
735
|
-
|
|
747
|
+
Sessions auto-persist after each execution and auto-restore when accessed via `app.session(id)`.
|
|
736
748
|
|
|
737
749
|
```typescript
|
|
738
750
|
const app = createApp(MyApp, {
|
|
739
751
|
model,
|
|
740
752
|
sessions: {
|
|
741
|
-
store:
|
|
742
|
-
maxActive: 100,
|
|
743
|
-
idleTimeout: 5 * 60 * 1000,
|
|
744
|
-
autoHibernate: true, // Auto-hibernate on idle
|
|
753
|
+
store: "./data/sessions.db", // SQLite file (or ':memory:', or custom SessionStore)
|
|
754
|
+
maxActive: 100, // Max concurrent in-memory sessions
|
|
755
|
+
idleTimeout: 5 * 60 * 1000, // Evict from memory after 5 min idle
|
|
745
756
|
},
|
|
757
|
+
});
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
**How it works:**
|
|
761
|
+
|
|
762
|
+
1. After each execution, session state is auto-saved to the store (fire-and-forget — persist failures don't block execution)
|
|
763
|
+
2. When `app.session("user-123")` is called and the session isn't in memory, it's auto-restored from the store
|
|
764
|
+
3. `useComState` and `useData` values are included in snapshots by default (set `{ persist: false }` to exclude)
|
|
765
|
+
4. `maxActive` and `idleTimeout` control memory — evicted sessions can be restored from store
|
|
746
766
|
|
|
747
|
-
|
|
748
|
-
onSessionCreate: (session) => { /* ... */ },
|
|
749
|
-
onSessionClose: (sessionId) => { /* ... */ },
|
|
767
|
+
#### Snapshot Contents
|
|
750
768
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
769
|
+
A `SessionSnapshot` captures:
|
|
770
|
+
|
|
771
|
+
| Field | Type | Description |
|
|
772
|
+
| ----------- | --------------------------- | ------------------------------------------------------ |
|
|
773
|
+
| `timeline` | `COMTimelineEntry[] ∣ null` | Full conversation history |
|
|
774
|
+
| `comState` | `Record<string, unknown>` | All `useComState` values (with `persist !== false`) |
|
|
775
|
+
| `dataCache` | `Record<string, ...>` | All `useData` cached values (with `persist !== false`) |
|
|
776
|
+
| `tick` | `number` | Tick count at snapshot time |
|
|
777
|
+
| `usage` | `UsageStats` | Accumulated token usage |
|
|
778
|
+
|
|
779
|
+
#### Lifecycle Hooks
|
|
780
|
+
|
|
781
|
+
```typescript
|
|
782
|
+
const app = createApp(MyApp, {
|
|
783
|
+
model,
|
|
784
|
+
sessions: { store: "./sessions.db" },
|
|
785
|
+
|
|
786
|
+
// Before save — cancel or modify snapshot
|
|
787
|
+
onBeforePersist: (session, snapshot) => {
|
|
788
|
+
if (snapshot.tick < 2) return false; // Don't persist short sessions
|
|
789
|
+
},
|
|
790
|
+
|
|
791
|
+
// After save
|
|
792
|
+
onAfterPersist: (sessionId, snapshot) => {
|
|
793
|
+
console.log(`Saved session ${sessionId}`);
|
|
755
794
|
},
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
795
|
+
|
|
796
|
+
// Before restore — migrate old formats
|
|
797
|
+
onBeforeRestore: (sessionId, snapshot) => {
|
|
798
|
+
if (snapshot.version !== "1.0") return migrateSnapshot(snapshot);
|
|
799
|
+
},
|
|
800
|
+
|
|
801
|
+
// After restore
|
|
802
|
+
onAfterRestore: (session, snapshot) => {
|
|
803
|
+
console.log(`Restored session ${session.id} at tick ${snapshot.tick}`);
|
|
759
804
|
},
|
|
760
|
-
|
|
805
|
+
});
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
#### Restore Layers
|
|
809
|
+
|
|
810
|
+
**Layer 1 (default):** Snapshot data is auto-applied. Timeline, comState, and dataCache are restored directly. Components see their previous state via `useComState` and `useData`.
|
|
811
|
+
|
|
812
|
+
**Layer 2 (resolve):** When `resolve` is configured, auto-apply is disabled. Resolve functions control reconstruction and receive the snapshot as context. Results are available via `useResolved(key)`.
|
|
813
|
+
|
|
814
|
+
```typescript
|
|
815
|
+
const app = createApp(MyApp, {
|
|
816
|
+
model,
|
|
817
|
+
sessions: { store: "./sessions.db" },
|
|
818
|
+
|
|
819
|
+
// Layer 2: resolve controls reconstruction
|
|
820
|
+
resolve: {
|
|
821
|
+
greeting: (ctx) => `Welcome back! You were on tick ${ctx.snapshot?.tick}`,
|
|
822
|
+
userData: async (ctx) => fetchUser(ctx.sessionId),
|
|
823
|
+
},
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
// In components:
|
|
827
|
+
function MyAgent() {
|
|
828
|
+
const greeting = useResolved<string>("greeting");
|
|
829
|
+
const userData = useResolved<User>("userData");
|
|
830
|
+
// ...
|
|
831
|
+
}
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
#### Context Management vs History
|
|
835
|
+
|
|
836
|
+
The session's `_timeline` is the append-only historical log — it grows with every message. The `<Timeline>` component controls what the model _sees_ (context) via its props:
|
|
837
|
+
|
|
838
|
+
```tsx
|
|
839
|
+
// Full history → model context (default)
|
|
840
|
+
<Timeline />
|
|
841
|
+
|
|
842
|
+
// Only recent messages in context
|
|
843
|
+
<Timeline limit={20} />
|
|
844
|
+
|
|
845
|
+
// Token-budgeted context
|
|
846
|
+
<Timeline maxTokens={8000} strategy="sliding-window" headroom={500} />
|
|
847
|
+
|
|
848
|
+
// Role filtering
|
|
849
|
+
<Timeline roles={['user', 'assistant']} />
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
The `useTimeline()` hook provides direct access for advanced patterns:
|
|
853
|
+
|
|
854
|
+
```tsx
|
|
855
|
+
function MyAgent() {
|
|
856
|
+
const timeline = useTimeline();
|
|
857
|
+
|
|
858
|
+
// Read current entries
|
|
859
|
+
console.log(timeline.entries.length);
|
|
860
|
+
|
|
861
|
+
// Replace timeline (e.g., after summarization)
|
|
862
|
+
timeline.set([summaryEntry, ...recentEntries]);
|
|
863
|
+
|
|
864
|
+
// Transform timeline
|
|
865
|
+
timeline.update((entries) => entries.filter((e) => e.message.role !== "system"));
|
|
866
|
+
}
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
#### `maxTimelineEntries` — OOM Safety Net
|
|
870
|
+
|
|
871
|
+
For long-running sessions, `maxTimelineEntries` prevents unbounded memory growth by trimming the oldest entries after each tick. This is a safety net, not a context management strategy — use `<Timeline>` props for context control.
|
|
872
|
+
|
|
873
|
+
```typescript
|
|
874
|
+
const app = createApp(MyApp, {
|
|
875
|
+
model,
|
|
876
|
+
maxTimelineEntries: 500, // Keep at most 500 entries in memory
|
|
761
877
|
});
|
|
762
878
|
```
|
|
763
879
|
|
|
@@ -802,8 +918,8 @@ Apps inherit from the global `Agentick` instance by default:
|
|
|
802
918
|
import { Agentick, createApp } from "@agentick/core";
|
|
803
919
|
|
|
804
920
|
// Register global middleware
|
|
805
|
-
Agentick.use(
|
|
806
|
-
Agentick.use(
|
|
921
|
+
Agentick.use("*", loggingMiddleware);
|
|
922
|
+
Agentick.use("tool:*", authMiddleware);
|
|
807
923
|
|
|
808
924
|
// App inherits global middleware (default)
|
|
809
925
|
const app = createApp(MyApp, { model });
|
|
@@ -811,7 +927,7 @@ const app = createApp(MyApp, { model });
|
|
|
811
927
|
// Isolated app (for testing)
|
|
812
928
|
const testApp = createApp(TestApp, {
|
|
813
929
|
model,
|
|
814
|
-
inheritDefaults: false
|
|
930
|
+
inheritDefaults: false,
|
|
815
931
|
});
|
|
816
932
|
```
|
|
817
933
|
|
|
@@ -822,10 +938,10 @@ For one-off executions without session management:
|
|
|
822
938
|
```tsx
|
|
823
939
|
import { run } from "@agentick/core";
|
|
824
940
|
|
|
825
|
-
const result = await run(
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
);
|
|
941
|
+
const result = await run(<MyApp />, {
|
|
942
|
+
messages: [{ role: "user", content: [{ type: "text", text: "Hello!" }] }],
|
|
943
|
+
model: myModel,
|
|
944
|
+
});
|
|
829
945
|
```
|
|
830
946
|
|
|
831
947
|
### Choosing `run()` vs `createApp`
|
|
@@ -843,7 +959,79 @@ const result = await run(
|
|
|
843
959
|
await run(<Agent query="default" />, { props: { query: "override" }, model, messages });
|
|
844
960
|
```
|
|
845
961
|
|
|
846
|
-
`createApp` takes a component function and returns a reusable app with session management,
|
|
962
|
+
`createApp` takes a component function and returns a reusable app with session management, persistence, and middleware support.
|
|
963
|
+
|
|
964
|
+
## Execution Environments
|
|
965
|
+
|
|
966
|
+
An `ExecutionEnvironment` controls the execution backend — how compiled context reaches the model and how tool calls are routed. The default is the standard model → tool_use protocol. Swap in a different environment to change the entire execution model without touching your agent code.
|
|
967
|
+
|
|
968
|
+
```tsx
|
|
969
|
+
import { createApp, type ExecutionEnvironment } from "@agentick/core";
|
|
970
|
+
|
|
971
|
+
const replEnvironment: ExecutionEnvironment = {
|
|
972
|
+
name: "repl",
|
|
973
|
+
|
|
974
|
+
// Transform what the model sees — replace tool schemas with prose descriptions,
|
|
975
|
+
// expose a single "execute" tool, restructure sections, anything.
|
|
976
|
+
prepareModelInput(compiled, tools) {
|
|
977
|
+
const commandList = tools
|
|
978
|
+
.map((t) => `- ${t.metadata?.name}: ${t.metadata?.description}`)
|
|
979
|
+
.join("\n");
|
|
980
|
+
return {
|
|
981
|
+
...compiled,
|
|
982
|
+
tools: [executeToolSchema],
|
|
983
|
+
system: [...compiled.system, { content: `Available commands:\n${commandList}` }],
|
|
984
|
+
};
|
|
985
|
+
},
|
|
986
|
+
|
|
987
|
+
// Route tool calls — intercept "execute" to a sandbox, let others pass through.
|
|
988
|
+
async executeToolCall(call, tool, next) {
|
|
989
|
+
if (call.name === "execute") {
|
|
990
|
+
return sandbox.run(call.input.code);
|
|
991
|
+
}
|
|
992
|
+
return next();
|
|
993
|
+
},
|
|
994
|
+
|
|
995
|
+
// Session lifecycle
|
|
996
|
+
onSessionInit(session) {
|
|
997
|
+
sandbox.create(session.id);
|
|
998
|
+
},
|
|
999
|
+
onDestroy(session) {
|
|
1000
|
+
sandbox.destroy(session.id);
|
|
1001
|
+
},
|
|
1002
|
+
onPersist(session, snapshot) {
|
|
1003
|
+
return { ...snapshot, comState: { ...snapshot.comState, _sandbox: sandbox.state() } };
|
|
1004
|
+
},
|
|
1005
|
+
onRestore(session, snapshot) {
|
|
1006
|
+
sandbox.restore(snapshot.comState._sandbox);
|
|
1007
|
+
},
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
// Same agent, different execution model
|
|
1011
|
+
const app = createApp(MyAgent, { model, environment: replEnvironment });
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
The agent's JSX — its `<System>`, `<Timeline>`, `<Tool>` components — stays identical. The environment transforms how that compiled context is consumed and how tool calls execute. This means you can build one agent and run it against multiple backends: standard tool_use for production, a sandboxed REPL for code execution, a human-in-the-loop gateway for approval workflows.
|
|
1015
|
+
|
|
1016
|
+
### Interface
|
|
1017
|
+
|
|
1018
|
+
All methods are optional. Omitted methods use default behavior.
|
|
1019
|
+
|
|
1020
|
+
| Hook | Purpose | Timing |
|
|
1021
|
+
| ------------------- | --------------------------------------------------- | ------------- |
|
|
1022
|
+
| `prepareModelInput` | Transform compiled context before the model sees it | Per tick |
|
|
1023
|
+
| `executeToolCall` | Intercept, transform, or replace tool execution | Per tool call |
|
|
1024
|
+
| `onSessionInit` | Set up per-session resources (sandbox, workspace) | Once |
|
|
1025
|
+
| `onPersist` | Add environment state to session snapshot | Per save |
|
|
1026
|
+
| `onRestore` | Restore environment state from snapshot | Once |
|
|
1027
|
+
| `onDestroy` | Clean up resources | Once |
|
|
1028
|
+
|
|
1029
|
+
### Use Cases
|
|
1030
|
+
|
|
1031
|
+
- **REPL/Code Execution**: Replace tool schemas with command descriptions, route `execute` calls to a sandboxed runtime, persist sandbox state across sessions.
|
|
1032
|
+
- **Human-in-the-Loop**: Transform tool calls into approval requests, gate execution on human confirmation, log decisions.
|
|
1033
|
+
- **Sandboxing**: Run tools in isolated containers, inject security boundaries, audit tool invocations.
|
|
1034
|
+
- **Testing**: Intercept specific tools to return canned responses, track all lifecycle calls for assertions. See `createTestEnvironment()` in `@agentick/core/testing`.
|
|
847
1035
|
|
|
848
1036
|
## DevTools Integration
|
|
849
1037
|
|
|
@@ -867,9 +1055,27 @@ For debugging the reconciler itself with React DevTools:
|
|
|
867
1055
|
import { enableReactDevTools } from "@agentick/core";
|
|
868
1056
|
|
|
869
1057
|
// Before creating sessions
|
|
870
|
-
enableReactDevTools();
|
|
1058
|
+
enableReactDevTools(); // Connects to npx react-devtools on port 8097
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
## Local Transport
|
|
1062
|
+
|
|
1063
|
+
`createLocalTransport(app)` bridges an in-process `App` to the `ClientTransport` interface. This enables `@agentick/client` (and `@agentick/react` hooks) to work with a local app without any network layer.
|
|
1064
|
+
|
|
1065
|
+
```typescript
|
|
1066
|
+
import { createApp } from "@agentick/core";
|
|
1067
|
+
import { createLocalTransport } from "@agentick/core";
|
|
1068
|
+
import { createClient } from "@agentick/client";
|
|
1069
|
+
|
|
1070
|
+
const app = createApp(MyAgent, { model });
|
|
1071
|
+
const transport = createLocalTransport(app);
|
|
1072
|
+
const client = createClient({ baseUrl: "local://", transport });
|
|
871
1073
|
```
|
|
872
1074
|
|
|
1075
|
+
The transport is always "connected" — there's no network. `send()` delegates to `app.send()` and streams `SessionExecutionHandle` events as `TransportEventData`. Used by `@agentick/tui` for local agent mode.
|
|
1076
|
+
|
|
1077
|
+
See [`packages/shared/src/transport.ts`](../shared/src/transport.ts) for the `ClientTransport` interface.
|
|
1078
|
+
|
|
873
1079
|
## License
|
|
874
1080
|
|
|
875
1081
|
MIT
|