@theokit/sdk 1.7.0 → 1.8.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.
@@ -0,0 +1,139 @@
1
+ ---
2
+ user-invocable: false
3
+ paths:
4
+ - "**/.theokit/**"
5
+ - "**/config.*"
6
+ - "**/theo.config.*"
7
+ description: TheoKit SDK configuration reference — .theokit/ structure, mcp.json, hooks, env vars, config discovery
8
+ ---
9
+
10
+ # TheoKit Configuration
11
+
12
+ ## Directory structure
13
+
14
+ ```
15
+ .theokit/
16
+ +-- hooks/ # one .md per hook
17
+ | +-- shell-policy.md
18
+ +-- context/ # one .md per context source
19
+ | +-- bot-readme.md
20
+ +-- skills/<name>/SKILL.md # named capability packs
21
+ +-- plugins/<name>/PLUGIN.md # plugin definitions
22
+ +-- cron/
23
+ | +-- jobs.json # local cron state (auto-created)
24
+ +-- agents/*.md # subagent definitions (name + description frontmatter)
25
+ +-- memory/ # local memory storage
26
+ ```
27
+
28
+ User-level config lives at `~/.theokit/` with the same structure.
29
+
30
+ ## Config file format (v1.5+)
31
+
32
+ Markdown + YAML frontmatter. One file per entity.
33
+
34
+ ### Hook example
35
+
36
+ ```markdown
37
+ ---
38
+ event: preToolUse
39
+ matcher: ^shell$
40
+ command: node .theokit/policy.js
41
+ ---
42
+
43
+ # Shell tool policy gate
44
+
45
+ Vets shell tool invocations before spawn.
46
+ ```
47
+
48
+ ### Disabling an entry
49
+
50
+ Rename `<name>.md` to `<name>.md.disabled` — the loader silently skips it.
51
+
52
+ ## Setting sources
53
+
54
+ `local.settingSources` controls which config layers a local agent loads:
55
+
56
+ | Value | Source |
57
+ |---|---|
58
+ | `"project"` | `.theokit/` in the workspace |
59
+ | `"user"` | `~/.theokit/` |
60
+ | `"team"` | Team settings synced from the dashboard |
61
+ | `"mdm"` | MDM-managed enterprise settings |
62
+ | `"plugins"` | Plugin-provided settings |
63
+ | `"all"` | All of the above |
64
+
65
+ Cloud agents always load project / team / plugins and ignore this field.
66
+
67
+ ```typescript
68
+ const agent = await Agent.create({
69
+ apiKey: process.env.THEOKIT_API_KEY!,
70
+ model: { id: "google/gemini-2.0-flash-001" },
71
+ local: { cwd: process.cwd(), settingSources: ["project", "user"] },
72
+ });
73
+ ```
74
+
75
+ ## Environment variables
76
+
77
+ | Env var | Purpose |
78
+ |---|---|
79
+ | `THEOKIT_API_KEY` | Default API key (user or service account) |
80
+ | `THEOKIT_REDACT_SECRETS` | Set `false` to disable secret redaction (default: `true`) |
81
+ | `OLLAMA_HOST` | Ollama server URL (default: `http://localhost:11434`) |
82
+ | `OLLAMA_API_KEY` | Bearer token for Ollama Cloud or proxy |
83
+ | `OPENROUTER_API_KEY` | OpenRouter provider key |
84
+ | `ANTHROPIC_API_KEY` | Anthropic provider key |
85
+ | `OPENAI_API_KEY` | OpenAI provider key |
86
+
87
+ ## MCP server discovery
88
+
89
+ Servers are loaded with first-match-wins precedence:
90
+
91
+ 1. `mcpServers` on `agent.send()` — fully replaces creation-time servers
92
+ 2. `mcpServers` on `Agent.create()` — used when no per-send override
93
+ 3. Plugin servers (if settingSources includes `"plugins"`)
94
+ 4. Project servers from `.theokit/mcp.json` (if settingSources includes `"project"`)
95
+ 5. User servers from `~/.theokit/mcp.json` (if settingSources includes `"user"`)
96
+
97
+ ## Context manager config
98
+
99
+ ```typescript
100
+ const agent = await Agent.create({
101
+ context: {
102
+ manager: "file", // reads .theokit/context/<name>.md
103
+ maxTokens: 1200,
104
+ },
105
+ local: { cwd: process.cwd(), settingSources: ["project"] },
106
+ });
107
+ ```
108
+
109
+ `manager: "inline"` uses `sources` passed directly in `Agent.create()`.
110
+
111
+ ## Skills config
112
+
113
+ ```typescript
114
+ const agent = await Agent.create({
115
+ skills: {
116
+ enabled: ["code-review", "test-architect"],
117
+ },
118
+ local: { cwd: process.cwd(), settingSources: ["project"] },
119
+ });
120
+ ```
121
+
122
+ Skills live at `.theokit/skills/<name>/SKILL.md` with strict YAML frontmatter
123
+ (`name`, `description` required).
124
+
125
+ ## Migration from legacy JSON
126
+
127
+ ```bash
128
+ npx theokit-migrate-config --apply
129
+ ```
130
+
131
+ Dry-run by default; `--apply` writes. Backs up originals to
132
+ `<file>.json.<unix-ts>.bak`. Legacy JSON still works in v1.x with a
133
+ deprecation warning. JSON support removed in v2.0.
134
+
135
+ ## `agent.reload()`
136
+
137
+ Re-reads filesystem config (context, skills, hooks, project MCP, subagents)
138
+ without disposing the agent or losing conversation state. Invalid files
139
+ raise `ConfigurationError`.
@@ -0,0 +1,148 @@
1
+ ---
2
+ user-invocable: false
3
+ paths:
4
+ - "**/*cron*"
5
+ - "**/*Cron*"
6
+ - "**/*job*"
7
+ - "**/*schedule*"
8
+ description: TheoKit SDK Cron jobs API reference — Cron.create, schedule syntax, job lifecycle
9
+ ---
10
+
11
+ # TheoKit Cron Jobs
12
+
13
+ Schedule Theo agent runs on a cron expression. Two runtimes mirror the agent
14
+ split: local (in-process scheduler) and cloud (Theo PaaS, pre-release).
15
+
16
+ ## Creating a job
17
+
18
+ ```typescript
19
+ import { Cron } from "@theokit/sdk";
20
+
21
+ const job = await Cron.create({
22
+ cron: "0 9 * * *", // every day at 09:00
23
+ timezone: "America/Sao_Paulo",
24
+ message: "Summarize yesterday's commits and post to #engineering",
25
+ agent: {
26
+ apiKey: process.env.THEOKIT_API_KEY!,
27
+ model: { id: "google/gemini-2.0-flash-001" },
28
+ local: { cwd: process.cwd() },
29
+ },
30
+ });
31
+
32
+ await Cron.start(); // required for local jobs to fire
33
+ ```
34
+
35
+ ## Agent binding
36
+
37
+ Pass exactly one of:
38
+
39
+ - **`agent`** (full `AgentOptions`) — a fresh agent is created on every fire.
40
+ Use for independent runs.
41
+ - **`agentId`** (string) — reuses an existing agent's conversation context
42
+ across fires. Use for continuity (e.g., weekly review building on past notes).
43
+
44
+ Setting both raises `ConfigurationError`.
45
+
46
+ ## Cron expressions
47
+
48
+ | Format | Example | Meaning |
49
+ |---|---|---|
50
+ | 5-field POSIX | `0 9 * * *` | Minute, hour, day-of-month, month, day-of-week |
51
+ | `@hourly` | `@hourly` | Every hour at minute 0 |
52
+ | `@daily` | `@daily` | Every day at midnight |
53
+ | `@weekly` | `@weekly` | Every Sunday at midnight |
54
+ | `@monthly` | `@monthly` | First day of month at midnight |
55
+ | `@yearly` | `@yearly` | January 1 at midnight |
56
+
57
+ `timezone` accepts any IANA identifier (default: `"UTC"`). Invalid expressions
58
+ throw `ConfigurationError` synchronously at create time.
59
+
60
+ ## Managing jobs
61
+
62
+ ```typescript
63
+ const { items } = await Cron.list({ runtime: "local", cwd: process.cwd() });
64
+ const job = await Cron.get(jobId);
65
+
66
+ await Cron.disable(jobId); // pause without deleting
67
+ await Cron.enable(jobId); // resume
68
+ await Cron.delete(jobId); // permanent removal
69
+ ```
70
+
71
+ ## Manual fire (off-schedule)
72
+
73
+ ```typescript
74
+ const run = await Cron.run(jobId);
75
+
76
+ for await (const event of run.stream()) {
77
+ // same SDKMessage events as any other run
78
+ }
79
+ ```
80
+
81
+ Manual fires do not update `lastRunAt` — only scheduled fires do.
82
+
83
+ ## Local scheduler lifecycle
84
+
85
+ ```typescript
86
+ await Cron.start({ cwd: process.cwd() }); // reads .theokit/cron/jobs.json
87
+ const status = await Cron.status();
88
+ // { running: true, jobCount: 3, nextFireAt: 1747... }
89
+ await Cron.stop(); // halts scheduling; does NOT delete jobs
90
+ ```
91
+
92
+ Local jobs only fire while the host process is alive. Run as `pm2` / `systemd`
93
+ service for 24/7 local scheduling.
94
+
95
+ ## Persistence
96
+
97
+ Local cron state lives in `.theokit/cron/jobs.json`. Created automatically by
98
+ `Cron.create()`. Commit it if jobs should travel with the repo; add to
99
+ `.gitignore` if environment-specific.
100
+
101
+ ## Type reference
102
+
103
+ ```typescript
104
+ interface CronJob {
105
+ id: string;
106
+ name?: string;
107
+ cron: string;
108
+ timezone?: string;
109
+ message: string | SDKUserMessage;
110
+ agent?: AgentOptions; // mutually exclusive with agentId
111
+ agentId?: string;
112
+ enabled: boolean;
113
+ status: "scheduled" | "running" | "paused" | "errored";
114
+ runtime: "local" | "cloud";
115
+ lastRunAt?: number;
116
+ nextRunAt?: number;
117
+ createdAt: number;
118
+ }
119
+
120
+ interface CronCreateOptions {
121
+ cron: string;
122
+ message: string | SDKUserMessage;
123
+ agent?: AgentOptions;
124
+ agentId?: string;
125
+ name?: string;
126
+ timezone?: string;
127
+ enabled?: boolean; // defaults to true
128
+ apiKey?: string;
129
+ }
130
+
131
+ interface CronSchedulerStatus {
132
+ running: boolean;
133
+ jobCount: number;
134
+ nextFireAt?: number;
135
+ lastError?: { jobId: string; message: string; at: number };
136
+ }
137
+ ```
138
+
139
+ ## Cloud jobs (pre-release)
140
+
141
+ Cloud jobs use Theo PaaS and do not need `Cron.start()`. Pass `agent.cloud`
142
+ to create a cloud-scheduled job. Pre-release — not yet GA.
143
+
144
+ ## Known limitations
145
+
146
+ - Local jobs only fire while the host process is alive.
147
+ - In-flight fires are not resumed if the host process crashes mid-run.
148
+ - `Cron.run()` (manual fire) does not update `lastRunAt`.
@@ -0,0 +1,233 @@
1
+ ---
2
+ user-invocable: false
3
+ description: Dependency injection container, decorators, scopes, and modules for @theokit/di.
4
+ paths:
5
+ - "**/*container*"
6
+ - "**/*inject*"
7
+ - "**/*provider*"
8
+ - "**/*module*"
9
+ ---
10
+
11
+ # TheoKit DI -- Dependency Injection
12
+
13
+ Quick reference for `@theokit/di` -- a TypeScript DI container with decorator metadata.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add @theokit/di reflect-metadata
19
+ ```
20
+
21
+ Requires `reflect-metadata` polyfill imported once at app entry and `experimentalDecorators` + `emitDecoratorMetadata` in `tsconfig.json`.
22
+
23
+ ## Core decorators
24
+
25
+ ### @Injectable
26
+
27
+ ```typescript
28
+ import { Injectable, Scope } from "@theokit/di";
29
+
30
+ @Injectable()
31
+ class UserRepository {
32
+ findById(id: string) { /* ... */ }
33
+ }
34
+
35
+ // With options:
36
+ @Injectable({ scope: Scope.REQUEST })
37
+ class RequestScopedService { /* ... */ }
38
+ ```
39
+
40
+ `InjectableOptions`:
41
+
42
+ | Option | Type | Default | Description |
43
+ |---|---|---|---|
44
+ | `scope` | `Scope` | `Scope.SINGLETON` | Lifecycle scope. |
45
+
46
+ ### @Inject
47
+
48
+ ```typescript
49
+ import { Inject } from "@theokit/di";
50
+
51
+ @Injectable()
52
+ class OrderService {
53
+ constructor(
54
+ @Inject("DATABASE_URL") private readonly dbUrl: string,
55
+ private readonly repo: UserRepository, // auto-resolved by type
56
+ ) {}
57
+ }
58
+ ```
59
+
60
+ Use `@Inject(token)` for string/symbol tokens. Constructor parameter types are auto-resolved via `reflect-metadata` when the parameter is a class.
61
+
62
+ ### @Optional
63
+
64
+ ```typescript
65
+ import { Optional } from "@theokit/di";
66
+
67
+ @Injectable()
68
+ class NotificationService {
69
+ constructor(
70
+ @Optional() private readonly sms?: SmsGateway,
71
+ ) {}
72
+ }
73
+ ```
74
+
75
+ Resolves to `undefined` instead of throwing `TokenNotFoundError` when the dependency is not registered.
76
+
77
+ ### @Qualifier
78
+
79
+ ```typescript
80
+ import { Qualifier } from "@theokit/di";
81
+
82
+ @Injectable()
83
+ class PaymentService {
84
+ constructor(
85
+ @Qualifier("stripe") private readonly gateway: PaymentGateway,
86
+ ) {}
87
+ }
88
+ ```
89
+
90
+ Disambiguates between multiple providers registered under the same interface token.
91
+
92
+ ### @Primary
93
+
94
+ ```typescript
95
+ import { Primary, Injectable } from "@theokit/di";
96
+
97
+ @Injectable()
98
+ @Primary()
99
+ class StripeGateway implements PaymentGateway { /* ... */ }
100
+ ```
101
+
102
+ Marks a provider as the default when multiple are registered for the same token. Wins over non-primary providers unless `@Qualifier` is used.
103
+
104
+ ### @PostConstruct / @PreDestroy
105
+
106
+ ```typescript
107
+ import { PostConstruct, PreDestroy, Injectable } from "@theokit/di";
108
+
109
+ @Injectable()
110
+ class DatabasePool {
111
+ @PostConstruct()
112
+ async init() { /* called after construction */ }
113
+
114
+ @PreDestroy()
115
+ async shutdown() { /* called on container.dispose() */ }
116
+ }
117
+ ```
118
+
119
+ ## Container
120
+
121
+ ```typescript
122
+ import { Container } from "@theokit/di";
123
+
124
+ const container = new Container();
125
+
126
+ // Register classes
127
+ container.register(UserRepository);
128
+ container.register(OrderService);
129
+
130
+ // Register value providers
131
+ container.register("DATABASE_URL", { useValue: "postgres://..." });
132
+
133
+ // Register factory providers
134
+ container.register("Logger", {
135
+ useFactory: (ctx) => new Logger(ctx.resolve("DATABASE_URL")),
136
+ });
137
+
138
+ // Register existing (alias)
139
+ container.register("PrimaryRepo", { useExisting: UserRepository });
140
+
141
+ // Resolve
142
+ const service = container.resolve(OrderService);
143
+ const asyncService = await container.resolveAsync(OrderService);
144
+
145
+ // Dispose (calls @PreDestroy hooks)
146
+ await container.dispose();
147
+ ```
148
+
149
+ ### ContainerOptions
150
+
151
+ ```typescript
152
+ interface ContainerOptions {
153
+ parent?: Container; // hierarchical containers
154
+ autoRegister?: boolean; // default false
155
+ }
156
+ ```
157
+
158
+ ### Provider types
159
+
160
+ ```typescript
161
+ type Provider =
162
+ | ClassProvider // { useClass: Constructor, scope? }
163
+ | ValueProvider // { useValue: any }
164
+ | FactoryProvider // { useFactory: (ctx) => any, scope? }
165
+ | ExistingProvider; // { useExisting: Token }
166
+ ```
167
+
168
+ ## Scopes
169
+
170
+ ```typescript
171
+ import { Scope } from "@theokit/di";
172
+ ```
173
+
174
+ | Scope | Behavior |
175
+ |---|---|
176
+ | `Scope.SINGLETON` | One instance per container (default). |
177
+ | `Scope.TRANSIENT` | New instance on every resolve. |
178
+ | `Scope.REQUEST` | One instance per request scope (via `container.createScope()`). |
179
+
180
+ Request scope example:
181
+
182
+ ```typescript
183
+ const requestContainer = container.createScope();
184
+ const handler = requestContainer.resolve(RequestHandler);
185
+ // All REQUEST-scoped deps share the same instance within this scope
186
+ ```
187
+
188
+ ## @Module
189
+
190
+ ```typescript
191
+ import { Module } from "@theokit/di";
192
+
193
+ @Module({
194
+ providers: [UserRepository, OrderService],
195
+ imports: [DatabaseModule],
196
+ exports: [UserRepository],
197
+ })
198
+ class UserModule {}
199
+
200
+ // Load module into container
201
+ container.loadModule(UserModule);
202
+ ```
203
+
204
+ `ModuleMetadata`:
205
+
206
+ | Field | Type | Description |
207
+ |---|---|---|
208
+ | `providers` | `Provider[]` | Classes/providers registered in this module. |
209
+ | `imports` | `Module[]` | Other modules whose exports become available. |
210
+ | `exports` | `Token[]` | Tokens visible to importing modules. |
211
+
212
+ ## Errors
213
+
214
+ | Error | Cause |
215
+ |---|---|
216
+ | `TokenNotFoundError` | Token not registered and not `@Optional`. |
217
+ | `CyclicDependencyError` | Circular dependency detected in resolution graph. |
218
+ | `MissingInjectableError` | Class used as dependency without `@Injectable`. |
219
+ | `ScopeViolationError` | Singleton depends on transient/request-scoped dep. |
220
+ | `ContainerDisposedError` | Resolve called after `container.dispose()`. |
221
+ | `ContainerFrozenError` | Register called after container is frozen. |
222
+ | `AsyncProviderInSyncResolveError` | Async factory used with `resolve()` instead of `resolveAsync()`. |
223
+ | `ReflectMetadataMissingError` | `reflect-metadata` polyfill not imported. |
224
+ | `CyclicModuleImportError` | Circular module imports detected. |
225
+ | `InvalidModuleError` | Module class missing `@Module` decorator. |
226
+ | `InvalidExportError` | Module exports a token not in its providers. |
227
+
228
+ ## Graph analysis
229
+
230
+ ```typescript
231
+ const graph: DependencyGraph = container.analyzeGraph();
232
+ // Useful for debugging dependency chains and detecting issues
233
+ ```