@theokit/sdk 4.2.6 → 4.2.8
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/CHANGELOG.md +12 -0
- package/claude-template/AGENTS.md +73 -55
- package/claude-template/CLAUDE.md +16 -1
- package/claude-template/dot-claude/rules/theokit-conventions.md +2 -3
- package/claude-template/dot-claude/skills/theokit-agent-core/SKILL.md +3 -3
- package/claude-template/dot-claude/skills/theokit-auth/SKILL.md +102 -0
- package/claude-template/dot-claude/skills/theokit-client/SKILL.md +58 -0
- package/claude-template/dot-claude/skills/theokit-compaction/SKILL.md +102 -0
- package/claude-template/dot-claude/skills/theokit-concurrency/SKILL.md +68 -0
- package/claude-template/dot-claude/skills/theokit-filesystem/SKILL.md +74 -0
- package/claude-template/dot-claude/skills/theokit-messages/SKILL.md +58 -0
- package/claude-template/dot-claude/skills/theokit-models/SKILL.md +79 -0
- package/claude-template/dot-claude/skills/theokit-path-safety/SKILL.md +60 -0
- package/claude-template/dot-claude/skills/theokit-persistence/SKILL.md +85 -0
- package/claude-template/dot-claude/skills/theokit-project/SKILL.md +55 -0
- package/claude-template/dot-claude/skills/theokit-retry/SKILL.md +50 -0
- package/claude-template/dot-claude/skills/theokit-sandbox/SKILL.md +93 -0
- package/claude-template/dot-claude/skills/theokit-sanitize/SKILL.md +66 -0
- package/claude-template/dot-claude/skills/theokit-skills/SKILL.md +68 -0
- package/claude-template/dot-claude/skills/theokit-subagents/SKILL.md +109 -0
- package/claude-template/dot-claude/skills/theokit-subscriptions/SKILL.md +6 -6
- package/claude-template/dot-claude/skills/theokit-task-store/SKILL.md +75 -0
- package/claude-template/dot-claude/skills/theokit-tools/SKILL.md +9 -9
- package/package.json +1 -1
- package/claude-template/dot-claude/skills/theokit-rag/SKILL.md +0 -226
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
user-invocable: false
|
|
3
|
+
description: Discover SKILL.md packs and render the <skills> block with @theokit/sdk/skills, plus enabling skills on an agent.
|
|
4
|
+
paths:
|
|
5
|
+
- "**/*skill*"
|
|
6
|
+
- "**/*Skill*"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# TheoKit SDK -- Skills
|
|
10
|
+
|
|
11
|
+
`discoverSkills` walks a directory for `<dir>/<name>/SKILL.md` packs, parses strict YAML frontmatter (`name`/`description` required; `category`/`dependencies` optional), skips malformed skills and symlink escapes, and NEVER throws (a missing/unreadable/non-directory path yields `[]`). `buildSkillsBlock` renders the prompt-injection-safe `<skills>` system-prompt block from the discovered list. These are the same primitives the SDK runtime uses internally for `.theokit/skills` discovery.
|
|
12
|
+
|
|
13
|
+
## Import
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { discoverSkills, buildSkillsBlock } from "@theokit/sdk/skills";
|
|
17
|
+
import type { Skill, DiscoverSkillsOptions, InvalidSkillInfo } from "@theokit/sdk/skills";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Signatures
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
function discoverSkills(dir: string, options?: DiscoverSkillsOptions): Promise<Skill[]>;
|
|
24
|
+
function buildSkillsBlock(
|
|
25
|
+
skills: ReadonlyArray<{ name: string; description: string }>,
|
|
26
|
+
): string | undefined; // undefined for an empty list
|
|
27
|
+
|
|
28
|
+
interface Skill {
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
source: string; // absolute path to the discovered SKILL.md
|
|
32
|
+
category?: string;
|
|
33
|
+
dependencies?: string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface DiscoverSkillsOptions {
|
|
37
|
+
onInvalidSkill?: (info: InvalidSkillInfo) => void; // called per malformed SKILL.md
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Discover and render
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
const skills = await discoverSkills(".theokit/skills", {
|
|
45
|
+
onInvalidSkill: (info: InvalidSkillInfo) =>
|
|
46
|
+
console.warn(`skipped ${info.name}: ${info.code} — ${info.message}`),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// readdir order is OS-dependent; sort for a stable block
|
|
50
|
+
skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
51
|
+
|
|
52
|
+
const block = buildSkillsBlock(skills); // string | undefined
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Skill packs live at `.theokit/skills/<name>/SKILL.md`
|
|
56
|
+
|
|
57
|
+
Each pack is a directory with a `SKILL.md` whose frontmatter has `name` + `description`. Enable specific packs on an agent by name:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { Agent } from "@theokit/sdk";
|
|
61
|
+
|
|
62
|
+
const agent = await Agent.create({
|
|
63
|
+
apiKey: process.env.THEOKIT_API_KEY!,
|
|
64
|
+
model: { id: "google/gemini-2.0-flash-001" },
|
|
65
|
+
local: { cwd: process.cwd() },
|
|
66
|
+
skills: { enabled: ["research", "code-review"] },
|
|
67
|
+
});
|
|
68
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
user-invocable: false
|
|
3
|
+
paths:
|
|
4
|
+
- "**/*subagent*"
|
|
5
|
+
- "**/*a2a*"
|
|
6
|
+
- "**/*delegat*"
|
|
7
|
+
description: TheoKit SDK subagents — @theokit/sdk/a2a (SubAgent.create, AgentMailbox, MessageBus, delegation hooks) and @theokit/sdk/subagents (subagentToolWhitelist, withSubagentToolScope)
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# TheoKit SubAgents
|
|
11
|
+
|
|
12
|
+
Two subpaths cover delegation. `@theokit/sdk/a2a` builds a child agent invocable as
|
|
13
|
+
a tool plus the in-process message bus; `@theokit/sdk/subagents` scopes which tools a
|
|
14
|
+
subagent may call.
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import {
|
|
18
|
+
SubAgent,
|
|
19
|
+
AgentMailbox,
|
|
20
|
+
MessageBus,
|
|
21
|
+
MaxDelegationDepthError,
|
|
22
|
+
type SubAgentSpec,
|
|
23
|
+
} from "@theokit/sdk/a2a";
|
|
24
|
+
import { subagentToolWhitelist, withSubagentToolScope } from "@theokit/sdk/subagents";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## `SubAgent.create` — delegation as a tool
|
|
28
|
+
|
|
29
|
+
`SubAgent.create(spec, parentDepth?)` returns a `CustomTool`. When the LLM invokes it,
|
|
30
|
+
a child agent runs the input as a message. Depth is tracked — exceeding
|
|
31
|
+
`maxDelegationDepth` throws `MaxDelegationDepthError`.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
const researcher = SubAgent.create({
|
|
35
|
+
name: "researcher",
|
|
36
|
+
description: "Delegate research questions to a focused child agent",
|
|
37
|
+
instructions: "You research topics and return a concise summary.",
|
|
38
|
+
model: "google/gemini-2.0-flash-001",
|
|
39
|
+
tools: [], // CustomTool[] the child may use
|
|
40
|
+
maxDelegationDepth: 3,
|
|
41
|
+
onDelegationStart: (ctx) => {
|
|
42
|
+
if (ctx.iteration > 5) return { proceed: false, rejectionReason: "too many calls" };
|
|
43
|
+
return { proceed: true };
|
|
44
|
+
},
|
|
45
|
+
onDelegationComplete: (ctx) => {
|
|
46
|
+
if (ctx.result) return { feedback: "(reviewed)" };
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const agent = await Agent.create({
|
|
51
|
+
apiKey: process.env.THEOKIT_API_KEY!,
|
|
52
|
+
model: { id: "google/gemini-2.0-flash-001" },
|
|
53
|
+
local: { cwd: process.cwd() },
|
|
54
|
+
tools: [researcher],
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`SubAgentSpec` also supports `messageFilter` (opt-in parent-context forwarding, off by
|
|
59
|
+
default so memory isolation stays the default) and `includeToolResults` (append the
|
|
60
|
+
child's tool results, otherwise text-only).
|
|
61
|
+
|
|
62
|
+
## Inline subagents on `Agent.create`
|
|
63
|
+
|
|
64
|
+
Simple cases need no `SubAgent.create` — declare them inline:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const agent = await Agent.create({
|
|
68
|
+
apiKey: process.env.THEOKIT_API_KEY!,
|
|
69
|
+
model: { id: "google/gemini-2.0-flash-001" },
|
|
70
|
+
local: { cwd: process.cwd() },
|
|
71
|
+
agents: {
|
|
72
|
+
reviewer: {
|
|
73
|
+
description: "Reviews code for bugs",
|
|
74
|
+
prompt: "You are a strict code reviewer.",
|
|
75
|
+
model: "google/gemini-2.0-flash-001",
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Tool scoping — `withSubagentToolScope`
|
|
82
|
+
|
|
83
|
+
`subagentToolWhitelist(definition)` derives the allowed tool-name `Set` (or `undefined`
|
|
84
|
+
when unscoped) from `definition.tools`. `withSubagentToolScope` runs a fn under that
|
|
85
|
+
whitelist so a `tools: ["read_file"]` subagent provably cannot call `write_file`.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const whitelist = subagentToolWhitelist({ tools: ["read_file"] }); // Set(["read_file"])
|
|
89
|
+
|
|
90
|
+
await withSubagentToolScope({ tools: ["read_file"] }, async () => {
|
|
91
|
+
// dispatch veto enforces the whitelist here
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Agent-to-agent messaging
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const bus = new MessageBus();
|
|
99
|
+
const alice = new AgentMailbox("alice", bus);
|
|
100
|
+
const bob = new AgentMailbox("bob", bus);
|
|
101
|
+
|
|
102
|
+
bob.onMessage(async (msg) => ({ type: "ack", payload: { ok: true } }));
|
|
103
|
+
|
|
104
|
+
await alice.send("bob", { type: "greet", payload: { text: "hi" } });
|
|
105
|
+
const reply = await alice.request("bob", { type: "ping", payload: null }, { timeoutMs: 1000 });
|
|
106
|
+
|
|
107
|
+
alice.dispose();
|
|
108
|
+
bob.dispose();
|
|
109
|
+
```
|
|
@@ -5,7 +5,7 @@ paths:
|
|
|
5
5
|
- "**/*sse*"
|
|
6
6
|
- "**/*websocket*"
|
|
7
7
|
- "**/*ws.*"
|
|
8
|
-
description: TheoKit SDK Subscriptions API —
|
|
8
|
+
description: TheoKit SDK Subscriptions API — Subscription.create, SSE/WebSocket transport, subscribe, tracked, resume tokens
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# TheoKit Subscriptions
|
|
@@ -13,13 +13,13 @@ description: TheoKit SDK Subscriptions API — defineSubscription, SSE/WebSocket
|
|
|
13
13
|
Typed WebSocket + W3C SSE subscriptions with opaque resume tokens. Available
|
|
14
14
|
via the `@theokit/sdk/subscription` sub-path import (not on the main barrel).
|
|
15
15
|
|
|
16
|
-
## Server side — `
|
|
16
|
+
## Server side — `Subscription.create`
|
|
17
17
|
|
|
18
18
|
```typescript
|
|
19
|
-
import {
|
|
19
|
+
import { Subscription } from "@theokit/sdk/subscription";
|
|
20
20
|
import { z } from "zod";
|
|
21
21
|
|
|
22
|
-
export default
|
|
22
|
+
export default Subscription.create({
|
|
23
23
|
input: z.object({
|
|
24
24
|
room: z.string(),
|
|
25
25
|
lastEventId: z.string().optional(),
|
|
@@ -91,11 +91,11 @@ for await (const msg of subscribe<
|
|
|
91
91
|
|
|
92
92
|
## Composing with LLM streaming
|
|
93
93
|
|
|
94
|
-
`Agent.streamObject` and `
|
|
94
|
+
`Agent.streamObject` and `Subscription.create` are independent surfaces. Call
|
|
95
95
|
`Agent.streamObject` inside a subscription handler:
|
|
96
96
|
|
|
97
97
|
```typescript
|
|
98
|
-
export default
|
|
98
|
+
export default Subscription.create({
|
|
99
99
|
input: z.object({ topic: z.string() }),
|
|
100
100
|
output: z.object({ kind: z.enum(["partial", "complete"]), text: z.string() }),
|
|
101
101
|
async *handler(input, ctx) {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
user-invocable: false
|
|
3
|
+
paths:
|
|
4
|
+
- "**/*task-store*"
|
|
5
|
+
- "**/*taskstore*"
|
|
6
|
+
- "**/*TaskStore*"
|
|
7
|
+
description: TheoKit SDK task persistence — @theokit/sdk/task-store (TaskStore interface, InMemoryTaskStore, JsonFileTaskStore, getTaskStoreFor factory)
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# TheoKit Task Store
|
|
11
|
+
|
|
12
|
+
`@theokit/sdk/task-store` is the storage layer behind the task registry. Pick
|
|
13
|
+
`InMemoryTaskStore` (transient, single-process default) or `JsonFileTaskStore` (one JSON
|
|
14
|
+
file per task under a dir; single-process invariant — v0.2 SQLite covers cross-process).
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import {
|
|
18
|
+
getTaskStoreFor,
|
|
19
|
+
InMemoryTaskStore,
|
|
20
|
+
JsonFileTaskStore,
|
|
21
|
+
type TaskStore,
|
|
22
|
+
} from "@theokit/sdk/task-store";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## The `TaskStore` interface
|
|
26
|
+
|
|
27
|
+
All methods are async. `TaskHandle` / `TaskFilter` come from the main `@theokit/sdk`
|
|
28
|
+
barrel.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
interface TaskStore {
|
|
32
|
+
insert(handle: TaskHandle): Promise<void>;
|
|
33
|
+
update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined>;
|
|
34
|
+
get(id: string): Promise<TaskHandle | undefined>;
|
|
35
|
+
list(filter: TaskFilter): Promise<TaskHandle[]>;
|
|
36
|
+
delete(id: string): Promise<boolean>;
|
|
37
|
+
evictTerminalOlderThan(epochMs: number): Promise<number>;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Factory — `getTaskStoreFor`
|
|
42
|
+
|
|
43
|
+
Discriminated on `backend`; the `json` backend auto-creates its dir (mkdir recursive).
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const memory: TaskStore = getTaskStoreFor({ backend: "memory" });
|
|
47
|
+
const onDisk: TaskStore = getTaskStoreFor({ backend: "json", dir: ".theokit/tasks" });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or construct directly:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
const store = new JsonFileTaskStore(".theokit/tasks"); // constructor(dir: string)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Reading tasks
|
|
57
|
+
|
|
58
|
+
`list` returns at most `filter.limit ?? 100` handles; `JsonFileTaskStore` hard-caps
|
|
59
|
+
loaded entries at 256 — page larger timelines via `submittedBefore`.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import type { TaskFilter } from "@theokit/sdk";
|
|
63
|
+
|
|
64
|
+
const filter: TaskFilter = { state: ["running", "queued"], kind: "run", limit: 50 };
|
|
65
|
+
const running = await store.list(filter);
|
|
66
|
+
|
|
67
|
+
for (const h of running) {
|
|
68
|
+
console.log(h.id, h.state, h.submittedAt);
|
|
69
|
+
if (h.cancelRequested) console.log(" (cross-process cancel requested)");
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`cancelRequested` is set by the CLI's cross-process best-effort cancel (EC-7); the owning
|
|
74
|
+
process polls it at checkpoints. `evictTerminalOlderThan(epochMs)` removes terminal
|
|
75
|
+
handles older than a cutoff and returns the count removed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
user-invocable: false
|
|
3
|
-
description: Custom tools,
|
|
3
|
+
description: Custom tools, Tool.create with Zod schemas, and built-in coding tools for @theokit/sdk.
|
|
4
4
|
paths:
|
|
5
5
|
- "**/*tool*"
|
|
6
6
|
- "**/*Tool*"
|
|
@@ -10,13 +10,13 @@ paths:
|
|
|
10
10
|
|
|
11
11
|
Quick reference for custom inline tools and built-in coding tools.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Tool.create (type-safe builder)
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
16
|
import { z } from "zod";
|
|
17
|
-
import {
|
|
17
|
+
import { Tool } from "@theokit/sdk";
|
|
18
18
|
|
|
19
|
-
const rollTool =
|
|
19
|
+
const rollTool = Tool.create({
|
|
20
20
|
name: "roll",
|
|
21
21
|
description: "Roll N dice with S sides each.",
|
|
22
22
|
inputSchema: z.object({
|
|
@@ -84,22 +84,22 @@ await agent.send("Use only the calculator.", {
|
|
|
84
84
|
// tools: [] -> no custom tools for this run
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
## Built-in coding tools (`@theokit/sdk
|
|
87
|
+
## Built-in coding tools (`@theokit/sdk-tools`)
|
|
88
88
|
|
|
89
|
-
Drop-in toolkit for coding agents. All tools are project-scoped and refuse sensitive files.
|
|
89
|
+
Drop-in toolkit for coding agents, shipped as the separate `@theokit/sdk-tools` package (not a `@theokit/sdk/tools` subpath). All tools are project-scoped and refuse sensitive files.
|
|
90
90
|
|
|
91
91
|
```typescript
|
|
92
|
-
import {
|
|
92
|
+
import { AgentFactory } from "@theokit/sdk";
|
|
93
93
|
import {
|
|
94
94
|
createReadFileTool,
|
|
95
95
|
createListDirTool,
|
|
96
96
|
createSearchTextTool,
|
|
97
97
|
createGitDiffTool,
|
|
98
98
|
createRunVitestTool,
|
|
99
|
-
} from "@theokit/sdk
|
|
99
|
+
} from "@theokit/sdk-tools";
|
|
100
100
|
|
|
101
101
|
const projectRoot = process.cwd();
|
|
102
|
-
const factory =
|
|
102
|
+
const factory = AgentFactory.create({
|
|
103
103
|
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
104
104
|
model: { id: "claude-sonnet-4-6" },
|
|
105
105
|
tools: [
|
package/package.json
CHANGED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
user-invocable: false
|
|
3
|
-
description: RAG primitives -- VectorRetriever, CohereReranker, text splitters from @theokit/sdk/rag.
|
|
4
|
-
paths:
|
|
5
|
-
- "**/*retriev*"
|
|
6
|
-
- "**/*rerank*"
|
|
7
|
-
- "**/*splitter*"
|
|
8
|
-
- "**/*rag*"
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# TheoKit SDK -- RAG
|
|
12
|
-
|
|
13
|
-
Quick reference for the RAG (Retrieval-Augmented Generation) sub-path at `@theokit/sdk/rag`.
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
16
|
-
|
|
17
|
-
The RAG module ships with `@theokit/sdk` -- no additional install needed.
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
import {
|
|
21
|
-
VectorRetriever,
|
|
22
|
-
CohereReranker,
|
|
23
|
-
NoopReranker,
|
|
24
|
-
splitByCharacter,
|
|
25
|
-
splitBySentence,
|
|
26
|
-
splitRecursive,
|
|
27
|
-
} from "@theokit/sdk/rag";
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Types
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
interface Document {
|
|
34
|
-
id: string;
|
|
35
|
-
text: string;
|
|
36
|
-
metadata?: Record<string, unknown>;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface Chunk {
|
|
40
|
-
text: string;
|
|
41
|
-
index: number;
|
|
42
|
-
metadata?: Record<string, unknown>;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface RetrievalResult {
|
|
46
|
-
text: string;
|
|
47
|
-
score: number;
|
|
48
|
-
metadata?: Record<string, unknown>;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
interface RankedChunk {
|
|
52
|
-
text: string;
|
|
53
|
-
score: number;
|
|
54
|
-
originalIndex: number;
|
|
55
|
-
metadata?: Record<string, unknown>;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
interface SplitOptions {
|
|
59
|
-
chunkSize: number;
|
|
60
|
-
overlap?: number;
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Interfaces
|
|
65
|
-
|
|
66
|
-
### Retriever
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
interface Retriever {
|
|
70
|
-
retrieve(query: string, options?: { topK?: number }): Promise<RetrievalResult[]>;
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Reranker
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
interface Reranker {
|
|
78
|
-
rerank(query: string, chunks: RetrievalResult[]): Promise<RankedChunk[]>;
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## VectorRetriever
|
|
83
|
-
|
|
84
|
-
Wraps any index that implements `search(query, topK)`.
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
interface VectorIndex {
|
|
88
|
-
search(query: string, topK: number): Promise<RetrievalResult[]>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
interface VectorRetrieverOptions {
|
|
92
|
-
index: VectorIndex;
|
|
93
|
-
topK?: number; // default 5
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Usage:
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
import { VectorRetriever } from "@theokit/sdk/rag";
|
|
101
|
-
|
|
102
|
-
const retriever = new VectorRetriever({
|
|
103
|
-
index: myVectorIndex,
|
|
104
|
-
topK: 10,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const results = await retriever.retrieve("How does auth work?");
|
|
108
|
-
// results: RetrievalResult[] sorted by relevance
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
The `VectorIndex` interface is the DI boundary. Consumers depend on the interface; implementations (e.g., backed by Memory's SQLite-vec or LanceDB index) depend on the index adapter.
|
|
112
|
-
|
|
113
|
-
## CohereReranker
|
|
114
|
-
|
|
115
|
-
Calls the Cohere Rerank v2 API to re-score retrieval results by relevance.
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
interface CohereRerankerOptions {
|
|
119
|
-
apiKey: string;
|
|
120
|
-
model?: string; // default "rerank-v3.5"
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Usage:
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
import { CohereReranker } from "@theokit/sdk/rag";
|
|
128
|
-
|
|
129
|
-
const reranker = new CohereReranker({
|
|
130
|
-
apiKey: process.env.COHERE_API_KEY!,
|
|
131
|
-
model: "rerank-v3.5",
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
const ranked = await reranker.rerank("auth middleware", retrievalResults);
|
|
135
|
-
// ranked: RankedChunk[] re-scored by Cohere
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## NoopReranker
|
|
139
|
-
|
|
140
|
-
Passes through results unchanged. Useful as a baseline or when reranking is not needed.
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
import { NoopReranker } from "@theokit/sdk/rag";
|
|
144
|
-
|
|
145
|
-
const reranker = new NoopReranker();
|
|
146
|
-
const ranked = await reranker.rerank(query, results);
|
|
147
|
-
// ranked === results (with originalIndex added)
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
## Text splitters
|
|
151
|
-
|
|
152
|
-
Three strategies for splitting documents into chunks. All return `Chunk[]` with `text` and `index`. Empty input returns an empty array.
|
|
153
|
-
|
|
154
|
-
### splitByCharacter
|
|
155
|
-
|
|
156
|
-
Fixed-size character windows with optional overlap.
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
import { splitByCharacter } from "@theokit/sdk/rag";
|
|
160
|
-
|
|
161
|
-
const chunks = splitByCharacter(longText, { chunkSize: 500, overlap: 50 });
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### splitBySentence
|
|
165
|
-
|
|
166
|
-
Groups sentences into chunks up to `chunkSize` characters.
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
import { splitBySentence } from "@theokit/sdk/rag";
|
|
170
|
-
|
|
171
|
-
const chunks = splitBySentence(longText, { chunkSize: 500 });
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
Splits on sentence boundaries (`.`, `!`, `?` followed by whitespace). Sentences are never broken mid-sentence.
|
|
175
|
-
|
|
176
|
-
### splitRecursive
|
|
177
|
-
|
|
178
|
-
Three-level cascading split: paragraph, then sentence, then character.
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
import { splitRecursive } from "@theokit/sdk/rag";
|
|
182
|
-
|
|
183
|
-
const chunks = splitRecursive(longText, { chunkSize: 500, overlap: 50 });
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
Algorithm:
|
|
187
|
-
1. Split by double newlines (paragraphs).
|
|
188
|
-
2. Paragraphs that exceed `chunkSize` are split by sentence.
|
|
189
|
-
3. Sentences that still exceed `chunkSize` are split by character.
|
|
190
|
-
|
|
191
|
-
This is the recommended default for most RAG use cases.
|
|
192
|
-
|
|
193
|
-
## Full RAG pipeline example
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
import { VectorRetriever, CohereReranker, splitRecursive } from "@theokit/sdk/rag";
|
|
197
|
-
|
|
198
|
-
// 1. Split documents
|
|
199
|
-
const chunks = splitRecursive(documentText, { chunkSize: 500, overlap: 50 });
|
|
200
|
-
|
|
201
|
-
// 2. Index chunks (your vector store)
|
|
202
|
-
await vectorStore.upsert(chunks.map((c, i) => ({
|
|
203
|
-
id: `doc-${i}`,
|
|
204
|
-
text: c.text,
|
|
205
|
-
embedding: await embed(c.text),
|
|
206
|
-
})));
|
|
207
|
-
|
|
208
|
-
// 3. Retrieve
|
|
209
|
-
const retriever = new VectorRetriever({ index: vectorStore, topK: 20 });
|
|
210
|
-
const results = await retriever.retrieve(userQuery);
|
|
211
|
-
|
|
212
|
-
// 4. Rerank
|
|
213
|
-
const reranker = new CohereReranker({ apiKey: process.env.COHERE_API_KEY! });
|
|
214
|
-
const ranked = await reranker.rerank(userQuery, results);
|
|
215
|
-
|
|
216
|
-
// 5. Use top results as agent context
|
|
217
|
-
const context = ranked.slice(0, 5).map((r) => r.text).join("\n\n");
|
|
218
|
-
const agent = await Agent.create({
|
|
219
|
-
systemPrompt: `Use this context:\n${context}`,
|
|
220
|
-
// ...
|
|
221
|
-
});
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## DI integration
|
|
225
|
-
|
|
226
|
-
Use `@Retriever` and `@Reranker` decorators from `@theokit/di-agent` to register RAG components in the DI container. See the theokit-di-agent skill for details.
|