@knightcodeai/cli-linux-x64 0.9.0 → 0.9.2
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/bin/CHANGELOG.md +88 -0
- package/bin/README.md +52 -19
- package/bin/docs/cli-integration.md +106 -0
- package/bin/docs/cli.md +270 -0
- package/bin/docs/compaction.md +56 -37
- package/bin/docs/configuration.md +46 -0
- package/bin/docs/containerization.md +86 -54
- package/bin/docs/custom-provider.md +132 -782
- package/bin/docs/docs.json +143 -103
- package/bin/docs/environment-variables.md +5 -3
- package/bin/docs/extensions.md +134 -2937
- package/bin/docs/how-knightcode-works.md +49 -0
- package/bin/docs/index.md +24 -69
- package/bin/docs/json.md +193 -65
- package/bin/docs/keybindings.md +56 -101
- package/bin/docs/llama-cpp.md +3 -3
- package/bin/docs/message-types.md +261 -0
- package/bin/docs/models.md +65 -517
- package/bin/docs/packages.md +66 -167
- package/bin/docs/prompt-templates.md +31 -68
- package/bin/docs/providers.md +103 -233
- package/bin/docs/quickstart.md +61 -106
- package/bin/docs/rpc-commands.md +854 -0
- package/bin/docs/rpc-extension-ui.md +200 -0
- package/bin/docs/rpc.md +129 -1556
- package/bin/docs/sdk.md +76 -1160
- package/bin/docs/security.md +70 -32
- package/bin/docs/session-format.md +39 -216
- package/bin/docs/sessions.md +43 -121
- package/bin/docs/settings.md +112 -367
- package/bin/docs/shell-aliases.md +85 -5
- package/bin/docs/skills.md +51 -189
- package/bin/docs/slash-commands.md +63 -0
- package/bin/docs/terminal-setup.md +107 -79
- package/bin/docs/termux.md +74 -83
- package/bin/docs/themes.md +68 -280
- package/bin/docs/tmux.md +31 -39
- package/bin/docs/tui.md +69 -923
- package/bin/docs/usage.md +79 -285
- package/bin/docs/windows.md +43 -17
- package/bin/export-html/template.js +6 -1
- package/bin/knightcode +2 -2
- package/bin/package.json +6 -6
- package/package.json +1 -1
- package/bin/docs/development.md +0 -71
package/bin/docs/sdk.md
CHANGED
|
@@ -1,1226 +1,142 @@
|
|
|
1
|
-
> knightcode can help you use the SDK. Ask it to build an integration for your use case.
|
|
2
|
-
|
|
3
1
|
# SDK
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
**Example use cases:**
|
|
8
|
-
- Build a custom UI (web, desktop, mobile)
|
|
9
|
-
- Integrate agent capabilities into existing applications
|
|
10
|
-
- Create automated pipelines with agent reasoning
|
|
11
|
-
- Build custom tools that spawn sub-agents
|
|
12
|
-
- Test agent behavior programmatically
|
|
13
|
-
|
|
14
|
-
See [examples/sdk/](../examples/sdk/) for working examples from minimal to full control.
|
|
15
|
-
|
|
16
|
-
## Quick Start
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
import { createAgentSession, ModelRuntime, SessionManager } from "@knightcodeai/cli";
|
|
20
|
-
|
|
21
|
-
const modelRuntime = await ModelRuntime.create();
|
|
22
|
-
const { session } = await createAgentSession({
|
|
23
|
-
sessionManager: SessionManager.inMemory(),
|
|
24
|
-
modelRuntime,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
session.subscribe((event) => {
|
|
28
|
-
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
29
|
-
process.stdout.write(event.assistantMessageEvent.delta);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
await session.prompt("What files are in the current directory?");
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Installation
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npm install @knightcodeai/cli
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
The SDK is included in the main package. No separate installation needed.
|
|
43
|
-
|
|
44
|
-
## Core Concepts
|
|
45
|
-
|
|
46
|
-
### createAgentSession()
|
|
3
|
+
`@knightcodeai/cli` embeds KnightCode in a Node.js or Bun process. It provides direct TypeScript access to the agent, sessions, tools, models, and resources used by the command-line application.
|
|
47
4
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`createAgentSession()` uses a `ResourceLoader` to supply extensions, skills, prompt templates, themes, and context files. If you do not provide one, it uses `DefaultResourceLoader` with standard discovery.
|
|
5
|
+
Use the SDK for in-process TypeScript integration. For a language-independent or isolated subprocess, see [CLI Integration](cli-integration.md).
|
|
51
6
|
|
|
52
7
|
```typescript
|
|
53
|
-
import { createAgentSession
|
|
8
|
+
import { createAgentSession } from "@knightcodeai/cli";
|
|
54
9
|
|
|
55
|
-
// Minimal: defaults with DefaultResourceLoader
|
|
56
10
|
const { session } = await createAgentSession();
|
|
57
11
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
});
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### AgentSession
|
|
67
|
-
|
|
68
|
-
The session manages agent lifecycle, message history, model state, compaction, and event streaming.
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
interface AgentSession {
|
|
72
|
-
// Send a prompt and wait for completion
|
|
73
|
-
prompt(text: string, options?: PromptOptions): Promise<void>;
|
|
74
|
-
|
|
75
|
-
// Queue messages during streaming
|
|
76
|
-
steer(text: string): Promise<void>;
|
|
77
|
-
followUp(text: string): Promise<void>;
|
|
78
|
-
|
|
79
|
-
// Subscribe to events (returns unsubscribe function)
|
|
80
|
-
subscribe(listener: (event: AgentSessionEvent) => void): () => void;
|
|
81
|
-
|
|
82
|
-
// Session info
|
|
83
|
-
sessionFile: string | undefined;
|
|
84
|
-
sessionId: string;
|
|
85
|
-
|
|
86
|
-
// Model control
|
|
87
|
-
setModel(model: Model): Promise<void>;
|
|
88
|
-
setThinkingLevel(level: ThinkingLevel): void;
|
|
89
|
-
cycleModel(): Promise<ModelCycleResult | undefined>;
|
|
90
|
-
cycleThinkingLevel(): ThinkingLevel | undefined;
|
|
91
|
-
|
|
92
|
-
// State access
|
|
93
|
-
agent: Agent;
|
|
94
|
-
model: Model | undefined;
|
|
95
|
-
thinkingLevel: ThinkingLevel;
|
|
96
|
-
messages: AgentMessage[];
|
|
97
|
-
isStreaming: boolean;
|
|
98
|
-
|
|
99
|
-
// In-place tree navigation within the current session file
|
|
100
|
-
navigateTree(targetId: string, options?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string }): Promise<{ editorText?: string; cancelled: boolean }>;
|
|
101
|
-
|
|
102
|
-
// Compaction
|
|
103
|
-
compact(customInstructions?: string): Promise<CompactionResult>;
|
|
104
|
-
abortCompaction(): void;
|
|
105
|
-
|
|
106
|
-
// Abort current operation
|
|
107
|
-
abort(): Promise<void>;
|
|
108
|
-
|
|
109
|
-
// Cleanup
|
|
110
|
-
dispose(): void;
|
|
111
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
`session.navigateTree()` rejects while an agent response, manual or automatic compaction, or another tree navigation is active, even with `summarize: false`. It does not queue navigation or return `{ cancelled: true }` for these conflicts. Wait for the active operation to finish (for example, with `await session.waitForIdle()`) and retry. Rejection leaves the active branch unchanged.
|
|
115
|
-
|
|
116
|
-
Session replacement APIs such as new-session, resume, fork, and import live on `AgentSessionRuntime`, not on `AgentSession`.
|
|
117
|
-
|
|
118
|
-
### createAgentSessionRuntime() and AgentSessionRuntime
|
|
119
|
-
|
|
120
|
-
Use the runtime API when you need to replace the active session and rebuild cwd-bound runtime state.
|
|
121
|
-
This is the same layer used by the built-in interactive, print, and RPC modes.
|
|
122
|
-
|
|
123
|
-
`createAgentSessionRuntime()` takes a runtime factory plus the initial cwd/session target. The factory closes over process-global fixed inputs, recreates cwd-bound services for the effective cwd, resolves session options against those services, and returns a full runtime result.
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
import {
|
|
127
|
-
type CreateAgentSessionRuntimeFactory,
|
|
128
|
-
createAgentSessionFromServices,
|
|
129
|
-
createAgentSessionRuntime,
|
|
130
|
-
createAgentSessionServices,
|
|
131
|
-
getAgentDir,
|
|
132
|
-
SessionManager,
|
|
133
|
-
} from "@knightcodeai/cli";
|
|
134
|
-
|
|
135
|
-
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
136
|
-
const services = await createAgentSessionServices({ cwd });
|
|
137
|
-
return {
|
|
138
|
-
...(await createAgentSessionFromServices({
|
|
139
|
-
services,
|
|
140
|
-
sessionManager,
|
|
141
|
-
sessionStartEvent,
|
|
142
|
-
})),
|
|
143
|
-
services,
|
|
144
|
-
diagnostics: services.diagnostics,
|
|
145
|
-
};
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const runtime = await createAgentSessionRuntime(createRuntime, {
|
|
149
|
-
cwd: process.cwd(),
|
|
150
|
-
agentDir: getAgentDir(),
|
|
151
|
-
sessionManager: SessionManager.create(process.cwd()),
|
|
152
|
-
});
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
`AgentSessionRuntime` owns replacement of the active runtime across:
|
|
156
|
-
|
|
157
|
-
- `newSession()`
|
|
158
|
-
- `switchSession()`
|
|
159
|
-
- `fork()`
|
|
160
|
-
- clone flows via `fork(entryId, { position: "at" })`
|
|
161
|
-
- `importFromJsonl()`
|
|
162
|
-
|
|
163
|
-
Important behavior:
|
|
164
|
-
|
|
165
|
-
- `runtime.session` changes after those operations
|
|
166
|
-
- event subscriptions are attached to a specific `AgentSession`, so re-subscribe after replacement
|
|
167
|
-
- if you use extensions, call `runtime.session.bindExtensions(...)` again for the new session
|
|
168
|
-
- creation returns diagnostics on `runtime.diagnostics`
|
|
169
|
-
- if runtime creation or replacement fails, the method throws and the caller decides how to handle it
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
let session = runtime.session;
|
|
173
|
-
let unsubscribe = session.subscribe(() => {});
|
|
174
|
-
|
|
175
|
-
await runtime.newSession();
|
|
176
|
-
|
|
177
|
-
unsubscribe();
|
|
178
|
-
session = runtime.session;
|
|
179
|
-
unsubscribe = session.subscribe(() => {});
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### Prompting and Message Queueing
|
|
183
|
-
|
|
184
|
-
`PromptOptions` controls prompt expansion, queueing behavior while streaming, and prompt preflight notifications:
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
interface PromptOptions {
|
|
188
|
-
expandPromptTemplates?: boolean;
|
|
189
|
-
images?: ImageContent[];
|
|
190
|
-
streamingBehavior?: "steer" | "followUp";
|
|
191
|
-
source?: InputSource;
|
|
192
|
-
preflightResult?: (success: boolean) => void;
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
`preflightResult` is called once per `prompt()` invocation:
|
|
197
|
-
|
|
198
|
-
- `true` when the prompt was accepted, queued, or handled immediately
|
|
199
|
-
- `false` when prompt preflight rejected before acceptance
|
|
200
|
-
|
|
201
|
-
It fires before `prompt()` resolves. `prompt()` still resolves only after the full accepted run finishes, including retries. Failures after acceptance are reported through the normal event and message stream, not through `preflightResult(false)`.
|
|
202
|
-
|
|
203
|
-
The `prompt()` method handles prompt templates, extension commands, and message sending:
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
// Basic prompt (when not streaming)
|
|
207
|
-
await session.prompt("What files are here?");
|
|
208
|
-
|
|
209
|
-
// With images
|
|
210
|
-
await session.prompt("What's in this image?", {
|
|
211
|
-
images: [{ type: "image", source: { type: "base64", mediaType: "image/png", data: "..." } }]
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// During streaming: must specify how to queue the message
|
|
215
|
-
await session.prompt("Stop and do this instead", { streamingBehavior: "steer" });
|
|
216
|
-
await session.prompt("After you're done, also check X", { streamingBehavior: "followUp" });
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
**Behavior:**
|
|
220
|
-
- **Extension commands** (e.g., `/mycommand`): Execute immediately, even during streaming. They manage their own LLM interaction via `knightcode.sendMessage()`.
|
|
221
|
-
- **File-based prompt templates** (from `.md` files): Expanded to their content before sending or queueing.
|
|
222
|
-
- **During streaming without `streamingBehavior`**: Throws an error. Use `steer()` or `followUp()` directly, or specify the option.
|
|
223
|
-
- **`preflightResult(true)`**: Means the prompt was accepted, queued, or handled immediately.
|
|
224
|
-
- **`preflightResult(false)`**: Means preflight rejected before acceptance.
|
|
225
|
-
|
|
226
|
-
For explicit queueing during streaming:
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
// Queue a steering message for delivery after the current assistant turn finishes its tool calls
|
|
230
|
-
await session.steer("New instruction");
|
|
231
|
-
|
|
232
|
-
// Wait for agent to finish (delivered only when agent stops)
|
|
233
|
-
await session.followUp("After you're done, also do this");
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
Both `steer()` and `followUp()` expand file-based prompt templates but error on extension commands (extension commands cannot be queued).
|
|
237
|
-
|
|
238
|
-
### Agent and AgentState
|
|
239
|
-
|
|
240
|
-
The `Agent` class (from `@knightcode/agent`) handles the core LLM interaction. Access it via `session.agent`.
|
|
241
|
-
|
|
242
|
-
```typescript
|
|
243
|
-
// Access current state
|
|
244
|
-
const state = session.agent.state;
|
|
245
|
-
|
|
246
|
-
// state.messages: AgentMessage[] - conversation history
|
|
247
|
-
// state.model: Model - current model
|
|
248
|
-
// state.thinkingLevel: ThinkingLevel - current thinking level
|
|
249
|
-
// state.systemPrompt: string - read-only, replayed from the transcript's system messages
|
|
250
|
-
// state.tools: AgentTool[] - executable tools; changes are declared to the model before the next request
|
|
251
|
-
// state.streamingMessage?: AgentMessage - current partial assistant message
|
|
252
|
-
// state.errorMessage?: string - latest assistant error
|
|
253
|
-
|
|
254
|
-
// Replace messages (useful for branching or restoration)
|
|
255
|
-
session.agent.state.messages = messages; // copies the top-level array
|
|
256
|
-
|
|
257
|
-
// Replace tools
|
|
258
|
-
session.agent.state.tools = tools; // copies the top-level array
|
|
259
|
-
|
|
260
|
-
// Wait for agent to finish processing
|
|
261
|
-
await session.agent.waitForIdle();
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
### Events
|
|
265
|
-
|
|
266
|
-
Subscribe to events to receive streaming output and lifecycle notifications.
|
|
267
|
-
|
|
268
|
-
```typescript
|
|
269
|
-
session.subscribe((event) => {
|
|
270
|
-
switch (event.type) {
|
|
271
|
-
// Streaming text from assistant
|
|
272
|
-
case "message_update":
|
|
273
|
-
if (event.assistantMessageEvent.type === "text_delta") {
|
|
274
|
-
process.stdout.write(event.assistantMessageEvent.delta);
|
|
275
|
-
}
|
|
276
|
-
if (event.assistantMessageEvent.type === "thinking_delta") {
|
|
277
|
-
// Thinking output (if thinking enabled)
|
|
278
|
-
}
|
|
279
|
-
break;
|
|
280
|
-
|
|
281
|
-
// Tool execution
|
|
282
|
-
case "tool_execution_start":
|
|
283
|
-
console.log(`Tool: ${event.toolName}`);
|
|
284
|
-
break;
|
|
285
|
-
case "tool_execution_update":
|
|
286
|
-
// Streaming tool output
|
|
287
|
-
break;
|
|
288
|
-
case "tool_execution_end":
|
|
289
|
-
console.log(`Result: ${event.isError ? "error" : "success"}`);
|
|
290
|
-
break;
|
|
291
|
-
|
|
292
|
-
// Message lifecycle
|
|
293
|
-
case "message_start":
|
|
294
|
-
// New message starting
|
|
295
|
-
break;
|
|
296
|
-
case "message_end":
|
|
297
|
-
// Message complete
|
|
298
|
-
break;
|
|
299
|
-
|
|
300
|
-
// Agent lifecycle
|
|
301
|
-
case "agent_start":
|
|
302
|
-
// Agent started processing prompt
|
|
303
|
-
break;
|
|
304
|
-
case "agent_end":
|
|
305
|
-
// Agent finished (event.messages contains new messages)
|
|
306
|
-
break;
|
|
307
|
-
|
|
308
|
-
// Turn lifecycle (one LLM response + tool calls)
|
|
309
|
-
case "turn_start":
|
|
310
|
-
break;
|
|
311
|
-
case "turn_end":
|
|
312
|
-
// event.message: assistant response
|
|
313
|
-
// event.toolResults: tool results from this turn
|
|
314
|
-
break;
|
|
315
|
-
|
|
316
|
-
// Session events (queue, compaction, retry)
|
|
317
|
-
case "queue_update":
|
|
318
|
-
console.log(event.steering, event.followUp);
|
|
319
|
-
break;
|
|
320
|
-
case "compaction_start":
|
|
321
|
-
case "compaction_end":
|
|
322
|
-
case "auto_retry_start":
|
|
323
|
-
case "auto_retry_end":
|
|
324
|
-
case "summarization_retry_scheduled":
|
|
325
|
-
case "summarization_retry_attempt_start":
|
|
326
|
-
case "summarization_retry_finished":
|
|
327
|
-
break;
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
## Options Reference
|
|
333
|
-
|
|
334
|
-
### Directories
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
const { session } = await createAgentSession({
|
|
338
|
-
// Working directory for DefaultResourceLoader discovery
|
|
339
|
-
cwd: process.cwd(), // default
|
|
340
|
-
|
|
341
|
-
// Global config directory
|
|
342
|
-
agentDir: "~/.knightcode/agent", // default (expands ~)
|
|
343
|
-
});
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
`cwd` is used by `DefaultResourceLoader` for:
|
|
347
|
-
- Project extensions (`.knightcode/extensions/`)
|
|
348
|
-
- Project skills:
|
|
349
|
-
- `.knightcode/skills/`
|
|
350
|
-
- `.agents/skills/` in `cwd` and ancestor directories (up to git repo root, or filesystem root when not in a repo)
|
|
351
|
-
- Project prompts (`.knightcode/prompts/`)
|
|
352
|
-
- Context files (`AGENTS.md` walking up from cwd)
|
|
353
|
-
- Session directory naming
|
|
354
|
-
|
|
355
|
-
`agentDir` is used by `DefaultResourceLoader` for:
|
|
356
|
-
- Global extensions (`extensions/`)
|
|
357
|
-
- Global skills:
|
|
358
|
-
- `skills/` under `agentDir` (for example `~/.knightcode/agent/skills/`)
|
|
359
|
-
- `~/.agents/skills/`
|
|
360
|
-
- Global prompts (`prompts/`)
|
|
361
|
-
- Global context file (`AGENTS.md`)
|
|
362
|
-
- Settings (`settings.json`)
|
|
363
|
-
- Custom models (`models.json`)
|
|
364
|
-
- Credentials (`auth.json`)
|
|
365
|
-
- Sessions (`sessions/`)
|
|
366
|
-
|
|
367
|
-
When you pass a custom `ResourceLoader`, `cwd` and `agentDir` no longer control resource discovery. They still influence session naming and tool path resolution.
|
|
368
|
-
|
|
369
|
-
### Model
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
import { getModel } from "@knightcode/ai";
|
|
373
|
-
import { ModelRuntime } from "@knightcodeai/cli";
|
|
374
|
-
|
|
375
|
-
const modelRuntime = await ModelRuntime.create();
|
|
376
|
-
|
|
377
|
-
// create() restores cached catalogs but does not refresh them from knightcode.dev by default.
|
|
378
|
-
// Opt in to a create-time network refresh and bound how long it may take:
|
|
379
|
-
const refreshedRuntime = await ModelRuntime.create({
|
|
380
|
-
allowModelNetwork: true,
|
|
381
|
-
modelRefreshTimeoutMs: 15_000,
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// Find specific built-in model (doesn't check if API key exists)
|
|
385
|
-
const opus = getModel("anthropic", "claude-opus-4-5");
|
|
386
|
-
if (!opus) throw new Error("Model not found");
|
|
387
|
-
|
|
388
|
-
// Find any model by provider/id, including custom models from models.json
|
|
389
|
-
// (doesn't check if API key exists)
|
|
390
|
-
const customModel = modelRuntime.getModel("my-provider", "my-model");
|
|
391
|
-
|
|
392
|
-
// Get only models that have valid authentication configured
|
|
393
|
-
const available = await modelRuntime.getAvailable();
|
|
394
|
-
|
|
395
|
-
const { session } = await createAgentSession({
|
|
396
|
-
model: opus,
|
|
397
|
-
thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh, max
|
|
398
|
-
|
|
399
|
-
// Models for cycling (Ctrl+P in interactive mode)
|
|
400
|
-
scopedModels: [
|
|
401
|
-
{ model: opus, thinkingLevel: "high" },
|
|
402
|
-
{ model: haiku, thinkingLevel: "off" },
|
|
403
|
-
],
|
|
404
|
-
|
|
405
|
-
modelRuntime,
|
|
406
|
-
});
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
If no model is provided:
|
|
410
|
-
1. Tries to restore from session (if continuing)
|
|
411
|
-
2. Uses default from settings
|
|
412
|
-
3. Falls back to first available model
|
|
413
|
-
|
|
414
|
-
Remote catalogs are persisted locally so later runtimes can restore them without a network request. The default file is `~/.knightcode/agent/models-store.json`; set `modelsStorePath` to choose another location, or inject `modelsStore` to control persistence. Network refreshes are throttled to once per provider every four hours unless forced. To force an immediate refresh, call `await modelRuntime.refresh({ allowNetwork: true, force: true, signal })`. Setting `KNIGHTCODE_OFFLINE` disables model network access.
|
|
415
|
-
|
|
416
|
-
To match CLI model parsing, use the exported resolver helpers:
|
|
417
|
-
|
|
418
|
-
```typescript
|
|
419
|
-
import {
|
|
420
|
-
resolveCliModel,
|
|
421
|
-
resolveModelScopeWithDiagnostics,
|
|
422
|
-
} from "@knightcodeai/cli";
|
|
423
|
-
|
|
424
|
-
const cliModel = resolveCliModel({
|
|
425
|
-
cliModel: "anthropic/claude-opus-4-5:high",
|
|
426
|
-
modelRuntime,
|
|
427
|
-
});
|
|
428
|
-
if (cliModel.error) throw new Error(cliModel.error);
|
|
429
|
-
if (cliModel.warning) console.warn(cliModel.warning);
|
|
430
|
-
|
|
431
|
-
const { scopedModels, diagnostics } = await resolveModelScopeWithDiagnostics(
|
|
432
|
-
["anthropic/*:high", "gpt-5"],
|
|
433
|
-
modelRuntime,
|
|
434
|
-
);
|
|
435
|
-
for (const diagnostic of diagnostics) {
|
|
436
|
-
console.warn(diagnostic.message);
|
|
12
|
+
try {
|
|
13
|
+
await session.prompt("What files are in the current directory?");
|
|
14
|
+
console.log(session.getLastAssistantText());
|
|
15
|
+
} finally {
|
|
16
|
+
session.dispose();
|
|
437
17
|
}
|
|
438
18
|
```
|
|
439
19
|
|
|
440
|
-
|
|
20
|
+
This uses the working directory, discovered resources, stored settings, and configured credentials. `prompt()` resolves when the run finishes.
|
|
441
21
|
|
|
442
|
-
|
|
22
|
+
The [complete minimal example](../examples/sdk/01-minimal.ts) also streams text events. All [SDK examples](../examples/sdk/) are typechecked with the repository.
|
|
443
23
|
|
|
444
|
-
|
|
24
|
+
<a id="session-management"></a>
|
|
445
25
|
|
|
446
|
-
|
|
447
|
-
1. Runtime overrides (via `setRuntimeApiKey`, not persisted)
|
|
448
|
-
2. Stored credentials in `auth.json` (API keys or OAuth tokens)
|
|
449
|
-
3. Environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.)
|
|
450
|
-
4. Fallback resolver (for custom provider keys from `models.json`)
|
|
26
|
+
## Session lifecycle
|
|
451
27
|
|
|
452
|
-
|
|
453
|
-
import { InMemoryCredentialStore } from "@knightcode/ai";
|
|
454
|
-
import { createAgentSession, ModelRuntime } from "@knightcodeai/cli";
|
|
28
|
+
`createAgentSession()` creates an `AgentSession`. The session owns one conversation, its model and tools, queued messages, compaction state, and extension runtime.
|
|
455
29
|
|
|
456
|
-
|
|
457
|
-
const modelRuntime = await ModelRuntime.create();
|
|
30
|
+
Read current state through `session.messages`, `session.model`, `session.thinkingLevel`, `session.systemPrompt`, and `session.getActiveToolNames()`.
|
|
458
31
|
|
|
459
|
-
|
|
460
|
-
for (const provider of modelRuntime.getProviders()) {
|
|
461
|
-
const status = await modelRuntime.checkAuth(provider.id);
|
|
462
|
-
console.log(provider.name, provider.auth, status);
|
|
463
|
-
}
|
|
32
|
+
`session.systemPrompt` is read-only and returns the current effective system prompt, including changes that have not yet been sent to the model. Tool changes are declared to the model before the next request.
|
|
464
33
|
|
|
465
|
-
|
|
466
|
-
await modelRuntime.setRuntimeApiKey("anthropic", "sk-my-temp-key");
|
|
34
|
+
<a id="sessionmanager-api"></a>
|
|
467
35
|
|
|
468
|
-
|
|
469
|
-
const customRuntime = await ModelRuntime.create({
|
|
470
|
-
authPath: "/my/app/auth.json",
|
|
471
|
-
modelsPath: "/my/app/models.json",
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// Or inject any @knightcode/ai CredentialStore
|
|
475
|
-
const credentials = new InMemoryCredentialStore();
|
|
476
|
-
const inMemoryRuntime = await ModelRuntime.create({ credentials });
|
|
36
|
+
### Session storage
|
|
477
37
|
|
|
478
|
-
|
|
479
|
-
modelRuntime: customRuntime,
|
|
480
|
-
});
|
|
481
|
-
```
|
|
38
|
+
Sessions are persistent by default. `SessionManager` owns the persisted or in-memory entry tree and tracks its active leaf. Branching changes that leaf without deleting abandoned branches. When KnightCode reconstructs model context, the manager selects the active branch and applies compaction.
|
|
482
39
|
|
|
483
|
-
`
|
|
40
|
+
`SessionManager` is authoritative for finalized model context. Restore external history by constructing the session with a manager containing those entries. Assigning `session.agent.state.messages` does not replace persisted context.
|
|
484
41
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
```typescript
|
|
488
|
-
const signal = AbortSignal.timeout(15_000);
|
|
489
|
-
const result = await modelRuntime.refresh({
|
|
490
|
-
providers: ["anthropic"],
|
|
491
|
-
signal,
|
|
492
|
-
});
|
|
493
|
-
if (result.aborted) console.warn("Catalog refresh timed out; using cached models");
|
|
494
|
-
for (const [providerId, error] of result.errors) {
|
|
495
|
-
console.warn(`Could not refresh ${providerId}:`, error);
|
|
496
|
-
}
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
A failed or timed-out network refresh does not undo a successful credential operation. `refresh()` starts a new provider generation, so it does not wait behind an older stalled refresh and stale generations cannot publish afterward.
|
|
500
|
-
|
|
501
|
-
> See [examples/sdk/09-api-keys-and-oauth.ts](../examples/sdk/09-api-keys-and-oauth.ts)
|
|
502
|
-
|
|
503
|
-
### System Prompt
|
|
504
|
-
|
|
505
|
-
Use a `ResourceLoader` to override the system prompt:
|
|
506
|
-
|
|
507
|
-
```typescript
|
|
508
|
-
import { createAgentSession, DefaultResourceLoader } from "@knightcodeai/cli";
|
|
509
|
-
|
|
510
|
-
const loader = new DefaultResourceLoader({
|
|
511
|
-
systemPromptOverride: () => "You are a helpful assistant.",
|
|
512
|
-
});
|
|
513
|
-
await loader.reload();
|
|
514
|
-
|
|
515
|
-
const { session } = await createAgentSession({ resourceLoader: loader });
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
> See [examples/sdk/03-custom-prompt.ts](../examples/sdk/03-custom-prompt.ts)
|
|
519
|
-
|
|
520
|
-
### Tools
|
|
521
|
-
|
|
522
|
-
Specify which built-in tools to enable:
|
|
523
|
-
|
|
524
|
-
- Built-in tool names: `read`, `bash`, `powershell`, `edit`, `write`, `grep`, `find`, `ls`
|
|
525
|
-
- Default built-ins: `read`, `bash`, `edit`, `write`
|
|
526
|
-
- `noTools: "all"` disables all tools
|
|
527
|
-
- `noTools: "builtin"` disables default built-ins while keeping extension and custom tools enabled
|
|
528
|
-
- `excludeTools` disables specific built-in, extension, or custom tool names after any `tools` allowlist is applied
|
|
529
|
-
|
|
530
|
-
The `edit` tool returns `details.diff` for KnightCode's TUI display and `details.patch` as a standard unified patch for SDK consumers.
|
|
531
|
-
|
|
532
|
-
```typescript
|
|
533
|
-
import { createAgentSession } from "@knightcodeai/cli";
|
|
534
|
-
|
|
535
|
-
// Read-only mode
|
|
536
|
-
const { session } = await createAgentSession({
|
|
537
|
-
tools: ["read", "grep", "find", "ls"],
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
// Pick specific tools
|
|
541
|
-
const { session } = await createAgentSession({
|
|
542
|
-
tools: ["read", "bash", "grep"],
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
// Use PowerShell instead of Bash on Windows
|
|
546
|
-
const { session } = await createAgentSession({
|
|
547
|
-
tools: ["read", "powershell", "edit", "write"],
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
// Disable one tool while keeping the rest available
|
|
551
|
-
const { session } = await createAgentSession({
|
|
552
|
-
excludeTools: ["ask_question"],
|
|
553
|
-
});
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
#### Tools with Custom cwd
|
|
557
|
-
|
|
558
|
-
When you pass a custom `cwd`, `createAgentSession()` builds selected built-in tools for that cwd.
|
|
42
|
+
Use an in-memory manager when the host does not want session files:
|
|
559
43
|
|
|
560
44
|
```typescript
|
|
561
45
|
import { createAgentSession, SessionManager } from "@knightcodeai/cli";
|
|
562
46
|
|
|
563
|
-
const cwd = "/path/to/project";
|
|
564
|
-
|
|
565
|
-
// Use default tools for custom cwd
|
|
566
|
-
const { session } = await createAgentSession({
|
|
567
|
-
cwd,
|
|
568
|
-
sessionManager: SessionManager.inMemory(cwd),
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
// Or pick specific tools for custom cwd
|
|
572
|
-
const { session } = await createAgentSession({
|
|
573
|
-
cwd,
|
|
574
|
-
tools: ["read", "bash", "grep"],
|
|
575
|
-
sessionManager: SessionManager.inMemory(cwd),
|
|
576
|
-
});
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
> See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
|
|
580
|
-
|
|
581
|
-
### Custom Tools
|
|
582
|
-
|
|
583
|
-
```typescript
|
|
584
|
-
import { Type } from "typebox";
|
|
585
|
-
import { createAgentSession, defineTool } from "@knightcodeai/cli";
|
|
586
|
-
|
|
587
|
-
// Inline custom tool
|
|
588
|
-
const myTool = defineTool({
|
|
589
|
-
name: "my_tool",
|
|
590
|
-
label: "My Tool",
|
|
591
|
-
description: "Does something useful",
|
|
592
|
-
parameters: Type.Object({
|
|
593
|
-
input: Type.String({ description: "Input value" }),
|
|
594
|
-
}),
|
|
595
|
-
execute: async (_toolCallId, params) => ({
|
|
596
|
-
content: [{ type: "text", text: `Result: ${params.input}` }],
|
|
597
|
-
details: {},
|
|
598
|
-
}),
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
// Pass custom tools directly
|
|
602
|
-
const { session } = await createAgentSession({
|
|
603
|
-
customTools: [myTool],
|
|
604
|
-
});
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
Use `defineTool()` for standalone definitions and arrays like `customTools: [myTool]`. Inline `knightcode.registerTool({ ... })` already infers parameter types correctly.
|
|
608
|
-
|
|
609
|
-
Custom tools passed via `customTools` are combined with extension-registered tools. Extensions loaded by the ResourceLoader can also register tools via `knightcode.registerTool()`.
|
|
610
|
-
|
|
611
|
-
If you pass `tools`, include each custom or extension tool name you want enabled, for example `tools: ["read", "bash", "my_tool"]`.
|
|
612
|
-
|
|
613
|
-
> See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
|
|
614
|
-
|
|
615
|
-
### Extensions
|
|
616
|
-
|
|
617
|
-
Extensions are loaded by the `ResourceLoader`. `DefaultResourceLoader` discovers extensions from `~/.knightcode/agent/extensions/`, `.knightcode/extensions/`, and settings.json extension sources.
|
|
618
|
-
|
|
619
|
-
```typescript
|
|
620
|
-
import { createAgentSession, DefaultResourceLoader } from "@knightcodeai/cli";
|
|
621
|
-
|
|
622
|
-
const loader = new DefaultResourceLoader({
|
|
623
|
-
additionalExtensionPaths: ["/path/to/my-extension.ts"],
|
|
624
|
-
extensionFactories: [
|
|
625
|
-
(knightcode) => {
|
|
626
|
-
knightcode.on("agent_start", () => {
|
|
627
|
-
console.log("[Inline Extension] Agent starting");
|
|
628
|
-
});
|
|
629
|
-
},
|
|
630
|
-
],
|
|
631
|
-
});
|
|
632
|
-
await loader.reload();
|
|
633
|
-
|
|
634
|
-
const { session } = await createAgentSession({ resourceLoader: loader });
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
Extensions can register tools, subscribe to events, add commands, and more. See [extensions.md](extensions.md) for the full API.
|
|
638
|
-
|
|
639
|
-
**Named inline extensions:** By default, inline factories display as `<inline:1>`, `<inline:2>`, etc. in the startup Extensions list. To show a descriptive name instead, wrap the factory:
|
|
640
|
-
|
|
641
|
-
```typescript
|
|
642
|
-
import type { InlineExtension } from "@knightcodeai/cli";
|
|
643
|
-
|
|
644
|
-
const myProvider: InlineExtension = {
|
|
645
|
-
name: "my-provider",
|
|
646
|
-
factory: (knightcode) => {
|
|
647
|
-
knightcode.on("agent_start", () => {
|
|
648
|
-
console.log("[my-provider] Agent starting");
|
|
649
|
-
});
|
|
650
|
-
},
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
const loader = new DefaultResourceLoader({
|
|
654
|
-
extensionFactories: [myProvider],
|
|
655
|
-
});
|
|
656
|
-
```
|
|
657
|
-
|
|
658
|
-
This displays as `<inline:my-provider>` instead of `<inline:1>`. Bare factory functions are still accepted for backward compatibility.
|
|
659
|
-
|
|
660
|
-
**Event Bus:** Extensions can communicate via `knightcode.events`. Pass a shared `eventBus` to `DefaultResourceLoader` if you need to emit or listen from outside:
|
|
661
|
-
|
|
662
|
-
```typescript
|
|
663
|
-
import { createEventBus, DefaultResourceLoader } from "@knightcodeai/cli";
|
|
664
|
-
|
|
665
|
-
const eventBus = createEventBus();
|
|
666
|
-
const loader = new DefaultResourceLoader({
|
|
667
|
-
eventBus,
|
|
668
|
-
});
|
|
669
|
-
await loader.reload();
|
|
670
|
-
|
|
671
|
-
eventBus.on("my-extension:status", (data) => console.log(data));
|
|
672
|
-
```
|
|
673
|
-
|
|
674
|
-
> See [examples/sdk/06-extensions.ts](../examples/sdk/06-extensions.ts) and [docs/extensions.md](extensions.md)
|
|
675
|
-
|
|
676
|
-
### Skills
|
|
677
|
-
|
|
678
|
-
```typescript
|
|
679
|
-
import {
|
|
680
|
-
createAgentSession,
|
|
681
|
-
DefaultResourceLoader,
|
|
682
|
-
type Skill,
|
|
683
|
-
} from "@knightcodeai/cli";
|
|
684
|
-
|
|
685
|
-
const customSkill: Skill = {
|
|
686
|
-
name: "my-skill",
|
|
687
|
-
description: "Custom instructions",
|
|
688
|
-
filePath: "/path/to/SKILL.md",
|
|
689
|
-
baseDir: "/path/to",
|
|
690
|
-
source: "custom",
|
|
691
|
-
};
|
|
692
|
-
|
|
693
|
-
const loader = new DefaultResourceLoader({
|
|
694
|
-
skillsOverride: (current) => ({
|
|
695
|
-
skills: [...current.skills, customSkill],
|
|
696
|
-
diagnostics: current.diagnostics,
|
|
697
|
-
}),
|
|
698
|
-
});
|
|
699
|
-
await loader.reload();
|
|
700
|
-
|
|
701
|
-
const { session } = await createAgentSession({ resourceLoader: loader });
|
|
702
|
-
```
|
|
703
|
-
|
|
704
|
-
> See [examples/sdk/04-skills.ts](../examples/sdk/04-skills.ts)
|
|
705
|
-
|
|
706
|
-
### Context Files
|
|
707
|
-
|
|
708
|
-
```typescript
|
|
709
|
-
import { createAgentSession, DefaultResourceLoader } from "@knightcodeai/cli";
|
|
710
|
-
|
|
711
|
-
const loader = new DefaultResourceLoader({
|
|
712
|
-
agentsFilesOverride: (current) => ({
|
|
713
|
-
agentsFiles: [
|
|
714
|
-
...current.agentsFiles,
|
|
715
|
-
{ path: "/virtual/AGENTS.md", content: "# Guidelines\n\n- Be concise" },
|
|
716
|
-
],
|
|
717
|
-
}),
|
|
718
|
-
});
|
|
719
|
-
await loader.reload();
|
|
720
|
-
|
|
721
|
-
const { session } = await createAgentSession({ resourceLoader: loader });
|
|
722
|
-
```
|
|
723
|
-
|
|
724
|
-
> See [examples/sdk/07-context-files.ts](../examples/sdk/07-context-files.ts)
|
|
725
|
-
|
|
726
|
-
### Slash Commands
|
|
727
|
-
|
|
728
|
-
```typescript
|
|
729
|
-
import {
|
|
730
|
-
createAgentSession,
|
|
731
|
-
DefaultResourceLoader,
|
|
732
|
-
type PromptTemplate,
|
|
733
|
-
} from "@knightcodeai/cli";
|
|
734
|
-
|
|
735
|
-
const customCommand: PromptTemplate = {
|
|
736
|
-
name: "deploy",
|
|
737
|
-
description: "Deploy the application",
|
|
738
|
-
source: "(custom)",
|
|
739
|
-
content: "# Deploy\n\n1. Build\n2. Test\n3. Deploy",
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
const loader = new DefaultResourceLoader({
|
|
743
|
-
promptsOverride: (current) => ({
|
|
744
|
-
prompts: [...current.prompts, customCommand],
|
|
745
|
-
diagnostics: current.diagnostics,
|
|
746
|
-
}),
|
|
747
|
-
});
|
|
748
|
-
await loader.reload();
|
|
749
|
-
|
|
750
|
-
const { session } = await createAgentSession({ resourceLoader: loader });
|
|
751
|
-
```
|
|
752
|
-
|
|
753
|
-
> See [examples/sdk/08-prompt-templates.ts](../examples/sdk/08-prompt-templates.ts)
|
|
754
|
-
|
|
755
|
-
### Session Management
|
|
756
|
-
|
|
757
|
-
Sessions use a tree structure with `id`/`parentId` linking, enabling in-place branching.
|
|
758
|
-
|
|
759
|
-
```typescript
|
|
760
|
-
import {
|
|
761
|
-
type CreateAgentSessionRuntimeFactory,
|
|
762
|
-
createAgentSession,
|
|
763
|
-
createAgentSessionFromServices,
|
|
764
|
-
createAgentSessionRuntime,
|
|
765
|
-
createAgentSessionServices,
|
|
766
|
-
getAgentDir,
|
|
767
|
-
SessionManager,
|
|
768
|
-
} from "@knightcodeai/cli";
|
|
769
|
-
|
|
770
|
-
// In-memory (no persistence)
|
|
771
47
|
const { session } = await createAgentSession({
|
|
772
48
|
sessionManager: SessionManager.inMemory(),
|
|
773
49
|
});
|
|
774
|
-
|
|
775
|
-
// New persistent session
|
|
776
|
-
const { session: persisted } = await createAgentSession({
|
|
777
|
-
sessionManager: SessionManager.create(process.cwd()),
|
|
778
|
-
});
|
|
779
|
-
|
|
780
|
-
// Continue most recent
|
|
781
|
-
const { session: continued, modelFallbackMessage } = await createAgentSession({
|
|
782
|
-
sessionManager: SessionManager.continueRecent(process.cwd()),
|
|
783
|
-
});
|
|
784
|
-
if (modelFallbackMessage) {
|
|
785
|
-
console.log("Note:", modelFallbackMessage);
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// Open specific file
|
|
789
|
-
const { session: opened } = await createAgentSession({
|
|
790
|
-
sessionManager: SessionManager.open("/path/to/session.jsonl"),
|
|
791
|
-
});
|
|
792
|
-
|
|
793
|
-
// Resume a session kept outside the filesystem, e.g. in a database
|
|
794
|
-
const { session: restored } = await createAgentSession({
|
|
795
|
-
sessionManager: SessionManager.inMemory(process.cwd(), { id: sessionId }, entries),
|
|
796
|
-
});
|
|
797
|
-
|
|
798
|
-
// List sessions
|
|
799
|
-
const currentProjectSessions = await SessionManager.list(process.cwd());
|
|
800
|
-
const allSessions = await SessionManager.listAll(process.cwd());
|
|
801
|
-
|
|
802
|
-
// Session replacement API for /new, /resume, /fork, /clone, and import flows.
|
|
803
|
-
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
804
|
-
const services = await createAgentSessionServices({ cwd });
|
|
805
|
-
return {
|
|
806
|
-
...(await createAgentSessionFromServices({
|
|
807
|
-
services,
|
|
808
|
-
sessionManager,
|
|
809
|
-
sessionStartEvent,
|
|
810
|
-
})),
|
|
811
|
-
services,
|
|
812
|
-
diagnostics: services.diagnostics,
|
|
813
|
-
};
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
const runtime = await createAgentSessionRuntime(createRuntime, {
|
|
817
|
-
cwd: process.cwd(),
|
|
818
|
-
agentDir: getAgentDir(),
|
|
819
|
-
sessionManager: SessionManager.create(process.cwd()),
|
|
820
|
-
});
|
|
821
|
-
|
|
822
|
-
// Replace the active session with a fresh one
|
|
823
|
-
await runtime.newSession();
|
|
824
|
-
|
|
825
|
-
// Replace the active session with another saved session
|
|
826
|
-
await runtime.switchSession("/path/to/session.jsonl");
|
|
827
|
-
|
|
828
|
-
// Replace the active session with a fork from a specific user entry
|
|
829
|
-
await runtime.fork("entry-id");
|
|
830
|
-
|
|
831
|
-
// Clone the active path through a specific entry
|
|
832
|
-
await runtime.fork("entry-id", { position: "at" });
|
|
833
|
-
```
|
|
834
|
-
|
|
835
|
-
**SessionManager tree API:**
|
|
836
|
-
|
|
837
|
-
```typescript
|
|
838
|
-
const sm = SessionManager.open("/path/to/session.jsonl");
|
|
839
|
-
|
|
840
|
-
// Session listing
|
|
841
|
-
const currentProjectSessions = await SessionManager.list(process.cwd());
|
|
842
|
-
const allSessions = await SessionManager.listAll(process.cwd());
|
|
843
|
-
|
|
844
|
-
// Tree traversal
|
|
845
|
-
const entries = sm.getEntries(); // All entries (excludes header)
|
|
846
|
-
const tree = sm.getTree(); // Full tree structure
|
|
847
|
-
const path = sm.getPath(); // Path from root to current leaf
|
|
848
|
-
const leaf = sm.getLeafEntry(); // Current leaf entry
|
|
849
|
-
const entry = sm.getEntry(id); // Get entry by ID
|
|
850
|
-
const children = sm.getChildren(id); // Direct children of entry
|
|
851
|
-
|
|
852
|
-
// Labels
|
|
853
|
-
const label = sm.getLabel(id); // Get label for entry
|
|
854
|
-
sm.appendLabelChange(id, "checkpoint"); // Set label
|
|
855
|
-
|
|
856
|
-
// Branching
|
|
857
|
-
sm.branch(entryId); // Move leaf to earlier entry
|
|
858
|
-
sm.branchWithSummary(id, "Summary..."); // Branch with context summary
|
|
859
|
-
sm.createBranchedSession(leafId); // Extract path to new file
|
|
860
50
|
```
|
|
861
51
|
|
|
862
|
-
|
|
52
|
+
See the checked [sessions example](../examples/sdk/11-sessions.ts) for creating, opening, continuing, listing, and forking sessions. [Session File Format](session-format.md) defines the persisted JSONL contract, and [Message Types](message-types.md) defines transcript values. For exact methods and signatures, use the exported TypeScript declarations or [`session-manager.ts`](../src/core/session-manager.ts).
|
|
863
53
|
|
|
864
|
-
|
|
54
|
+
`cwd` selects the workspace used for project resource discovery, context files, session grouping, and built-in tool paths. Pass it explicitly when the target differs from `process.cwd()`.
|
|
865
55
|
|
|
866
|
-
|
|
867
|
-
import { createAgentSession, SettingsManager, SessionManager } from "@knightcodeai/cli";
|
|
868
|
-
|
|
869
|
-
// Default: loads from files (global + project merged)
|
|
870
|
-
const { session } = await createAgentSession({
|
|
871
|
-
settingsManager: SettingsManager.create(),
|
|
872
|
-
});
|
|
873
|
-
|
|
874
|
-
// With overrides
|
|
875
|
-
const settingsManager = SettingsManager.create();
|
|
876
|
-
settingsManager.applyOverrides({
|
|
877
|
-
compaction: { enabled: false },
|
|
878
|
-
retry: { enabled: true, maxRetries: 5 },
|
|
879
|
-
});
|
|
880
|
-
const { session } = await createAgentSession({ settingsManager });
|
|
881
|
-
|
|
882
|
-
// In-memory (no file I/O, for testing)
|
|
883
|
-
const { session } = await createAgentSession({
|
|
884
|
-
settingsManager: SettingsManager.inMemory({ compaction: { enabled: false } }),
|
|
885
|
-
sessionManager: SessionManager.inMemory(),
|
|
886
|
-
});
|
|
887
|
-
|
|
888
|
-
// Custom directories
|
|
889
|
-
const { session } = await createAgentSession({
|
|
890
|
-
settingsManager: SettingsManager.create("/custom/cwd", "/custom/agent"),
|
|
891
|
-
});
|
|
892
|
-
```
|
|
56
|
+
`session.dispose()` aborts active work, invalidates extension contexts, disconnects from the agent, and removes event listeners. Call it when the session is no longer needed.
|
|
893
57
|
|
|
894
|
-
|
|
895
|
-
- `SettingsManager.create(cwd?, agentDir?)` - Load from files
|
|
896
|
-
- `SettingsManager.inMemory(settings?)` - No file I/O
|
|
58
|
+
`AgentSessionRuntime` adds `newSession()`, `switchSession()`, `fork()`, and `importFromJsonl()`. Each operation replaces the active `AgentSession` and recreates services for the target working directory.
|
|
897
59
|
|
|
898
|
-
|
|
60
|
+
After a runtime replacement, subscriptions belong to the old `AgentSession` and must be rebound. See the [session runtime example](../examples/sdk/13-session-runtime.ts).
|
|
899
61
|
|
|
900
|
-
|
|
901
|
-
1. Global: `~/.knightcode/agent/settings.json`
|
|
902
|
-
2. Project: `<cwd>/.knightcode/settings.json`
|
|
62
|
+
## Prompting
|
|
903
63
|
|
|
904
|
-
|
|
64
|
+
`prompt()` handles extension commands and expands file-based prompt templates before ordinary user messages enter the agent. For an accepted agent run, it resolves after the run finishes, including automatic retries.
|
|
905
65
|
|
|
906
|
-
|
|
66
|
+
A prompt sent while the session is already streaming must specify whether it should steer the current run or follow it. Calling `prompt()` without that choice rejects rather than guessing.
|
|
907
67
|
|
|
908
|
-
|
|
909
|
-
- Setters enqueue persistence writes asynchronously.
|
|
910
|
-
- Call `await settingsManager.flush()` when you need a durability boundary (for example, before process exit or before asserting file contents in tests).
|
|
911
|
-
- `SettingsManager` does not print settings I/O errors. Use `settingsManager.drainErrors()` and report them in your app layer.
|
|
68
|
+
A steering message enters after the current assistant turn and its tool calls. A follow-up enters after the current run finishes its pending work. `steer()` and `followUp()` expose those behaviors directly.
|
|
912
69
|
|
|
913
|
-
|
|
70
|
+
`abort()` stops the active operation and waits for the session to become idle. `waitForIdle()` waits without aborting it.
|
|
914
71
|
|
|
915
|
-
##
|
|
72
|
+
## Subscribing to events
|
|
916
73
|
|
|
917
|
-
|
|
74
|
+
Subscribe before prompting when the host needs streamed output:
|
|
918
75
|
|
|
919
76
|
```typescript
|
|
920
|
-
|
|
921
|
-
DefaultResourceLoader,
|
|
922
|
-
getAgentDir,
|
|
923
|
-
} from "@knightcodeai/cli";
|
|
924
|
-
|
|
925
|
-
const loader = new DefaultResourceLoader({
|
|
926
|
-
cwd,
|
|
927
|
-
agentDir: getAgentDir(),
|
|
928
|
-
});
|
|
929
|
-
await loader.reload();
|
|
930
|
-
|
|
931
|
-
const extensions = loader.getExtensions();
|
|
932
|
-
const skills = loader.getSkills();
|
|
933
|
-
const prompts = loader.getPrompts();
|
|
934
|
-
const themes = loader.getThemes();
|
|
935
|
-
const contextFiles = loader.getAgentsFiles().agentsFiles;
|
|
936
|
-
```
|
|
937
|
-
|
|
938
|
-
## Return Value
|
|
939
|
-
|
|
940
|
-
`createAgentSession()` returns:
|
|
941
|
-
|
|
942
|
-
```typescript
|
|
943
|
-
interface CreateAgentSessionResult {
|
|
944
|
-
// The session
|
|
945
|
-
session: AgentSession;
|
|
946
|
-
|
|
947
|
-
// Extensions result (for runner setup)
|
|
948
|
-
extensionsResult: LoadExtensionsResult;
|
|
949
|
-
|
|
950
|
-
// Warning if session model couldn't be restored
|
|
951
|
-
modelFallbackMessage?: string;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
interface LoadExtensionsResult {
|
|
955
|
-
extensions: Extension[];
|
|
956
|
-
errors: Array<{ path: string; error: string }>;
|
|
957
|
-
runtime: ExtensionRuntime;
|
|
958
|
-
}
|
|
959
|
-
```
|
|
960
|
-
|
|
961
|
-
## Complete Example
|
|
962
|
-
|
|
963
|
-
```typescript
|
|
964
|
-
import { getModel } from "@knightcode/ai";
|
|
965
|
-
import { Type } from "typebox";
|
|
966
|
-
import {
|
|
967
|
-
createAgentSession,
|
|
968
|
-
DefaultResourceLoader,
|
|
969
|
-
defineTool,
|
|
970
|
-
ModelRuntime,
|
|
971
|
-
SessionManager,
|
|
972
|
-
SettingsManager,
|
|
973
|
-
} from "@knightcodeai/cli";
|
|
974
|
-
|
|
975
|
-
const modelRuntime = await ModelRuntime.create({
|
|
976
|
-
authPath: "/custom/agent/auth.json",
|
|
977
|
-
modelsPath: "/custom/agent/models.json",
|
|
978
|
-
});
|
|
979
|
-
if (process.env.MY_KEY) {
|
|
980
|
-
await modelRuntime.setRuntimeApiKey("anthropic", process.env.MY_KEY);
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
// Inline tool
|
|
984
|
-
const statusTool = defineTool({
|
|
985
|
-
name: "status",
|
|
986
|
-
label: "Status",
|
|
987
|
-
description: "Get system status",
|
|
988
|
-
parameters: Type.Object({}),
|
|
989
|
-
execute: async () => ({
|
|
990
|
-
content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }],
|
|
991
|
-
details: {},
|
|
992
|
-
}),
|
|
993
|
-
});
|
|
994
|
-
|
|
995
|
-
const model = getModel("anthropic", "claude-opus-4-5");
|
|
996
|
-
if (!model) throw new Error("Model not found");
|
|
997
|
-
|
|
998
|
-
// In-memory settings with overrides
|
|
999
|
-
const settingsManager = SettingsManager.inMemory({
|
|
1000
|
-
compaction: { enabled: false },
|
|
1001
|
-
retry: { enabled: true, maxRetries: 2 },
|
|
1002
|
-
});
|
|
1003
|
-
|
|
1004
|
-
const loader = new DefaultResourceLoader({
|
|
1005
|
-
cwd: process.cwd(),
|
|
1006
|
-
agentDir: "/custom/agent",
|
|
1007
|
-
settingsManager,
|
|
1008
|
-
systemPromptOverride: () => "You are a minimal assistant. Be concise.",
|
|
1009
|
-
});
|
|
1010
|
-
await loader.reload();
|
|
1011
|
-
|
|
1012
|
-
const { session } = await createAgentSession({
|
|
1013
|
-
cwd: process.cwd(),
|
|
1014
|
-
agentDir: "/custom/agent",
|
|
1015
|
-
|
|
1016
|
-
model,
|
|
1017
|
-
thinkingLevel: "off",
|
|
1018
|
-
modelRuntime,
|
|
1019
|
-
|
|
1020
|
-
tools: ["read", "bash", "status"],
|
|
1021
|
-
customTools: [statusTool],
|
|
1022
|
-
resourceLoader: loader,
|
|
1023
|
-
|
|
1024
|
-
sessionManager: SessionManager.inMemory(),
|
|
1025
|
-
settingsManager,
|
|
1026
|
-
});
|
|
1027
|
-
|
|
1028
|
-
session.subscribe((event) => {
|
|
77
|
+
const unsubscribe = session.subscribe((event) => {
|
|
1029
78
|
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
1030
79
|
process.stdout.write(event.assistantMessageEvent.delta);
|
|
1031
80
|
}
|
|
1032
81
|
});
|
|
1033
82
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
The SDK exports run mode utilities for building custom interfaces on top of `createAgentSession()`:
|
|
1040
|
-
|
|
1041
|
-
### InteractiveMode
|
|
1042
|
-
|
|
1043
|
-
Full TUI interactive mode with editor, chat history, and all built-in commands:
|
|
1044
|
-
|
|
1045
|
-
```typescript
|
|
1046
|
-
import {
|
|
1047
|
-
type CreateAgentSessionRuntimeFactory,
|
|
1048
|
-
createAgentSessionFromServices,
|
|
1049
|
-
createAgentSessionRuntime,
|
|
1050
|
-
createAgentSessionServices,
|
|
1051
|
-
getAgentDir,
|
|
1052
|
-
InteractiveMode,
|
|
1053
|
-
SessionManager,
|
|
1054
|
-
} from "@knightcodeai/cli";
|
|
1055
|
-
|
|
1056
|
-
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
1057
|
-
const services = await createAgentSessionServices({ cwd });
|
|
1058
|
-
return {
|
|
1059
|
-
...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent })),
|
|
1060
|
-
services,
|
|
1061
|
-
diagnostics: services.diagnostics,
|
|
1062
|
-
};
|
|
1063
|
-
};
|
|
1064
|
-
const runtime = await createAgentSessionRuntime(createRuntime, {
|
|
1065
|
-
cwd: process.cwd(),
|
|
1066
|
-
agentDir: getAgentDir(),
|
|
1067
|
-
sessionManager: SessionManager.create(process.cwd()),
|
|
1068
|
-
});
|
|
1069
|
-
|
|
1070
|
-
const mode = new InteractiveMode(runtime, {
|
|
1071
|
-
migratedProviders: [],
|
|
1072
|
-
modelFallbackMessage: undefined,
|
|
1073
|
-
initialMessage: "Hello",
|
|
1074
|
-
initialImages: [],
|
|
1075
|
-
initialMessages: [],
|
|
1076
|
-
});
|
|
1077
|
-
|
|
1078
|
-
await mode.run();
|
|
1079
|
-
```
|
|
1080
|
-
|
|
1081
|
-
### runPrintMode
|
|
1082
|
-
|
|
1083
|
-
Single-shot mode: send prompts, output result, exit:
|
|
1084
|
-
|
|
1085
|
-
```typescript
|
|
1086
|
-
import {
|
|
1087
|
-
type CreateAgentSessionRuntimeFactory,
|
|
1088
|
-
createAgentSessionFromServices,
|
|
1089
|
-
createAgentSessionRuntime,
|
|
1090
|
-
createAgentSessionServices,
|
|
1091
|
-
getAgentDir,
|
|
1092
|
-
runPrintMode,
|
|
1093
|
-
SessionManager,
|
|
1094
|
-
} from "@knightcodeai/cli";
|
|
1095
|
-
|
|
1096
|
-
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
1097
|
-
const services = await createAgentSessionServices({ cwd });
|
|
1098
|
-
return {
|
|
1099
|
-
...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent })),
|
|
1100
|
-
services,
|
|
1101
|
-
diagnostics: services.diagnostics,
|
|
1102
|
-
};
|
|
1103
|
-
};
|
|
1104
|
-
const runtime = await createAgentSessionRuntime(createRuntime, {
|
|
1105
|
-
cwd: process.cwd(),
|
|
1106
|
-
agentDir: getAgentDir(),
|
|
1107
|
-
sessionManager: SessionManager.create(process.cwd()),
|
|
1108
|
-
});
|
|
1109
|
-
|
|
1110
|
-
await runPrintMode(runtime, {
|
|
1111
|
-
mode: "text",
|
|
1112
|
-
initialMessage: "Hello",
|
|
1113
|
-
initialImages: [],
|
|
1114
|
-
messages: ["Follow up"],
|
|
1115
|
-
});
|
|
1116
|
-
```
|
|
1117
|
-
|
|
1118
|
-
### runRpcMode
|
|
1119
|
-
|
|
1120
|
-
JSON-RPC mode for subprocess integration:
|
|
1121
|
-
|
|
1122
|
-
```typescript
|
|
1123
|
-
import {
|
|
1124
|
-
type CreateAgentSessionRuntimeFactory,
|
|
1125
|
-
createAgentSessionFromServices,
|
|
1126
|
-
createAgentSessionRuntime,
|
|
1127
|
-
createAgentSessionServices,
|
|
1128
|
-
getAgentDir,
|
|
1129
|
-
runRpcMode,
|
|
1130
|
-
SessionManager,
|
|
1131
|
-
} from "@knightcodeai/cli";
|
|
1132
|
-
|
|
1133
|
-
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
1134
|
-
const services = await createAgentSessionServices({ cwd });
|
|
1135
|
-
return {
|
|
1136
|
-
...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent })),
|
|
1137
|
-
services,
|
|
1138
|
-
diagnostics: services.diagnostics,
|
|
1139
|
-
};
|
|
1140
|
-
};
|
|
1141
|
-
const runtime = await createAgentSessionRuntime(createRuntime, {
|
|
1142
|
-
cwd: process.cwd(),
|
|
1143
|
-
agentDir: getAgentDir(),
|
|
1144
|
-
sessionManager: SessionManager.create(process.cwd()),
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
await runRpcMode(runtime);
|
|
83
|
+
try {
|
|
84
|
+
await session.prompt("Explain this repository");
|
|
85
|
+
} finally {
|
|
86
|
+
unsubscribe();
|
|
87
|
+
}
|
|
1148
88
|
```
|
|
1149
89
|
|
|
1150
|
-
|
|
90
|
+
Session events report message updates, tool execution, queues, compaction, retries, and run lifecycle changes.
|
|
1151
91
|
|
|
1152
|
-
|
|
92
|
+
`message_end` contains the authoritative completed message. `agent_end` marks the end of one low-level agent run, but automatic recovery or queued work can still follow.
|
|
1153
93
|
|
|
1154
|
-
|
|
94
|
+
Use `agent_settled` when the host needs to know that KnightCode will not continue automatically.
|
|
1155
95
|
|
|
1156
|
-
|
|
1157
|
-
knightcode --mode rpc --no-session
|
|
1158
|
-
```
|
|
96
|
+
## Configuring a session
|
|
1159
97
|
|
|
1160
|
-
|
|
98
|
+
Without overrides, the factory creates a `ModelRuntime`, file-backed `SettingsManager`, persistent `SessionManager`, `DefaultResourceLoader`, and the configured default tools.
|
|
1161
99
|
|
|
1162
|
-
|
|
1163
|
-
- You want type safety
|
|
1164
|
-
- You're in the same Node.js process
|
|
1165
|
-
- You need direct access to agent state
|
|
1166
|
-
- You want to customize tools/extensions programmatically
|
|
100
|
+
Each boundary can be supplied explicitly:
|
|
1167
101
|
|
|
1168
|
-
|
|
1169
|
-
-
|
|
1170
|
-
-
|
|
1171
|
-
-
|
|
102
|
+
- `modelRuntime`, `model`, `thinkingLevel`, and `scopedModels` control model access and selection.
|
|
103
|
+
- `settingsManager` supplies merged settings or an in-memory configuration.
|
|
104
|
+
- `sessionManager` supplies persistent or in-memory conversation history.
|
|
105
|
+
- `resourceLoader` supplies extensions, skills, prompt templates, themes, and context files.
|
|
106
|
+
- `tools`, `noTools`, `excludeTools`, and `customTools` control the active tool set.
|
|
1172
107
|
|
|
1173
|
-
|
|
108
|
+
Use `DefaultResourceLoader` when you want standard discovery with selected overrides. Supply a custom `ResourceLoader` when the host owns resource storage and discovery completely.
|
|
1174
109
|
|
|
1175
|
-
|
|
110
|
+
<a id="inlineextension"></a>
|
|
1176
111
|
|
|
1177
|
-
|
|
1178
|
-
// Factory
|
|
1179
|
-
createAgentSession
|
|
1180
|
-
createAgentSessionRuntime
|
|
1181
|
-
AgentSessionRuntime
|
|
112
|
+
Inline extension factories can be supplied through `DefaultResourceLoader`. Give one an `InlineExtension` name only when it needs a stable name in diagnostics and startup output.
|
|
1182
113
|
|
|
1183
|
-
|
|
1184
|
-
ModelRuntime // implements @knightcode/ai Models and owns credential storage
|
|
1185
|
-
ModelRegistry // synchronous extension compatibility facade
|
|
1186
|
-
CredentialSynchronizationError
|
|
1187
|
-
resolveCliModel
|
|
1188
|
-
resolveModelScopeWithDiagnostics
|
|
114
|
+
See the focused examples for [models](../examples/sdk/02-custom-model.ts), [tools](../examples/sdk/05-tools.ts), [extensions](../examples/sdk/06-extensions.ts), and [full control](../examples/sdk/12-full-control.ts).
|
|
1189
115
|
|
|
1190
|
-
|
|
1191
|
-
DefaultResourceLoader
|
|
1192
|
-
type ResourceLoader
|
|
1193
|
-
createEventBus
|
|
116
|
+
## Examples
|
|
1194
117
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
118
|
+
| Example | Purpose |
|
|
119
|
+
|---|---|
|
|
120
|
+
| [Minimal](../examples/sdk/01-minimal.ts) | Create, prompt, observe, and dispose a session |
|
|
121
|
+
| [Custom model](../examples/sdk/02-custom-model.ts) | Select a model and thinking level |
|
|
122
|
+
| [System prompt](../examples/sdk/03-custom-prompt.ts) | Replace or append to the system prompt |
|
|
123
|
+
| [Skills](../examples/sdk/04-skills.ts) | Discover, filter, and add skills |
|
|
124
|
+
| [Tools](../examples/sdk/05-tools.ts) | Select built-in tools and their working directory |
|
|
125
|
+
| [Extensions](../examples/sdk/06-extensions.ts) | Load file-based and inline extensions |
|
|
126
|
+
| [Context files](../examples/sdk/07-context-files.ts) | Add or replace project instructions |
|
|
127
|
+
| [Prompt templates](../examples/sdk/08-prompt-templates.ts) | Add file-style prompt templates |
|
|
128
|
+
| [Credentials](../examples/sdk/09-api-keys-and-oauth.ts) | Configure credential and model storage |
|
|
129
|
+
| [Settings](../examples/sdk/10-settings.ts) | Supply file-backed or in-memory settings |
|
|
130
|
+
| [Sessions](../examples/sdk/11-sessions.ts) | Control session persistence and restoration |
|
|
131
|
+
| [Full control](../examples/sdk/12-full-control.ts) | Replace default discovery and state services |
|
|
132
|
+
| [Session runtime](../examples/sdk/13-session-runtime.ts) | Replace the active session safely |
|
|
1203
133
|
|
|
1204
|
-
|
|
1205
|
-
SessionManager
|
|
1206
|
-
SettingsManager
|
|
134
|
+
<a id="exports"></a>
|
|
1207
135
|
|
|
1208
|
-
|
|
1209
|
-
createCodingTools
|
|
1210
|
-
createReadOnlyTools
|
|
1211
|
-
createReadTool, createBashTool, createPowerShellTool, createEditTool, createWriteTool
|
|
1212
|
-
createGrepTool, createFindTool, createLsTool
|
|
1213
|
-
|
|
1214
|
-
// Types
|
|
1215
|
-
type CreateAgentSessionOptions
|
|
1216
|
-
type CreateAgentSessionResult
|
|
1217
|
-
type ExtensionFactory
|
|
1218
|
-
type InlineExtension
|
|
1219
|
-
type ExtensionAPI
|
|
1220
|
-
type ToolDefinition
|
|
1221
|
-
type Skill
|
|
1222
|
-
type PromptTemplate
|
|
1223
|
-
type Tool
|
|
1224
|
-
```
|
|
136
|
+
## Resources
|
|
1225
137
|
|
|
1226
|
-
|
|
138
|
+
- [Choose a Model](models.md) covers model selection and compatible endpoints; [Provider Authentication](providers.md) covers credentials and cloud-provider setup.
|
|
139
|
+
- [Configuration](configuration.md) explains normal discovery and settings; [Settings](settings.md) lists every setting.
|
|
140
|
+
- [Sessions and Context](sessions.md) explains session behavior; [Session Format](session-format.md) defines persisted entries; [Message Types](message-types.md) defines shared transcript values.
|
|
141
|
+
- [Extensions](extensions.md), [Skills](skills.md), and [Prompt Templates](prompt-templates.md) document resources supplied through a `ResourceLoader`.
|
|
142
|
+
- [CLI Integration](cli-integration.md) covers print, JSON, and RPC alternatives to an in-process SDK integration.
|