@draht/coding-agent 2026.3.25-1 → 2026.4.5
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 +107 -0
- package/README.md +6 -2
- package/dist/core/agent-session-runtime.d.ts +136 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -0
- package/dist/core/agent-session-runtime.js +267 -0
- package/dist/core/agent-session-runtime.js.map +1 -0
- package/dist/core/agent-session.d.ts +22 -44
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +44 -248
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +3 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +5 -2
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +2 -2
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +2 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +9 -9
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts +2 -0
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +2 -2
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +16 -16
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +10 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +5 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +70 -8
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/model-registry.d.ts +21 -3
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +90 -70
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +4 -4
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts +6 -0
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js +37 -5
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts +2 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +5 -1
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +6 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +17 -23
- package/dist/core/sdk.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +49 -10
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +4 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +8 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +90 -87
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +6 -11
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +4 -4
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +87 -74
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +2 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +69 -49
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/docs/development.md +2 -2
- package/docs/extensions.md +78 -22
- package/docs/models.md +6 -0
- package/docs/packages.md +3 -3
- package/docs/rpc.md +2 -2
- package/docs/sdk.md +170 -82
- package/docs/tree.md +1 -1
- package/examples/extensions/custom-compaction.ts +17 -4
- package/examples/extensions/handoff.ts +5 -2
- package/examples/extensions/hello.ts +18 -17
- package/examples/extensions/qna.ts +5 -2
- package/examples/extensions/rpc-demo.ts +3 -9
- package/examples/extensions/status-line.ts +0 -8
- package/examples/extensions/subagent/index.ts +1 -1
- package/examples/extensions/summarize.ts +15 -4
- package/examples/extensions/todo.ts +0 -2
- package/examples/extensions/tools.ts +0 -5
- package/examples/extensions/widget-placement.ts +4 -12
- package/examples/sdk/02-custom-model.ts +1 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +3 -3
- package/examples/sdk/12-full-control.ts +1 -1
- package/examples/sdk/13-session-runtime.ts +49 -0
- package/examples/sdk/README.md +5 -4
- package/package.json +4 -4
package/docs/sdk.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
>
|
|
1
|
+
> draht can help you use the SDK. Ask it to build an integration for your use case.
|
|
2
2
|
|
|
3
3
|
# SDK
|
|
4
4
|
|
|
5
|
-
The SDK provides programmatic access to
|
|
5
|
+
The SDK provides programmatic access to draht's agent capabilities. Use it to embed draht in other applications, build custom interfaces, or integrate with automated workflows.
|
|
6
6
|
|
|
7
7
|
**Example use cases:**
|
|
8
8
|
- Build a custom UI (web, desktop, mobile)
|
|
@@ -20,7 +20,7 @@ import { AuthStorage, createAgentSession, ModelRegistry, SessionManager } from "
|
|
|
20
20
|
|
|
21
21
|
// Set up credential storage and model registry
|
|
22
22
|
const authStorage = AuthStorage.create();
|
|
23
|
-
const modelRegistry =
|
|
23
|
+
const modelRegistry = ModelRegistry.create(authStorage);
|
|
24
24
|
|
|
25
25
|
const { session } = await createAgentSession({
|
|
26
26
|
sessionManager: SessionManager.inMemory(),
|
|
@@ -49,7 +49,7 @@ The SDK is included in the main package. No separate installation needed.
|
|
|
49
49
|
|
|
50
50
|
### createAgentSession()
|
|
51
51
|
|
|
52
|
-
The main factory function
|
|
52
|
+
The main factory function for a single `AgentSession`.
|
|
53
53
|
|
|
54
54
|
`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.
|
|
55
55
|
|
|
@@ -69,61 +69,102 @@ const { session } = await createAgentSession({
|
|
|
69
69
|
|
|
70
70
|
### AgentSession
|
|
71
71
|
|
|
72
|
-
The session manages
|
|
72
|
+
The session manages agent lifecycle, message history, model state, compaction, and event streaming.
|
|
73
73
|
|
|
74
74
|
```typescript
|
|
75
75
|
interface AgentSession {
|
|
76
76
|
// Send a prompt and wait for completion
|
|
77
|
-
// If streaming, requires streamingBehavior option to queue the message
|
|
78
77
|
prompt(text: string, options?: PromptOptions): Promise<void>;
|
|
79
|
-
|
|
78
|
+
|
|
80
79
|
// Queue messages during streaming
|
|
81
|
-
steer(text: string): Promise<void>;
|
|
82
|
-
followUp(text: string): Promise<void>;
|
|
83
|
-
|
|
80
|
+
steer(text: string): Promise<void>;
|
|
81
|
+
followUp(text: string): Promise<void>;
|
|
82
|
+
|
|
84
83
|
// Subscribe to events (returns unsubscribe function)
|
|
85
84
|
subscribe(listener: (event: AgentSessionEvent) => void): () => void;
|
|
86
|
-
|
|
85
|
+
|
|
87
86
|
// Session info
|
|
88
|
-
sessionFile: string | undefined;
|
|
87
|
+
sessionFile: string | undefined;
|
|
89
88
|
sessionId: string;
|
|
90
|
-
|
|
89
|
+
|
|
91
90
|
// Model control
|
|
92
91
|
setModel(model: Model): Promise<void>;
|
|
93
92
|
setThinkingLevel(level: ThinkingLevel): void;
|
|
94
93
|
cycleModel(): Promise<ModelCycleResult | undefined>;
|
|
95
94
|
cycleThinkingLevel(): ThinkingLevel | undefined;
|
|
96
|
-
|
|
95
|
+
|
|
97
96
|
// State access
|
|
98
97
|
agent: Agent;
|
|
99
98
|
model: Model | undefined;
|
|
100
99
|
thinkingLevel: ThinkingLevel;
|
|
101
100
|
messages: AgentMessage[];
|
|
102
101
|
isStreaming: boolean;
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// Forking
|
|
109
|
-
fork(entryId: string): Promise<{ selectedText: string; cancelled: boolean }>; // Creates new session file
|
|
110
|
-
navigateTree(targetId: string, options?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string }): Promise<{ editorText?: string; cancelled: boolean }>; // In-place navigation
|
|
111
|
-
|
|
112
|
-
// Hook message injection
|
|
113
|
-
sendHookMessage(message: HookMessage, triggerTurn?: boolean): Promise<void>;
|
|
114
|
-
|
|
102
|
+
|
|
103
|
+
// In-place tree navigation within the current session file
|
|
104
|
+
navigateTree(targetId: string, options?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string }): Promise<{ editorText?: string; cancelled: boolean }>;
|
|
105
|
+
|
|
115
106
|
// Compaction
|
|
116
107
|
compact(customInstructions?: string): Promise<CompactionResult>;
|
|
117
108
|
abortCompaction(): void;
|
|
118
|
-
|
|
109
|
+
|
|
119
110
|
// Abort current operation
|
|
120
111
|
abort(): Promise<void>;
|
|
121
|
-
|
|
112
|
+
|
|
122
113
|
// Cleanup
|
|
123
114
|
dispose(): void;
|
|
124
115
|
}
|
|
125
116
|
```
|
|
126
117
|
|
|
118
|
+
Session replacement APIs such as new-session, resume, fork, and import live on `AgentSessionRuntimeHost`, not on `AgentSession`.
|
|
119
|
+
|
|
120
|
+
### createAgentSessionRuntime() and AgentSessionRuntimeHost
|
|
121
|
+
|
|
122
|
+
Use the runtime API when you need to replace the active session and rebuild cwd-bound runtime state.
|
|
123
|
+
This is the same layer used by the built-in interactive, print, and RPC modes.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import {
|
|
127
|
+
AgentSessionRuntimeHost,
|
|
128
|
+
createAgentSessionRuntime,
|
|
129
|
+
SessionManager,
|
|
130
|
+
} from "@draht/coding-agent";
|
|
131
|
+
|
|
132
|
+
const bootstrap = {
|
|
133
|
+
// Optional: authStorage, model, thinkingLevel, tools, customTools, resourceLoader
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const runtime = await createAgentSessionRuntime(bootstrap, {
|
|
137
|
+
cwd: process.cwd(),
|
|
138
|
+
sessionManager: SessionManager.create(process.cwd()),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const runtimeHost = new AgentSessionRuntimeHost(bootstrap, runtime);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`createAgentSessionRuntime()` returns an internal runtime bundle. `AgentSessionRuntimeHost` owns replacement of that bundle across:
|
|
145
|
+
|
|
146
|
+
- `newSession()`
|
|
147
|
+
- `switchSession()`
|
|
148
|
+
- `fork()`
|
|
149
|
+
- `importFromJsonl()`
|
|
150
|
+
|
|
151
|
+
Important behavior:
|
|
152
|
+
|
|
153
|
+
- `runtimeHost.session` changes after those operations
|
|
154
|
+
- event subscriptions are attached to a specific `AgentSession`, so re-subscribe after replacement
|
|
155
|
+
- if you use extensions, call `runtimeHost.session.bindExtensions(...)` again for the new session
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
let session = runtimeHost.session;
|
|
159
|
+
let unsubscribe = session.subscribe(() => {});
|
|
160
|
+
|
|
161
|
+
await runtimeHost.newSession();
|
|
162
|
+
|
|
163
|
+
unsubscribe();
|
|
164
|
+
session = runtimeHost.session;
|
|
165
|
+
unsubscribe = session.subscribe(() => {});
|
|
166
|
+
```
|
|
167
|
+
|
|
127
168
|
### Prompting and Message Queueing
|
|
128
169
|
|
|
129
170
|
The `prompt()` method handles prompt templates, extension commands, and message sending:
|
|
@@ -286,7 +327,7 @@ import { getModel } from "@draht/ai";
|
|
|
286
327
|
import { AuthStorage, ModelRegistry } from "@draht/coding-agent";
|
|
287
328
|
|
|
288
329
|
const authStorage = AuthStorage.create();
|
|
289
|
-
const modelRegistry =
|
|
330
|
+
const modelRegistry = ModelRegistry.create(authStorage);
|
|
290
331
|
|
|
291
332
|
// Find specific built-in model (doesn't check if API key exists)
|
|
292
333
|
const opus = getModel("anthropic", "claude-opus-4-5");
|
|
@@ -334,7 +375,7 @@ import { AuthStorage, ModelRegistry } from "@draht/coding-agent";
|
|
|
334
375
|
|
|
335
376
|
// Default: uses ~/.pi/agent/auth.json and ~/.pi/agent/models.json
|
|
336
377
|
const authStorage = AuthStorage.create();
|
|
337
|
-
const modelRegistry =
|
|
378
|
+
const modelRegistry = ModelRegistry.create(authStorage);
|
|
338
379
|
|
|
339
380
|
const { session } = await createAgentSession({
|
|
340
381
|
sessionManager: SessionManager.inMemory(),
|
|
@@ -347,7 +388,7 @@ authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key");
|
|
|
347
388
|
|
|
348
389
|
// Custom auth storage location
|
|
349
390
|
const customAuth = AuthStorage.create("/my/app/auth.json");
|
|
350
|
-
const customRegistry =
|
|
391
|
+
const customRegistry = ModelRegistry.create(customAuth, "/my/app/models.json");
|
|
351
392
|
|
|
352
393
|
const { session } = await createAgentSession({
|
|
353
394
|
sessionManager: SessionManager.inMemory(),
|
|
@@ -356,7 +397,7 @@ const { session } = await createAgentSession({
|
|
|
356
397
|
});
|
|
357
398
|
|
|
358
399
|
// No custom models.json (built-in models only)
|
|
359
|
-
const simpleRegistry =
|
|
400
|
+
const simpleRegistry = ModelRegistry.inMemory(authStorage);
|
|
360
401
|
```
|
|
361
402
|
|
|
362
403
|
> See [examples/sdk/09-api-keys-and-oauth.ts](../examples/sdk/09-api-keys-and-oauth.ts)
|
|
@@ -432,7 +473,7 @@ const { session } = await createAgentSession({
|
|
|
432
473
|
```
|
|
433
474
|
|
|
434
475
|
**When you don't need factories:**
|
|
435
|
-
- If you omit `tools`,
|
|
476
|
+
- If you omit `tools`, draht automatically creates them with the correct `cwd`
|
|
436
477
|
- If you use `process.cwd()` as your `cwd`, the pre-built instances work fine
|
|
437
478
|
|
|
438
479
|
**When you must use factories:**
|
|
@@ -444,21 +485,21 @@ const { session } = await createAgentSession({
|
|
|
444
485
|
|
|
445
486
|
```typescript
|
|
446
487
|
import { Type } from "@sinclair/typebox";
|
|
447
|
-
import { createAgentSession,
|
|
488
|
+
import { createAgentSession, defineTool } from "@draht/coding-agent";
|
|
448
489
|
|
|
449
490
|
// Inline custom tool
|
|
450
|
-
const myTool
|
|
491
|
+
const myTool = defineTool({
|
|
451
492
|
name: "my_tool",
|
|
452
493
|
label: "My Tool",
|
|
453
494
|
description: "Does something useful",
|
|
454
495
|
parameters: Type.Object({
|
|
455
496
|
input: Type.String({ description: "Input value" }),
|
|
456
497
|
}),
|
|
457
|
-
execute: async (
|
|
498
|
+
execute: async (_toolCallId, params) => ({
|
|
458
499
|
content: [{ type: "text", text: `Result: ${params.input}` }],
|
|
459
500
|
details: {},
|
|
460
501
|
}),
|
|
461
|
-
};
|
|
502
|
+
});
|
|
462
503
|
|
|
463
504
|
// Pass custom tools directly
|
|
464
505
|
const { session } = await createAgentSession({
|
|
@@ -466,6 +507,8 @@ const { session } = await createAgentSession({
|
|
|
466
507
|
});
|
|
467
508
|
```
|
|
468
509
|
|
|
510
|
+
Use `defineTool()` for standalone definitions and arrays like `customTools: [myTool]`. Inline `pi.registerTool({ ... })` already infers parameter types correctly.
|
|
511
|
+
|
|
469
512
|
Custom tools passed via `customTools` are combined with extension-registered tools. Extensions loaded by the ResourceLoader can also register tools via `pi.registerTool()`.
|
|
470
513
|
|
|
471
514
|
> See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
|
|
@@ -594,7 +637,12 @@ const { session } = await createAgentSession({ resourceLoader: loader });
|
|
|
594
637
|
Sessions use a tree structure with `id`/`parentId` linking, enabling in-place branching.
|
|
595
638
|
|
|
596
639
|
```typescript
|
|
597
|
-
import {
|
|
640
|
+
import {
|
|
641
|
+
AgentSessionRuntimeHost,
|
|
642
|
+
createAgentSession,
|
|
643
|
+
createAgentSessionRuntime,
|
|
644
|
+
SessionManager,
|
|
645
|
+
} from "@draht/coding-agent";
|
|
598
646
|
|
|
599
647
|
// In-memory (no persistence)
|
|
600
648
|
const { session } = await createAgentSession({
|
|
@@ -602,12 +650,12 @@ const { session } = await createAgentSession({
|
|
|
602
650
|
});
|
|
603
651
|
|
|
604
652
|
// New persistent session
|
|
605
|
-
const { session } = await createAgentSession({
|
|
653
|
+
const { session: persisted } = await createAgentSession({
|
|
606
654
|
sessionManager: SessionManager.create(process.cwd()),
|
|
607
655
|
});
|
|
608
656
|
|
|
609
657
|
// Continue most recent
|
|
610
|
-
const { session, modelFallbackMessage } = await createAgentSession({
|
|
658
|
+
const { session: continued, modelFallbackMessage } = await createAgentSession({
|
|
611
659
|
sessionManager: SessionManager.continueRecent(process.cwd()),
|
|
612
660
|
});
|
|
613
661
|
if (modelFallbackMessage) {
|
|
@@ -615,26 +663,30 @@ if (modelFallbackMessage) {
|
|
|
615
663
|
}
|
|
616
664
|
|
|
617
665
|
// Open specific file
|
|
618
|
-
const { session } = await createAgentSession({
|
|
666
|
+
const { session: opened } = await createAgentSession({
|
|
619
667
|
sessionManager: SessionManager.open("/path/to/session.jsonl"),
|
|
620
668
|
});
|
|
621
669
|
|
|
622
|
-
// List
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
console.log(`${info.id}: ${info.firstMessage} (${info.messageCount} messages, cwd: ${info.cwd})`);
|
|
626
|
-
}
|
|
670
|
+
// List sessions
|
|
671
|
+
const currentProjectSessions = await SessionManager.list(process.cwd());
|
|
672
|
+
const allSessions = await SessionManager.listAll(process.cwd());
|
|
627
673
|
|
|
628
|
-
//
|
|
629
|
-
const
|
|
630
|
-
|
|
674
|
+
// Session replacement API for /new, /resume, /fork, and import flows.
|
|
675
|
+
const bootstrap = {};
|
|
676
|
+
const runtime = await createAgentSessionRuntime(bootstrap, {
|
|
677
|
+
cwd: process.cwd(),
|
|
678
|
+
sessionManager: SessionManager.create(process.cwd()),
|
|
631
679
|
});
|
|
680
|
+
const runtimeHost = new AgentSessionRuntimeHost(bootstrap, runtime);
|
|
632
681
|
|
|
633
|
-
//
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
682
|
+
// Replace the active session with a fresh one
|
|
683
|
+
await runtimeHost.newSession();
|
|
684
|
+
|
|
685
|
+
// Replace the active session with another saved session
|
|
686
|
+
await runtimeHost.switchSession("/path/to/session.jsonl");
|
|
687
|
+
|
|
688
|
+
// Replace the active session with a fork from a specific entry
|
|
689
|
+
await runtimeHost.fork("entry-id");
|
|
638
690
|
```
|
|
639
691
|
|
|
640
692
|
**SessionManager tree API:**
|
|
@@ -642,6 +694,10 @@ const { session } = await createAgentSession({
|
|
|
642
694
|
```typescript
|
|
643
695
|
const sm = SessionManager.open("/path/to/session.jsonl");
|
|
644
696
|
|
|
697
|
+
// Session listing
|
|
698
|
+
const currentProjectSessions = await SessionManager.list(process.cwd());
|
|
699
|
+
const allSessions = await SessionManager.listAll(process.cwd());
|
|
700
|
+
|
|
645
701
|
// Tree traversal
|
|
646
702
|
const entries = sm.getEntries(); // All entries (excludes header)
|
|
647
703
|
const tree = sm.getTree(); // Full tree structure
|
|
@@ -766,14 +822,14 @@ import { getModel } from "@draht/ai";
|
|
|
766
822
|
import { Type } from "@sinclair/typebox";
|
|
767
823
|
import {
|
|
768
824
|
AuthStorage,
|
|
825
|
+
bashTool,
|
|
769
826
|
createAgentSession,
|
|
770
827
|
DefaultResourceLoader,
|
|
828
|
+
defineTool,
|
|
771
829
|
ModelRegistry,
|
|
830
|
+
readTool,
|
|
772
831
|
SessionManager,
|
|
773
832
|
SettingsManager,
|
|
774
|
-
readTool,
|
|
775
|
-
bashTool,
|
|
776
|
-
type ToolDefinition,
|
|
777
833
|
} from "@draht/coding-agent";
|
|
778
834
|
|
|
779
835
|
// Set up auth storage (custom location)
|
|
@@ -785,10 +841,10 @@ if (process.env.MY_KEY) {
|
|
|
785
841
|
}
|
|
786
842
|
|
|
787
843
|
// Model registry (no custom models.json)
|
|
788
|
-
const modelRegistry =
|
|
844
|
+
const modelRegistry = ModelRegistry.create(authStorage);
|
|
789
845
|
|
|
790
846
|
// Inline tool
|
|
791
|
-
const statusTool
|
|
847
|
+
const statusTool = defineTool({
|
|
792
848
|
name: "status",
|
|
793
849
|
label: "Status",
|
|
794
850
|
description: "Get system status",
|
|
@@ -797,7 +853,7 @@ const statusTool: ToolDefinition = {
|
|
|
797
853
|
content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }],
|
|
798
854
|
details: {},
|
|
799
855
|
}),
|
|
800
|
-
};
|
|
856
|
+
});
|
|
801
857
|
|
|
802
858
|
const model = getModel("anthropic", "claude-opus-4-5");
|
|
803
859
|
if (!model) throw new Error("Model not found");
|
|
@@ -851,20 +907,29 @@ The SDK exports run mode utilities for building custom interfaces on top of `cre
|
|
|
851
907
|
Full TUI interactive mode with editor, chat history, and all built-in commands:
|
|
852
908
|
|
|
853
909
|
```typescript
|
|
854
|
-
import {
|
|
855
|
-
|
|
856
|
-
|
|
910
|
+
import {
|
|
911
|
+
AgentSessionRuntimeHost,
|
|
912
|
+
createAgentSessionRuntime,
|
|
913
|
+
InteractiveMode,
|
|
914
|
+
SessionManager,
|
|
915
|
+
} from "@draht/coding-agent";
|
|
857
916
|
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
917
|
+
const bootstrap = {};
|
|
918
|
+
const runtime = await createAgentSessionRuntime(bootstrap, {
|
|
919
|
+
cwd: process.cwd(),
|
|
920
|
+
sessionManager: SessionManager.create(process.cwd()),
|
|
921
|
+
});
|
|
922
|
+
const runtimeHost = new AgentSessionRuntimeHost(bootstrap, runtime);
|
|
923
|
+
|
|
924
|
+
const mode = new InteractiveMode(runtimeHost, {
|
|
925
|
+
migratedProviders: [],
|
|
926
|
+
modelFallbackMessage: undefined,
|
|
927
|
+
initialMessage: "Hello",
|
|
928
|
+
initialImages: [],
|
|
929
|
+
initialMessages: [],
|
|
865
930
|
});
|
|
866
931
|
|
|
867
|
-
await mode.run();
|
|
932
|
+
await mode.run();
|
|
868
933
|
```
|
|
869
934
|
|
|
870
935
|
### runPrintMode
|
|
@@ -872,15 +937,25 @@ await mode.run(); // Blocks until exit
|
|
|
872
937
|
Single-shot mode: send prompts, output result, exit:
|
|
873
938
|
|
|
874
939
|
```typescript
|
|
875
|
-
import {
|
|
940
|
+
import {
|
|
941
|
+
AgentSessionRuntimeHost,
|
|
942
|
+
createAgentSessionRuntime,
|
|
943
|
+
runPrintMode,
|
|
944
|
+
SessionManager,
|
|
945
|
+
} from "@draht/coding-agent";
|
|
876
946
|
|
|
877
|
-
const
|
|
947
|
+
const bootstrap = {};
|
|
948
|
+
const runtime = await createAgentSessionRuntime(bootstrap, {
|
|
949
|
+
cwd: process.cwd(),
|
|
950
|
+
sessionManager: SessionManager.create(process.cwd()),
|
|
951
|
+
});
|
|
952
|
+
const runtimeHost = new AgentSessionRuntimeHost(bootstrap, runtime);
|
|
878
953
|
|
|
879
|
-
await runPrintMode(
|
|
880
|
-
mode: "text",
|
|
881
|
-
initialMessage: "Hello",
|
|
882
|
-
initialImages: [],
|
|
883
|
-
messages: ["Follow up"],
|
|
954
|
+
await runPrintMode(runtimeHost, {
|
|
955
|
+
mode: "text",
|
|
956
|
+
initialMessage: "Hello",
|
|
957
|
+
initialImages: [],
|
|
958
|
+
messages: ["Follow up"],
|
|
884
959
|
});
|
|
885
960
|
```
|
|
886
961
|
|
|
@@ -889,11 +964,21 @@ await runPrintMode(session, {
|
|
|
889
964
|
JSON-RPC mode for subprocess integration:
|
|
890
965
|
|
|
891
966
|
```typescript
|
|
892
|
-
import {
|
|
967
|
+
import {
|
|
968
|
+
AgentSessionRuntimeHost,
|
|
969
|
+
createAgentSessionRuntime,
|
|
970
|
+
runRpcMode,
|
|
971
|
+
SessionManager,
|
|
972
|
+
} from "@draht/coding-agent";
|
|
893
973
|
|
|
894
|
-
const
|
|
974
|
+
const bootstrap = {};
|
|
975
|
+
const runtime = await createAgentSessionRuntime(bootstrap, {
|
|
976
|
+
cwd: process.cwd(),
|
|
977
|
+
sessionManager: SessionManager.create(process.cwd()),
|
|
978
|
+
});
|
|
979
|
+
const runtimeHost = new AgentSessionRuntimeHost(bootstrap, runtime);
|
|
895
980
|
|
|
896
|
-
await runRpcMode(
|
|
981
|
+
await runRpcMode(runtimeHost);
|
|
897
982
|
```
|
|
898
983
|
|
|
899
984
|
See [RPC documentation](rpc.md) for the JSON protocol.
|
|
@@ -903,7 +988,7 @@ See [RPC documentation](rpc.md) for the JSON protocol.
|
|
|
903
988
|
For subprocess-based integration without building with the SDK, use the CLI directly:
|
|
904
989
|
|
|
905
990
|
```bash
|
|
906
|
-
|
|
991
|
+
draht --mode rpc --no-session
|
|
907
992
|
```
|
|
908
993
|
|
|
909
994
|
See [RPC documentation](rpc.md) for the JSON protocol.
|
|
@@ -926,6 +1011,8 @@ The main entry point exports:
|
|
|
926
1011
|
```typescript
|
|
927
1012
|
// Factory
|
|
928
1013
|
createAgentSession
|
|
1014
|
+
createAgentSessionRuntime
|
|
1015
|
+
AgentSessionRuntimeHost
|
|
929
1016
|
|
|
930
1017
|
// Auth and Models
|
|
931
1018
|
AuthStorage
|
|
@@ -937,6 +1024,7 @@ type ResourceLoader
|
|
|
937
1024
|
createEventBus
|
|
938
1025
|
|
|
939
1026
|
// Helpers
|
|
1027
|
+
defineTool
|
|
940
1028
|
|
|
941
1029
|
// Session management
|
|
942
1030
|
SessionManager
|
package/docs/tree.md
CHANGED
|
@@ -13,7 +13,7 @@ Sessions are stored as trees where each entry has an `id` and `parentId`. The "l
|
|
|
13
13
|
| View | Flat list of user messages | Full tree structure |
|
|
14
14
|
| Action | Extracts path to **new session file** | Changes leaf in **same session** |
|
|
15
15
|
| Summary | Never | Optional (user prompted) |
|
|
16
|
-
| Events | `session_before_fork` / `
|
|
16
|
+
| Events | `session_before_fork` / `session_start` (`reason: "fork"`) | `session_before_tree` / `session_tree` |
|
|
17
17
|
|
|
18
18
|
## Tree UI
|
|
19
19
|
|
|
@@ -31,9 +31,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// Resolve
|
|
35
|
-
const
|
|
36
|
-
if (!
|
|
34
|
+
// Resolve request auth for the summarization model
|
|
35
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
36
|
+
if (!auth.ok) {
|
|
37
|
+
ctx.ui.notify(`Compaction auth failed: ${auth.error}`, "warning");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!auth.apiKey) {
|
|
37
41
|
ctx.ui.notify(`No API key for ${model.provider}, using default compaction`, "warning");
|
|
38
42
|
return;
|
|
39
43
|
}
|
|
@@ -83,7 +87,16 @@ ${conversationText}
|
|
|
83
87
|
|
|
84
88
|
try {
|
|
85
89
|
// Pass signal to honor abort requests (e.g., user cancels compaction)
|
|
86
|
-
const response = await complete(
|
|
90
|
+
const response = await complete(
|
|
91
|
+
model,
|
|
92
|
+
{ messages: summaryMessages },
|
|
93
|
+
{
|
|
94
|
+
apiKey: auth.apiKey,
|
|
95
|
+
headers: auth.headers,
|
|
96
|
+
maxTokens: 8192,
|
|
97
|
+
signal,
|
|
98
|
+
},
|
|
99
|
+
);
|
|
87
100
|
|
|
88
101
|
const summary = response.content
|
|
89
102
|
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
@@ -80,7 +80,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
80
80
|
loader.onAbort = () => done(null);
|
|
81
81
|
|
|
82
82
|
const doGenerate = async () => {
|
|
83
|
-
const
|
|
83
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model!);
|
|
84
|
+
if (!auth.ok || !auth.apiKey) {
|
|
85
|
+
throw new Error(auth.ok ? `No API key for ${ctx.model!.provider}` : auth.error);
|
|
86
|
+
}
|
|
84
87
|
|
|
85
88
|
const userMessage: Message = {
|
|
86
89
|
role: "user",
|
|
@@ -96,7 +99,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
96
99
|
const response = await complete(
|
|
97
100
|
ctx.model!,
|
|
98
101
|
{ systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
|
|
99
|
-
{ apiKey, signal: loader.signal },
|
|
102
|
+
{ apiKey: auth.apiKey, headers: auth.headers, signal: loader.signal },
|
|
100
103
|
);
|
|
101
104
|
|
|
102
105
|
if (response.stopReason === "aborted") {
|
|
@@ -3,23 +3,24 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Type } from "@draht/ai";
|
|
6
|
-
import type
|
|
6
|
+
import { defineTool, type ExtensionAPI } from "@draht/coding-agent";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
const helloTool = defineTool({
|
|
9
|
+
name: "hello",
|
|
10
|
+
label: "Hello",
|
|
11
|
+
description: "A simple greeting tool",
|
|
12
|
+
parameters: Type.Object({
|
|
13
|
+
name: Type.String({ description: "Name to greet" }),
|
|
14
|
+
}),
|
|
15
|
+
|
|
16
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
17
|
+
return {
|
|
18
|
+
content: [{ type: "text", text: `Hello, ${params.name}!` }],
|
|
19
|
+
details: { greeted: params.name },
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
});
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
content: [{ type: "text", text: `Hello, ${name}!` }],
|
|
21
|
-
details: { greeted: name },
|
|
22
|
-
};
|
|
23
|
-
},
|
|
24
|
-
});
|
|
24
|
+
export default function (pi: ExtensionAPI) {
|
|
25
|
+
pi.registerTool(helloTool);
|
|
25
26
|
}
|
|
@@ -77,7 +77,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
77
77
|
|
|
78
78
|
// Do the work
|
|
79
79
|
const doExtract = async () => {
|
|
80
|
-
const
|
|
80
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model!);
|
|
81
|
+
if (!auth.ok || !auth.apiKey) {
|
|
82
|
+
throw new Error(auth.ok ? `No API key for ${ctx.model!.provider}` : auth.error);
|
|
83
|
+
}
|
|
81
84
|
const userMessage: UserMessage = {
|
|
82
85
|
role: "user",
|
|
83
86
|
content: [{ type: "text", text: lastAssistantText! }],
|
|
@@ -87,7 +90,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
87
90
|
const response = await complete(
|
|
88
91
|
ctx.model!,
|
|
89
92
|
{ systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
|
|
90
|
-
{ apiKey, signal: loader.signal },
|
|
93
|
+
{ apiKey: auth.apiKey, headers: auth.headers, signal: loader.signal },
|
|
91
94
|
);
|
|
92
95
|
|
|
93
96
|
if (response.stopReason === "aborted") {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - notify() - after each dialog completes
|
|
14
14
|
* - setStatus() - on turn_start/turn_end
|
|
15
15
|
* - setWidget() - on session_start
|
|
16
|
-
* - setTitle() - on session_start
|
|
16
|
+
* - setTitle() - on session_start
|
|
17
17
|
* - setEditorText() - via /rpc-prefill command
|
|
18
18
|
*/
|
|
19
19
|
|
|
@@ -24,18 +24,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
24
24
|
|
|
25
25
|
// -- setTitle, setWidget, setStatus on session lifecycle --
|
|
26
26
|
|
|
27
|
-
pi.on("session_start", async (
|
|
28
|
-
ctx.ui.setTitle("pi RPC Demo");
|
|
27
|
+
pi.on("session_start", async (event, ctx) => {
|
|
28
|
+
ctx.ui.setTitle(event.reason === "new" ? "pi RPC Demo (new session)" : "pi RPC Demo");
|
|
29
29
|
ctx.ui.setWidget("rpc-demo", ["--- RPC Extension UI Demo ---", "Loaded and ready."]);
|
|
30
30
|
ctx.ui.setStatus("rpc-demo", `Turns: ${turnCount}`);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
pi.on("session_switch", async (_event, ctx) => {
|
|
34
|
-
turnCount = 0;
|
|
35
|
-
ctx.ui.setTitle("pi RPC Demo (new session)");
|
|
36
|
-
ctx.ui.setStatus("rpc-demo", `Turns: ${turnCount}`);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
33
|
// -- setStatus on turn lifecycle --
|
|
40
34
|
|
|
41
35
|
pi.on("turn_start", async (_event, ctx) => {
|
|
@@ -29,12 +29,4 @@ export default function (pi: ExtensionAPI) {
|
|
|
29
29
|
const text = theme.fg("dim", ` Turn ${turnCount} complete`);
|
|
30
30
|
ctx.ui.setStatus("status-demo", check + text);
|
|
31
31
|
});
|
|
32
|
-
|
|
33
|
-
pi.on("session_switch", async (event, ctx) => {
|
|
34
|
-
if (event.reason === "new") {
|
|
35
|
-
turnCount = 0;
|
|
36
|
-
const theme = ctx.ui.theme;
|
|
37
|
-
ctx.ui.setStatus("status-demo", theme.fg("dim", "Ready"));
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
32
|
}
|
|
@@ -229,7 +229,7 @@ function getPiInvocation(args: string[]): { command: string; args: string[] } {
|
|
|
229
229
|
return { command: process.execPath, args };
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
return { command: "
|
|
232
|
+
return { command: "draht", args };
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
type OnUpdateCallback = (partial: AgentToolResult<SubagentDetails>) => void;
|