@theokit/sdk 4.5.0 → 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.
- package/CHANGELOG.md +6 -0
- package/README.md +12 -2
- package/docs/error-codes.md +157 -0
- package/docs/harness-capability-map.md +260 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e39cdf6: docs: ship the reference docs inside the npm package. `harness-capability-map.md` (every public primitive + its import path) and `error-codes.md` (the `AgentRunError.code` table) are now readable offline at `node_modules/@theokit/sdk/docs/`, pinned to the installed version — useful for agents that read their own dependencies, and for air-gapped setups. They live at the repo root (linked from the root README/CONTRIBUTING/CLAUDE.md) so `build` copies them into the package via `scripts/copy-docs.mjs`, rewriting repo-relative links to absolute GitHub URLs so they still resolve from `node_modules`. A `tests/lint/shipped-docs.test.ts` gate fails if `files` drops the `docs` entry or a new root reference doc is not added to the ship list. Tarball grows ~7 KB. The package README now also points agents at the docs site's machine-readable corpora (`llms.txt` / `llms-full.txt`).
|
|
8
|
+
|
|
3
9
|
## 4.5.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -24,11 +24,21 @@
|
|
|
24
24
|
|
|
25
25
|
> **Public beta.** APIs may change before general availability.
|
|
26
26
|
|
|
27
|
-
For the full reference, see the [root README](
|
|
27
|
+
For the full reference, see the [root README](https://github.com/usetheodev/theokit-sdk#readme) and the [**Harness Capability Map**](https://github.com/usetheodev/theokit-sdk/blob/main/docs/harness-capability-map.md). The exported TypeScript types are the canonical contract.
|
|
28
28
|
|
|
29
29
|
## Capability map
|
|
30
30
|
|
|
31
|
-
New here? The [**Theo Harness Capability Map**](
|
|
31
|
+
New here? The [**Theo Harness Capability Map**](https://github.com/usetheodev/theokit-sdk/blob/main/docs/harness-capability-map.md) is the discovery front-door — every harness primitive with its import path, signature, and a one-line example (find `compactTranscript`, `buildRepoMap`, `isTransientError`, `@theokit/sdk/persistence`, ... without reading source). The exported TypeScript types are the canonical contract.
|
|
32
|
+
|
|
33
|
+
It also **ships inside this package** — no network needed, and pinned to the version you installed:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
node_modules/@theokit/sdk/docs/harness-capability-map.md # every public primitive + import path
|
|
37
|
+
node_modules/@theokit/sdk/docs/error-codes.md # AgentRunError.code reference
|
|
38
|
+
node_modules/@theokit/sdk/claude-template/ # agent context (npx theokit-init-claude)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Agents that consume documentation should prefer the machine-readable corpora on the docs site ([llmstxt.org](https://llmstxt.org) convention): [`llms.txt`](https://docs.usetheo.dev/llms.txt) (curated index) and [`llms-full.txt`](https://docs.usetheo.dev/llms-full.txt) (every page inlined).
|
|
32
42
|
|
|
33
43
|
## Install
|
|
34
44
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# `@theokit/sdk` Error Codes Reference
|
|
2
|
+
|
|
3
|
+
Canonical reference for `AgentRunError.code` values + provider-to-code mapping (Production-Readiness #3, ADRs D311-D314).
|
|
4
|
+
|
|
5
|
+
## `AgentRunErrorCode` union (16 codes)
|
|
6
|
+
|
|
7
|
+
| Code | Origin | Retriable | When |
|
|
8
|
+
|---|---|:---:|---|
|
|
9
|
+
| `auth_failed` | provider HTTP 401/403 | no | bad API key, revoked token |
|
|
10
|
+
| `rate_limit` | provider HTTP 429 | **yes** | back off using `retryAfterMs` |
|
|
11
|
+
| `quota_exceeded` | provider HTTP 402 / billing body code | no | billing limit hit |
|
|
12
|
+
| `invalid_request` | provider HTTP 400 (generic) | no | malformed payload |
|
|
13
|
+
| `invalid_model` | provider HTTP 400 + "model not found" | no | model id wrong/unavailable |
|
|
14
|
+
| `context_too_long` | provider 400 + context_length code | no | input exceeds model window |
|
|
15
|
+
| `content_filtered` | provider safety filter | no | safety filter blocked |
|
|
16
|
+
| `safety_blocked` | provider safety filter (alias) | no | reserved for stricter mapping |
|
|
17
|
+
| `model_unavailable` | provider 400 + model_unavailable code | no | model temporarily unavailable |
|
|
18
|
+
| `timeout` | HTTP 408 | **yes** | retry with backoff |
|
|
19
|
+
| `network` | DNS/TCP/transport | **yes** | retry with backoff |
|
|
20
|
+
| `server_error` | HTTP 5xx | **yes** | retry with backoff |
|
|
21
|
+
| `provider_unreachable` | DNS/TCP/timeout/5xx (alias) | **yes** | reserved for stricter mapping |
|
|
22
|
+
| `tool_runtime_error` | tool handler throw inside dispatch | no | bug in handler |
|
|
23
|
+
| `aborted` | `AbortSignal` fired (Phase 4) | no | user/lifecycle cancel |
|
|
24
|
+
| `unknown` | unmapped | no | provider returned shape we don't recognize |
|
|
25
|
+
|
|
26
|
+
## Exhaustive `switch` pattern
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { AgentRunError } from "@theokit/sdk";
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await agent.send(message);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (!(err instanceof AgentRunError)) throw err;
|
|
35
|
+
switch (err.code) {
|
|
36
|
+
case "auth_failed":
|
|
37
|
+
// bad key — show login UI
|
|
38
|
+
break;
|
|
39
|
+
case "rate_limit":
|
|
40
|
+
if (err.retryAfterMs !== undefined) {
|
|
41
|
+
setTimeout(retry, err.retryAfterMs);
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
case "quota_exceeded":
|
|
45
|
+
// billing — upsell page
|
|
46
|
+
break;
|
|
47
|
+
case "tool_runtime_error":
|
|
48
|
+
// handler bug — log + tell user
|
|
49
|
+
break;
|
|
50
|
+
case "aborted":
|
|
51
|
+
// user cancelled — no UI noise
|
|
52
|
+
break;
|
|
53
|
+
default:
|
|
54
|
+
// unknown / new code — generic fallback
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Provider mapping table
|
|
61
|
+
|
|
62
|
+
### OpenAI (and OpenAI-compat: OpenRouter, DeepSeek, Together, Mistral, Voyage, DeepInfra)
|
|
63
|
+
|
|
64
|
+
| Status | Body hint | → ErrorCode | → AgentRunErrorCode |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| 401 / 403 | any | `auth_failed` | `auth_failed` |
|
|
67
|
+
| 429 | any | `rate_limit` | `rate_limit` |
|
|
68
|
+
| 402 | any | `invalid_request` | `quota_exceeded` (at AgentRunError layer) |
|
|
69
|
+
| 400 | `code: "context_length_exceeded"` | `context_too_long` | `context_too_long` |
|
|
70
|
+
| 400 | `code: "content_policy_violation"` | `content_filtered` | `content_filtered` |
|
|
71
|
+
| 400 | `code: "model_not_found"` | `model_unavailable` | `model_unavailable` |
|
|
72
|
+
| 400 | `code: "insufficient_quota"` | `invalid_request` | `quota_exceeded` |
|
|
73
|
+
| 400 | other | `invalid_request` | `invalid_request` |
|
|
74
|
+
| 408 | any | `timeout` | `timeout` |
|
|
75
|
+
| 5xx | any | `server_error` | `server_error` |
|
|
76
|
+
| other | any | `unknown` | `unknown` |
|
|
77
|
+
|
|
78
|
+
### Anthropic
|
|
79
|
+
|
|
80
|
+
Same status-based map as OpenAI. Body code hints are dialect-specific (`overloaded_error` → `server_error`, etc.) — see `internal/error-mappers/anthropic.ts` for the authoritative list.
|
|
81
|
+
|
|
82
|
+
### Vertex AI
|
|
83
|
+
|
|
84
|
+
| GCP status | Canonical | Code |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `429` / `RESOURCE_EXHAUSTED` | `RateLimitError` | `rate_limit` |
|
|
87
|
+
| `401` / `UNAUTHENTICATED` | `AuthenticationError` | `auth_failed` |
|
|
88
|
+
| `403` / `PERMISSION_DENIED` | `AuthenticationError` | `auth_failed` |
|
|
89
|
+
| `400` / `INVALID_ARGUMENT` | `ConfigurationError` | `invalid_request` |
|
|
90
|
+
| `408` / `DEADLINE_EXCEEDED` | `NetworkError` | `timeout` |
|
|
91
|
+
| `5xx` | `NetworkError` | `server_error` |
|
|
92
|
+
| other | `UnknownAgentError` | `unknown` |
|
|
93
|
+
|
|
94
|
+
### Bedrock
|
|
95
|
+
|
|
96
|
+
| AWS status / type | Canonical | Code |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| 429 / `ThrottlingException` | `RateLimitError` | `rate_limit` |
|
|
99
|
+
| 401/403 / `AccessDeniedException` | `AuthenticationError` | `auth_failed` |
|
|
100
|
+
| 400 / `ValidationException` | `ConfigurationError` | `invalid_request` |
|
|
101
|
+
| 5xx | `NetworkError` | `server_error` |
|
|
102
|
+
|
|
103
|
+
### Ollama (local)
|
|
104
|
+
|
|
105
|
+
| Failure mode | Code |
|
|
106
|
+
|---|---|
|
|
107
|
+
| Connection refused | `network` |
|
|
108
|
+
| Timeout | `timeout` |
|
|
109
|
+
| Unknown response | `unknown` |
|
|
110
|
+
|
|
111
|
+
Ollama has no billing, no rate limit, no auth → `quota_exceeded`/`rate_limit`/`auth_failed` never fire.
|
|
112
|
+
|
|
113
|
+
## Fields
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
class AgentRunError extends TheokitAgentError {
|
|
117
|
+
readonly code: AgentRunErrorCode;
|
|
118
|
+
readonly provider?: string;
|
|
119
|
+
readonly raw?: string;
|
|
120
|
+
readonly requestId?: string; // x-request-id / request-id header
|
|
121
|
+
readonly conversationId?: string; // SDK agentId where error fired
|
|
122
|
+
|
|
123
|
+
get retriable(): boolean; // alias for isRetryable
|
|
124
|
+
get retryAfterMs(): number | undefined; // metadata.retryAfter * 1000
|
|
125
|
+
get providerError(): unknown; // metadata.raw alias
|
|
126
|
+
|
|
127
|
+
readonly metadata?: ErrorMetadata; // full structured context
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## `retryAfterMs` semantics
|
|
132
|
+
|
|
133
|
+
Returns milliseconds derived from `metadata.retryAfter` (seconds). Use with `setTimeout`:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
if (err.retryAfterMs !== undefined) {
|
|
137
|
+
setTimeout(retry, err.retryAfterMs);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**EC-11: Use `=== undefined` check, NOT truthy check.** `retryAfterMs === 0` is a legitimate value (provider asked for immediate retry — `setTimeout(0)` is valid).
|
|
142
|
+
|
|
143
|
+
## Anti-leak invariant
|
|
144
|
+
|
|
145
|
+
`AgentRunError.message` NEVER contains `providerError` content (raw response body may carry sensitive data — internal field names, log fragments, etc). To inspect the raw body:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
console.log(err.providerError); // alias for err.metadata?.raw
|
|
149
|
+
console.log(err.metadata?.raw); // same value (already redacted via D68)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The redacted body is safe to log — `redactSecrets` strips known secret patterns (API keys, JWT, Authorization headers).
|
|
153
|
+
|
|
154
|
+
## See also
|
|
155
|
+
|
|
156
|
+
- `internal/error-mappers/` — per-provider mapping implementations
|
|
157
|
+
- ADRs D311-D314, D65-D68 (the broader error system)
|
|
@@ -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.5.
|
|
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"
|
|
@@ -377,7 +378,7 @@
|
|
|
377
378
|
"zod": "^4.0.0"
|
|
378
379
|
},
|
|
379
380
|
"scripts": {
|
|
380
|
-
"build": "tsup && cp src/internal/providers/provider-catalog.json dist/provider-catalog.json",
|
|
381
|
+
"build": "tsup && cp src/internal/providers/provider-catalog.json dist/provider-catalog.json && node scripts/copy-docs.mjs",
|
|
381
382
|
"test": "vitest run --no-file-parallelism",
|
|
382
383
|
"test:watch": "vitest",
|
|
383
384
|
"eval": "vitest run --no-file-parallelism tests/eval/suites",
|