@theokit/sdk 4.4.2 → 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,260 @@
1
+ # Theo Harness Capability Map
2
+
3
+ A single navigable index of **what the Theo harness gives you** — every public
4
+ primitive with its real import path, a one-line description, and a minimal
5
+ example. The goal: find `compactTranscript`, `buildRepoMap`, `isTransientError`,
6
+ or any other capability **without reading source**.
7
+
8
+ - Every `import` line below resolves against the published packages — verified by the committed resolve-check `scripts/check-capability-map.mjs` (run it after `pnpm --filter @theokit/sdk build`; intended to be wired into CI).
9
+ - Public `@theokit/sdk/*` and `@theokit/sdk-tools` sub-paths are **semver-protected**. The `@theokit/sdk/internal/*` sub-paths are **semver-exempt** (may break) — prefer the public homes listed here.
10
+ - The exported TypeScript types are the canonical, exhaustive API contract; this map is the discovery front-door, organized by capability.
11
+
12
+ > Scope: this map covers the **Harness** packages in this repo — `@theokit/sdk` and `@theokit/sdk-tools`. Capabilities that live in sibling repos (UI, the `theokit` HTTP framework, ORM, memory adapters) are listed under [Out-of-repo capabilities](#out-of-repo-capabilities) with a pointer.
13
+
14
+ ---
15
+
16
+ ## Agent runtime core — `@theokit/sdk`
17
+
18
+ ```typescript
19
+ import {
20
+ Agent, // Agent.create({ model, apiKey, tools, ... }) → agent; agent.send(msg) → run
21
+ AgentFactory, // AgentFactory.create(defaults) → reusable agent builder (SE36)
22
+ AgentBuilder, // fluent agent construction
23
+ Tool, // Tool.create({ name, description, inputSchema, handler }) → CustomTool (SE36)
24
+ Plugin, // Plugin.create({ hooks }) → Plugin (pre/post tool-call, etc.) (SE36)
25
+ Provider, // Provider.create(...) → custom LLM provider (SE36)
26
+ PermissionEngine, // permission decisions for tool calls (default-allow; pluggable)
27
+ PermissionPlugin, // PermissionPlugin.create(...) → wrap a permission policy as a Plugin (SE36)
28
+ createCounterBudgetTracker, // createCounterBudgetTracker({ maxIterations }) → BudgetTracker (enforced step cap)
29
+ Budget, UsageAccumulator, // usage/cost accounting
30
+ computeCost, normalizeUsage, getPricingEntry, // cost helpers (never 0 when pricing unknown)
31
+ Squad, Task, // Squad.create(...) multi-agent + task primitives (SE36)
32
+ Theokit, Cron, // top-level namespaces (Theokit.models.list(), Cron.create(...))
33
+ } from "@theokit/sdk";
34
+
35
+ // Step cap fail-closed in one line:
36
+ const agent = await Agent.create({
37
+ model: { id: "anthropic/claude-3-5-sonnet" },
38
+ apiKey: process.env.OPENROUTER_API_KEY,
39
+ budgetTracker: createCounterBudgetTracker({ maxIterations: 50 }), // stops + sets RunResult.stoppedAtIterationLimit
40
+ });
41
+ ```
42
+
43
+ ## Errors & transient classification — `@theokit/sdk/errors`
44
+
45
+ ```typescript
46
+ import {
47
+ isTransientError, // isTransientError(err) → boolean (429/5xx/network/ECONNRESET on TheokitAgentError) — the single retry taxonomy
48
+ TheokitAgentError, // base error class (all SDK errors extend it)
49
+ RateLimitError, NetworkError, AuthenticationError, ConfigurationError, // typed subclasses
50
+ } from "@theokit/sdk/errors"; // NOTE: import isTransientError + classes from this subpath OR the barrel — use ONE entry consistently (cross-entry instanceof is class-identity sensitive)
51
+
52
+ if (isTransientError(err)) await retry();
53
+ ```
54
+
55
+ ## Retry — `@theokit/sdk/retry`
56
+
57
+ ```typescript
58
+ import { Retry } from "@theokit/sdk/retry";
59
+ // Retry.create(fn, { retries, isRetryable, initialDelayMs, maxDelayMs, backoffMultiplier, sleep, signal }) (SE36)
60
+ const res = await Retry.create(() => callApi(), { retries: 3, isRetryable: isTransientError, initialDelayMs: 200 });
61
+ ```
62
+
63
+ ## Concurrency — `@theokit/sdk/concurrency`
64
+
65
+ ```typescript
66
+ import { mapWithConcurrency, Semaphore } from "@theokit/sdk/concurrency";
67
+ // mapWithConcurrency(items, concurrency, fn) → ordered results, bounded pool; Semaphore.create(n) → gate (SE36)
68
+ const out = await mapWithConcurrency(tasks, 8, async (t) => run(t));
69
+ ```
70
+
71
+ ## Context & compaction — `@theokit/sdk/compaction`
72
+
73
+ ```typescript
74
+ import {
75
+ estimateTokens, // estimateTokens(text) → ceil(len/4) tokenizer-free estimate
76
+ shouldCompact, // shouldCompact({ estimated, contextWindow, buffer }) → boolean (pre-call gate)
77
+ compactTranscript, // compactTranscript(messages, { keepRecent, summarize }) → CompressibleMessage[]
78
+ buildCheckpoint, // buildCheckpoint(label?) → a checkpoint system turn
79
+ filterFromLatestCheckpoint,// filterFromLatestCheckpoint(messages) → turns after the latest checkpoint
80
+ CHECKPOINT_MARKER, // sentinel prefix for checkpoint turns
81
+ isContextOverflowError, // isContextOverflowError(err) → boolean (typed context_too_long, not regex)
82
+ } from "@theokit/sdk/compaction";
83
+
84
+ if (shouldCompact({ estimated: estimateTokens(joined), contextWindow: 200_000, buffer: 20_000 })) {
85
+ messages = await compactTranscript(messages, { keepRecent: 6, summarize });
86
+ }
87
+ ```
88
+
89
+ ## SDKMessage readers — `@theokit/sdk/messages`
90
+
91
+ ```typescript
92
+ import { assistantText, extractToolUses, costAmountUsd } from "@theokit/sdk/messages";
93
+ // assistantText(msg) → string|undefined; extractToolUses(msg) → tool calls; costAmountUsd(msg) → number|undefined (never 0 when unknown)
94
+ for await (const msg of run.stream()) { const text = assistantText(msg); if (text) ui.append(text); }
95
+ ```
96
+
97
+ ## Model catalog — `@theokit/sdk/models`
98
+
99
+ ```typescript
100
+ import { resolveModelCapabilities, parseModelId, humanizeModelName, toModelOption } from "@theokit/sdk/models";
101
+ // resolveModelCapabilities(modelId) → { contextWindow, ... } | undefined (sync, offline)
102
+ const caps = resolveModelCapabilities("anthropic/claude-3-5-sonnet");
103
+ const label = humanizeModelName("openai/gpt-4o"); // → "GPT-4o"
104
+ ```
105
+
106
+ ## Skills discovery — `@theokit/sdk/skills`
107
+
108
+ ```typescript
109
+ import { discoverSkills, buildSkillsBlock } from "@theokit/sdk/skills";
110
+ // discoverSkills(dir) → Skill[] from an arbitrary directory convention; buildSkillsBlock(skills) → <skills> prompt block
111
+ const block = buildSkillsBlock(await discoverSkills(".theo/skills"));
112
+ ```
113
+
114
+ ## Project instructions — `@theokit/sdk/project`
115
+
116
+ ```typescript
117
+ import { readProjectInstructions, writeProjectInstructions } from "@theokit/sdk/project";
118
+ // readProjectInstructions(cwd, { filename, scope, maxBytes }) → string (git-root-walk); writeProjectInstructions(...) atomic + path-guarded
119
+ const instructions = readProjectInstructions(process.cwd(), { filename: "THEO.md" });
120
+ ```
121
+
122
+ ## Subagent tool scoping — `@theokit/sdk/subagents`
123
+
124
+ ```typescript
125
+ import { withSubagentToolScope, subagentToolWhitelist } from "@theokit/sdk/subagents";
126
+ // restrict a subagent to a tool whitelist (enforced, not prompt-soft)
127
+ const scoped = withSubagentToolScope(agentDef, ["read_file", "search_text"]);
128
+ ```
129
+
130
+ ## Path safety — `@theokit/sdk/path-safety`
131
+
132
+ ```typescript
133
+ import { safePathJoin, sanitizeIdentifier, safeFilenameForId, assertNoSymlinkEscape, isForbiddenPath } from "@theokit/sdk/path-safety";
134
+ // safeFilenameForId(id, { maxLen }) → deterministic safe filename for any opaque id; safePathJoin(root, ...parts) → path that cannot escape root
135
+ const file = safePathJoin(root, safeFilenameForId(sessionId) + ".md");
136
+ ```
137
+
138
+ ## Persistence — `@theokit/sdk/persistence`
139
+
140
+ ```typescript
141
+ import {
142
+ appendJsonl, // appendJsonl(path, record) → append one \n-terminated JSON line (mkdirs parent; crash-safe per-line flush)
143
+ readJsonlIds, // readJsonlIds(path, keyFn) → Set<string> of done keys (tolerates a trailing partial line) — resume
144
+ loadJsonl, // loadJsonl(path, { map? }) → rows (throws JsonlParseError with line number); also at @theokit/sdk/eval
145
+ replaceFileAtomic, // replaceFileAtomic(path, content) → Promise<void> (temp + fsync + 0o600 + rename; never torn)
146
+ atomicWriteText, atomicWriteJson,
147
+ withFileLock, // withFileLock(path, fn) → run an async critical section under a cross-process lock
148
+ openSqliteResilient, applyWalWithFallback, isCorruptionError, // resilient SQLite bootstrap
149
+ } from "@theokit/sdk/persistence";
150
+
151
+ // Durable, resumable batch run:
152
+ appendJsonl("out/preds.jsonl", { id, patch });
153
+ const done = readJsonlIds("out/preds.jsonl", (r) => (r.patch ? String(r.id) : undefined));
154
+ ```
155
+
156
+ ## Eval & sandbox — `@theokit/sdk/eval`, `@theokit/sdk/sandbox`
157
+
158
+ ```typescript
159
+ import { Eval, Scorers, assertEval, EvalThresholdError, loadJsonl, captureArtifact } from "@theokit/sdk/eval";
160
+ import { LocalSandbox, provisionRepo, RepoProvisionError } from "@theokit/sdk/sandbox";
161
+ // Scorers: exactMatch, containsExpected, regex, jsonShape(zod), llmJudge, verifyGate,
162
+ // levenshtein({ threshold?, caseSensitive? }), numericDiff({ tolerance? }),
163
+ // embeddingSimilarity({ apiKey?, model?, threshold?, embed? }) ← SE41
164
+ const ev = Eval.create({ name: "qa", dataset, scorers: [Scorers.levenshtein({ threshold: 0.8 })], agent, trials: 3 });
165
+ const run = await ev.run();
166
+ // CI gate (SE41): throws EvalThresholdError with every unmet threshold, else void.
167
+ assertEval(run, { minMeanScore: 0.8, minPassRatio: 0.9, maxErrorRatio: 0, perScorer: { "levenshtein(>=0.8)": 0.7 } });
168
+ ```
169
+
170
+ ## Workflow, task store, cron, subscription, A2A, client
171
+
172
+ ```typescript
173
+ import { Workflow, WorkflowBuilder, agentStep, fn } from "@theokit/sdk/workflow"; // typed multi-step workflows
174
+ import { InMemoryTaskStore, JsonFileTaskStore, getTaskStoreFor } from "@theokit/sdk/task-store"; // durable task persistence
175
+ import { Cron } from "@theokit/sdk/cron"; // Cron.create(...) scheduled agent runs
176
+ import { Subscription, subscribe, tracked } from "@theokit/sdk/subscription"; // Subscription.create(...) streaming + resume tokens (SE36)
177
+ import { AgentMailbox, MessageBus, SubAgent } from "@theokit/sdk/a2a"; // SubAgent.create(...) agent-to-agent messaging (SE36)
178
+ import { TheoKitClient } from "@theokit/sdk/client"; // typed client for the cloud runtime
179
+ ```
180
+
181
+ ## Server-side (cloud runtime) — `@theokit/sdk/server/*`
182
+
183
+ For building the server that backs the cloud runtime (OAuth callbacks + a canonical error envelope across HTTP surfaces).
184
+
185
+ ```typescript
186
+ import { Auth, validateReturnTo } from "@theokit/sdk/server/auth";
187
+ // Auth.create({ providers, ... }) → auth handler (SE36); validateReturnTo(url, allowlist) → safe redirect target
188
+ // + typed errors: AuthCallbackError, AuthCancelledError, AuthConfigError, AuthProviderNotFoundError, AuthSecretTooShortError
189
+
190
+ import { toEnvelope, fromEnvelope } from "@theokit/sdk/server/errors-envelope";
191
+ // toEnvelope(err) → a canonical wire error envelope; fromEnvelope(envelope) → a typed error (cross-surface error contract)
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Code-assistant toolbox — `@theokit/sdk-tools`
197
+
198
+ ```typescript
199
+ import {
200
+ // File / shell / search tools (factories returning CustomTool):
201
+ createReadFileTool, createWriteFileTool, createEditFileTool, createApplyPatchTool,
202
+ createListDirTool, createGlobTool, createSearchTextTool, createGitDiffTool,
203
+ createShellTool, createRunVitestTool, createTodolistTool, createPlanModeTool, createQuestionTool,
204
+ // Web fetch + search (SSRF-guarded):
205
+ createWebFetchTool, createWebSearchTool, createBraveWebSearchAdapter,
206
+ // SSRF guard primitives:
207
+ isBlockedIp, // isBlockedIp(ip) → boolean (private/loopback/link-local/metadata)
208
+ resolveAndScreen, // resolveAndScreen(host) → screened addresses (throws SsrfBlockedError)
209
+ screenedFetch, // screenedFetch(url, opts) → Response (redirect:'manual' + re-screen each hop)
210
+ // Catastrophic shell screen:
211
+ catastrophicShellReason, // catastrophicShellReason(cmd) → reason | null (rm -rf /, curl|sh, force-push, exfil, ...)
212
+ denyCatastrophicCommands, commandDenialReason, isCommandAllowed,
213
+ // Context builders:
214
+ buildRepoMap, // buildRepoMap(cwd, { budget, ignore }) → directory map string (orient the LLM in one call)
215
+ buildEnvContext, // buildEnvContext(cwd) → env/git orientation block
216
+ // ACI / tool-result ergonomics:
217
+ withDescription, renderToolList, withToolResultGuidance, injectGuidance, withDefaultGuidance,
218
+ todoItemsToPlanNodes, // todoItemsToPlanNodes(items) → PlanNode[]
219
+ createSessionArtifactStore, // durable plan/artifact persistence
220
+ // Output formatting + truncation:
221
+ formatDiff, formatCode, formatError, formatFileList, truncateOutput,
222
+ } from "@theokit/sdk-tools";
223
+
224
+ const map = buildRepoMap(process.cwd(), { budget: 4000 }); // codebase orientation in one call
225
+ const reason = catastrophicShellReason("rm -rf /"); // → a non-null deny reason
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Out-of-repo capabilities
231
+
232
+ These GAP_AUDIT primitives target packages that live in **sibling repos**, not in `theokit-sdk`. Look for them there:
233
+
234
+ | Capability | Target package | Where |
235
+ |---|---|---|
236
+ | `AgentToolRenderer`, `ToolCallCard`, `AgentStream`, `useStickToBottom`, tool-result→props adapters, `TokenUsageChart` | `@theokit/ui` | `theokit-tools/theo-ui/` (React presentation library) |
237
+ | `liveText`/`error` on the stream hook, `foldAgentToolCards`/`useAgentToolCards` | `theokit/client` | `theokit-tools/theokit/` (the `theokit` framework, `theokit/client` subpath) |
238
+ | `defineHealthRoute`/`defineReadyRoute`, 404 typed exceptions in `defineRoute`, programmatic boot (`theokit/boot`) | `theokit` (packages/theo) | `theokit-tools/theokit/` |
239
+ | `createRepository(db, table)` (sync-aware Drizzle CRUD) | `@theokit/orm` | separate package (not yet installed by default) |
240
+ | `createCategorizedMemory({ categories })` (typed markdown memory) | `@theokit/sdk-memory` | `packages/sdk-memory/` |
241
+ | Honest-null cost aggregation | `@theokit/sdk-budget` | `packages/sdk-budget/` |
242
+ | `catastrophicShellReason` + `denyCatastrophicCommands` composition in the permission path | `@theokit/agents` | sibling repo (the mechanism is public in `@theokit/sdk-tools`) |
243
+
244
+ ---
245
+
246
+ ## Behavior wired, but not a standalone export
247
+
248
+ A few GAP_AUDIT items are runtime behaviors the harness performs internally — there is no single exported symbol to import. The relevant public pieces are linked:
249
+
250
+ - **Enforced iteration cap.** The cap is enforced by `createCounterBudgetTracker({ maxIterations })` (above) + `RunResult.stoppedAtIterationLimit`; there is no separate `nextIteration` export to call.
251
+ - **Continuation over the internal step cap.** Local agents ship the public `agent.runToCompletion()` driver: when a `send` stops at the loop's iteration cap (`RunResult.stoppedAtIterationLimit`) it re-sends a short continuation prompt until a genuine terminal — the agent's native session transcript preserves the conversation across rounds (no manual history rebuild). (The v3 `buildReplayHistory` primitive was removed in v4.0.)
252
+ - **Bounded reflection ladder.** This is a consumer-side policy; the harness exposes the stream/hook surface but not a packaged reflection ladder.
253
+
254
+ ---
255
+
256
+ ## See also
257
+
258
+ - The exported TypeScript types — the canonical, exhaustive public API contract.
259
+ - [`packages/sdk/README.md`](https://github.com/usetheodev/theokit-sdk/blob/main/packages/sdk/README.md) — `@theokit/sdk` front door.
260
+ - [`packages/sdk-tools/README.md`](https://github.com/usetheodev/theokit-sdk/blob/main/packages/sdk-tools/README.md) — `@theokit/sdk-tools` front door.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theokit/sdk",
3
- "version": "4.4.2",
3
+ "version": "4.5.1",
4
4
  "description": "TypeScript SDK for the Theo agent harness — same surface, local or cloud.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/usetheodev/theokit-sdk#readme",
@@ -294,6 +294,7 @@
294
294
  "dist",
295
295
  "bin",
296
296
  "claude-template",
297
+ "docs",
297
298
  "README.md",
298
299
  "CHANGELOG.md",
299
300
  "LICENSE"
@@ -308,16 +309,6 @@
308
309
  "**/agent.js",
309
310
  "**/agent.cjs"
310
311
  ],
311
- "scripts": {
312
- "build": "tsup && cp src/internal/providers/provider-catalog.json dist/provider-catalog.json",
313
- "test": "vitest run --no-file-parallelism",
314
- "test:watch": "vitest",
315
- "test:contract": "vitest run --no-file-parallelism tests/theokit-consumer-contract.test.ts",
316
- "prepublishOnly": "pnpm build && pnpm test:contract",
317
- "typecheck": "tsc --noEmit",
318
- "clean": "rm -rf dist",
319
- "docs:json": "typedoc --options typedoc.json"
320
- },
321
312
  "peerDependencies": {
322
313
  "@lancedb/lancedb": "^0.30.0",
323
314
  "@types/ws": ">=8.0.0",
@@ -385,5 +376,15 @@
385
376
  "typedoc": "^0.28.19",
386
377
  "ws": "^8.18.0",
387
378
  "zod": "^4.0.0"
379
+ },
380
+ "scripts": {
381
+ "build": "tsup && cp src/internal/providers/provider-catalog.json dist/provider-catalog.json && node scripts/copy-docs.mjs",
382
+ "test": "vitest run --no-file-parallelism",
383
+ "test:watch": "vitest",
384
+ "eval": "vitest run --no-file-parallelism tests/eval/suites",
385
+ "test:contract": "vitest run --no-file-parallelism tests/theokit-consumer-contract.test.ts",
386
+ "typecheck": "tsc --noEmit",
387
+ "clean": "rm -rf dist",
388
+ "docs:json": "typedoc --options typedoc.json"
388
389
  }
389
- }
390
+ }