@witqq/agent-sdk 0.6.1 → 0.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.
- package/README.md +539 -6
- package/dist/{types-BvwNzZCj.d.cts → agent-CW9XbmG_.d.ts} +148 -95
- package/dist/{types-BvwNzZCj.d.ts → agent-DxY68NZL.d.cts} +148 -95
- package/dist/auth/index.cjs +260 -2
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +21 -138
- package/dist/auth/index.d.ts +21 -138
- package/dist/auth/index.js +260 -3
- package/dist/auth/index.js.map +1 -1
- package/dist/backends/claude.cjs +653 -140
- package/dist/backends/claude.cjs.map +1 -1
- package/dist/backends/claude.d.cts +4 -1
- package/dist/backends/claude.d.ts +4 -1
- package/dist/backends/claude.js +653 -140
- package/dist/backends/claude.js.map +1 -1
- package/dist/backends/copilot.cjs +428 -88
- package/dist/backends/copilot.cjs.map +1 -1
- package/dist/backends/copilot.d.cts +13 -4
- package/dist/backends/copilot.d.ts +13 -4
- package/dist/backends/copilot.js +428 -88
- package/dist/backends/copilot.js.map +1 -1
- package/dist/backends/vercel-ai.cjs +349 -77
- package/dist/backends/vercel-ai.cjs.map +1 -1
- package/dist/backends/vercel-ai.d.cts +3 -1
- package/dist/backends/vercel-ai.d.ts +3 -1
- package/dist/backends/vercel-ai.js +349 -77
- package/dist/backends/vercel-ai.js.map +1 -1
- package/dist/backends-BSrsBYFn.d.cts +39 -0
- package/dist/backends-BSrsBYFn.d.ts +39 -0
- package/dist/chat/accumulator.cjs +147 -0
- package/dist/chat/accumulator.cjs.map +1 -0
- package/dist/chat/accumulator.d.cts +64 -0
- package/dist/chat/accumulator.d.ts +64 -0
- package/dist/chat/accumulator.js +145 -0
- package/dist/chat/accumulator.js.map +1 -0
- package/dist/chat/backends.cjs +3524 -0
- package/dist/chat/backends.cjs.map +1 -0
- package/dist/chat/backends.d.cts +66 -0
- package/dist/chat/backends.d.ts +66 -0
- package/dist/chat/backends.js +3512 -0
- package/dist/chat/backends.js.map +1 -0
- package/dist/chat/context.cjs +280 -0
- package/dist/chat/context.cjs.map +1 -0
- package/dist/chat/context.d.cts +191 -0
- package/dist/chat/context.d.ts +191 -0
- package/dist/chat/context.js +277 -0
- package/dist/chat/context.js.map +1 -0
- package/dist/chat/core.cjs +305 -0
- package/dist/chat/core.cjs.map +1 -0
- package/dist/chat/core.d.cts +84 -0
- package/dist/chat/core.d.ts +84 -0
- package/dist/chat/core.js +282 -0
- package/dist/chat/core.js.map +1 -0
- package/dist/chat/errors.cjs +273 -0
- package/dist/chat/errors.cjs.map +1 -0
- package/dist/chat/errors.d.cts +97 -0
- package/dist/chat/errors.d.ts +97 -0
- package/dist/chat/errors.js +266 -0
- package/dist/chat/errors.js.map +1 -0
- package/dist/chat/events.cjs +203 -0
- package/dist/chat/events.cjs.map +1 -0
- package/dist/chat/events.d.cts +245 -0
- package/dist/chat/events.d.ts +245 -0
- package/dist/chat/events.js +196 -0
- package/dist/chat/events.js.map +1 -0
- package/dist/chat/index.cjs +5550 -0
- package/dist/chat/index.cjs.map +1 -0
- package/dist/chat/index.d.cts +77 -0
- package/dist/chat/index.d.ts +77 -0
- package/dist/chat/index.js +5505 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/react/theme.css +2517 -0
- package/dist/chat/react.cjs +3589 -0
- package/dist/chat/react.cjs.map +1 -0
- package/dist/chat/react.d.cts +1088 -0
- package/dist/chat/react.d.ts +1088 -0
- package/dist/chat/react.js +3547 -0
- package/dist/chat/react.js.map +1 -0
- package/dist/chat/runtime.cjs +1245 -0
- package/dist/chat/runtime.cjs.map +1 -0
- package/dist/chat/runtime.d.cts +182 -0
- package/dist/chat/runtime.d.ts +182 -0
- package/dist/chat/runtime.js +1243 -0
- package/dist/chat/runtime.js.map +1 -0
- package/dist/chat/server.cjs +2668 -0
- package/dist/chat/server.cjs.map +1 -0
- package/dist/chat/server.d.cts +648 -0
- package/dist/chat/server.d.ts +648 -0
- package/dist/chat/server.js +2628 -0
- package/dist/chat/server.js.map +1 -0
- package/dist/chat/sessions.cjs +380 -0
- package/dist/chat/sessions.cjs.map +1 -0
- package/dist/chat/sessions.d.cts +158 -0
- package/dist/chat/sessions.d.ts +158 -0
- package/dist/chat/sessions.js +376 -0
- package/dist/chat/sessions.js.map +1 -0
- package/dist/chat/sqlite.cjs +441 -0
- package/dist/chat/sqlite.cjs.map +1 -0
- package/dist/chat/sqlite.d.cts +128 -0
- package/dist/chat/sqlite.d.ts +128 -0
- package/dist/chat/sqlite.js +435 -0
- package/dist/chat/sqlite.js.map +1 -0
- package/dist/chat/state.cjs +190 -0
- package/dist/chat/state.cjs.map +1 -0
- package/dist/chat/state.d.cts +95 -0
- package/dist/chat/state.d.ts +95 -0
- package/dist/chat/state.js +180 -0
- package/dist/chat/state.js.map +1 -0
- package/dist/chat/storage.cjs +249 -0
- package/dist/chat/storage.cjs.map +1 -0
- package/dist/chat/storage.d.cts +197 -0
- package/dist/chat/storage.d.ts +197 -0
- package/dist/chat/storage.js +245 -0
- package/dist/chat/storage.js.map +1 -0
- package/dist/errors-C-so0M4t.d.cts +33 -0
- package/dist/errors-C-so0M4t.d.ts +33 -0
- package/dist/errors-CmVvczxZ.d.cts +28 -0
- package/dist/errors-CmVvczxZ.d.ts +28 -0
- package/dist/in-process-transport-C1JnJGVR.d.ts +228 -0
- package/dist/in-process-transport-C7DSqPyX.d.cts +228 -0
- package/dist/index.cjs +365 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +322 -125
- package/dist/index.d.ts +322 -125
- package/dist/index.js +359 -60
- package/dist/index.js.map +1 -1
- package/dist/provider-types-PTSlRPNB.d.cts +39 -0
- package/dist/provider-types-PTSlRPNB.d.ts +39 -0
- package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
- package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
- package/dist/testing.cjs +383 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +132 -0
- package/dist/testing.d.ts +132 -0
- package/dist/testing.js +377 -0
- package/dist/testing.js.map +1 -0
- package/dist/token-store-CSUBgYwn.d.ts +48 -0
- package/dist/token-store-CuC4hB9Z.d.cts +48 -0
- package/dist/transport-Cdh3M0tS.d.cts +68 -0
- package/dist/transport-Ciap4PWK.d.ts +68 -0
- package/dist/types-4vbcmPTp.d.cts +143 -0
- package/dist/types-BxggH0Yh.d.ts +143 -0
- package/dist/types-DRgd_9R7.d.cts +363 -0
- package/dist/types-ajANVzf7.d.ts +363 -0
- package/package.json +178 -6
package/README.md
CHANGED
|
@@ -53,6 +53,24 @@ agent.dispose();
|
|
|
53
53
|
await service.dispose();
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
### Retry on Transient Errors
|
|
57
|
+
|
|
58
|
+
`BaseAgent` supports automatic retry for transient failures:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const agent = service.createAgent({ systemPrompt: "..." });
|
|
62
|
+
const result = await agent.run("prompt", {
|
|
63
|
+
model: "gpt-5-mini",
|
|
64
|
+
retry: {
|
|
65
|
+
maxRetries: 3,
|
|
66
|
+
initialDelayMs: 1000,
|
|
67
|
+
backoffMultiplier: 2,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Retries on transient error codes: `TIMEOUT`, `RATE_LIMIT`, `NETWORK`, `MODEL_OVERLOADED`. Never retries `AbortError`, `ReentrancyError`, or `DisposedError`.
|
|
73
|
+
|
|
56
74
|
## Tool Definition
|
|
57
75
|
|
|
58
76
|
Tools are defined with a Zod schema for parameters and an `execute` function:
|
|
@@ -81,6 +99,23 @@ const writeFileTool: ToolDefinition = {
|
|
|
81
99
|
|
|
82
100
|
When `needsApproval: true`, the `supervisor.onPermission` callback is invoked before execution. Without a supervisor, approval-required tools are denied by default.
|
|
83
101
|
|
|
102
|
+
Runtime-registered tools receive an optional `ToolContext` as their second parameter:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import type { ToolContext } from "@witqq/agent-sdk";
|
|
106
|
+
|
|
107
|
+
const dbTool: ToolDefinition = {
|
|
108
|
+
name: "query_db",
|
|
109
|
+
description: "Query the database",
|
|
110
|
+
parameters: z.object({ sql: z.string() }),
|
|
111
|
+
execute: async (params, context?: ToolContext) => {
|
|
112
|
+
// context.sessionId — current chat session
|
|
113
|
+
// context.custom — session metadata
|
|
114
|
+
return db.query(params.sql);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
84
119
|
## Permission Handling
|
|
85
120
|
|
|
86
121
|
The `supervisor` hooks intercept permission requests and user-facing questions:
|
|
@@ -144,6 +179,8 @@ interface IPermissionStore {
|
|
|
144
179
|
}
|
|
145
180
|
```
|
|
146
181
|
|
|
182
|
+
Built-in stores: `InMemoryPermissionStore` (session-scoped), `FilePermissionStore` (persists to JSON file), `CompositePermissionStore` (chains multiple stores — first match wins, writes to the store matching the scope). `createDefaultPermissionStore(projectDir)` returns a `CompositePermissionStore` combining project-level and global `FilePermissionStore` instances.
|
|
183
|
+
|
|
147
184
|
## Structured Output
|
|
148
185
|
|
|
149
186
|
Extract typed data from LLM responses using `runStructured`:
|
|
@@ -502,19 +539,515 @@ interface CopilotAuthToken extends AuthToken {
|
|
|
502
539
|
}
|
|
503
540
|
```
|
|
504
541
|
|
|
542
|
+
### Token Auto-Refresh
|
|
543
|
+
|
|
544
|
+
`TokenRefreshManager` schedules background token refresh before expiry:
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
import { TokenRefreshManager } from "@witqq/agent-sdk/auth";
|
|
548
|
+
|
|
549
|
+
const manager = new TokenRefreshManager({
|
|
550
|
+
token: authToken,
|
|
551
|
+
refreshFn: async (token) => claudeAuth.refreshToken(token.refreshToken!),
|
|
552
|
+
refreshThreshold: 0.8, // refresh at 80% of token lifetime
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
manager.on("refreshed", (newToken) => { /* update stored token */ });
|
|
556
|
+
manager.on("expired", () => { /* re-authenticate */ });
|
|
557
|
+
manager.start();
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
## Chat SDK (experimental)
|
|
561
|
+
|
|
562
|
+
Higher-level primitives for building AI chat applications on top of agent-sdk.
|
|
563
|
+
|
|
564
|
+
### Composable Architecture
|
|
565
|
+
|
|
566
|
+
The SDK is layered — use only what you need:
|
|
567
|
+
|
|
568
|
+
**Standalone agent** (no server, no UI):
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
import { createAgentService } from "@witqq/agent-sdk";
|
|
572
|
+
const service = await createAgentService("copilot", { useLoggedInUser: true });
|
|
573
|
+
const agent = service.createAgent({ systemPrompt: "You are helpful." });
|
|
574
|
+
const result = await agent.run("Hello");
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Server with runtime** (add HTTP layer):
|
|
578
|
+
|
|
579
|
+
```typescript
|
|
580
|
+
import * as http from "node:http";
|
|
581
|
+
import { createAgentService } from "@witqq/agent-sdk";
|
|
582
|
+
import type { AuthToken } from "@witqq/agent-sdk/auth";
|
|
583
|
+
import { CopilotAuth } from "@witqq/agent-sdk/auth";
|
|
584
|
+
import { CopilotChatAdapter } from "@witqq/agent-sdk/chat/backends";
|
|
585
|
+
import { createChatRuntime } from "@witqq/agent-sdk/chat/runtime";
|
|
586
|
+
import { createChatServer } from "@witqq/agent-sdk/chat/server";
|
|
587
|
+
import { createSQLiteStorage } from "@witqq/agent-sdk/chat/sqlite";
|
|
588
|
+
|
|
589
|
+
const { sessionStore, providerStore, tokenStore } = createSQLiteStorage("chat.db");
|
|
590
|
+
|
|
591
|
+
const runtime = createChatRuntime({
|
|
592
|
+
backends: {
|
|
593
|
+
copilot: async (credentials: AuthToken) => {
|
|
594
|
+
const svc = await createAgentService("copilot", { githubToken: credentials.accessToken });
|
|
595
|
+
return new CopilotChatAdapter({ agentConfig: { systemPrompt: "Hello" }, agentService: svc });
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
defaultBackend: "copilot", sessionStore,
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
const handler = createChatServer({
|
|
602
|
+
runtime,
|
|
603
|
+
auth: { tokenStore, createCopilotAuth: () => new CopilotAuth() },
|
|
604
|
+
providers: { providerStore },
|
|
605
|
+
});
|
|
606
|
+
http.createServer(handler).listen(3000);
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
**Full-stack with React** (add frontend):
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
// frontend — 4 lines
|
|
613
|
+
import { ChatUI, RemoteChatClient } from "@witqq/agent-sdk/chat/react";
|
|
614
|
+
|
|
615
|
+
const runtime = new RemoteChatClient({ baseUrl: "/api/chat" });
|
|
616
|
+
<ChatUI runtime={runtime} authBaseUrl="/api" />
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Barrel Import
|
|
620
|
+
|
|
621
|
+
For most consumer apps, import common types from the barrel:
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
// Core types and runtime (barrel export)
|
|
625
|
+
import {
|
|
626
|
+
ChatMessage, ChatSession, ChatEvent, IChatRuntime,
|
|
627
|
+
createChatRuntime, ChatError, classifyError,
|
|
628
|
+
MessageAccumulator, SSEChatTransport,
|
|
629
|
+
} from "@witqq/agent-sdk/chat";
|
|
630
|
+
|
|
631
|
+
// React hooks and components (separate import — not in barrel)
|
|
632
|
+
import {
|
|
633
|
+
useChat, useRemoteChat, useRemoteAuth,
|
|
634
|
+
ChatProvider, Thread, Composer,
|
|
635
|
+
} from "@witqq/agent-sdk/chat/react";
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### Individual Module Imports
|
|
639
|
+
|
|
640
|
+
```typescript
|
|
641
|
+
import { ChatMessage, ChatSession, isChatMessage } from "@witqq/agent-sdk/chat/core";
|
|
642
|
+
import type { IChatBackend } from "@witqq/agent-sdk/chat/backends";
|
|
643
|
+
import {
|
|
644
|
+
classifyError, withRetry, isRetryable,
|
|
645
|
+
ChatError, ErrorCode,
|
|
646
|
+
ExponentialBackoffStrategy
|
|
647
|
+
} from "@witqq/agent-sdk/chat/errors";
|
|
648
|
+
import { ChatEventBus } from "@witqq/agent-sdk/chat/events";
|
|
649
|
+
import { filterEvents, collectText } from "@witqq/agent-sdk/chat/events";
|
|
650
|
+
import {
|
|
651
|
+
InMemoryStorage, FileStorage,
|
|
652
|
+
type IStorageAdapter, StorageError
|
|
653
|
+
} from "@witqq/agent-sdk/chat/storage";
|
|
654
|
+
import {
|
|
655
|
+
InMemorySessionStore, FileSessionStore,
|
|
656
|
+
type IChatSessionStore
|
|
657
|
+
} from "@witqq/agent-sdk/chat/sessions";
|
|
658
|
+
import {
|
|
659
|
+
ContextWindowManager, estimateTokens
|
|
660
|
+
} from "@witqq/agent-sdk/chat/context";
|
|
661
|
+
import {
|
|
662
|
+
CopilotChatAdapter, VercelAIChatAdapter, BaseBackendAdapter,
|
|
663
|
+
SSEChatTransport, WsChatTransport, InProcessChatTransport,
|
|
664
|
+
streamToTransport, withInterceptors,
|
|
665
|
+
type IResumableBackend, type BackendAdapterOptions, type IChatTransport
|
|
666
|
+
} from "@witqq/agent-sdk/chat/backends";
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
### Error Classification
|
|
670
|
+
|
|
671
|
+
```typescript
|
|
672
|
+
try {
|
|
673
|
+
await provider.send(message);
|
|
674
|
+
} catch (err) {
|
|
675
|
+
const classified = classifyError(err);
|
|
676
|
+
if (classified.code === ErrorCode.RATE_LIMIT) {
|
|
677
|
+
console.log(`Rate limited, retry after ${classified.retryAfter}ms`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### Retry with Backoff
|
|
683
|
+
|
|
684
|
+
```typescript
|
|
685
|
+
const result = await withRetry(
|
|
686
|
+
() => provider.send(message),
|
|
687
|
+
new ExponentialBackoffStrategy({ maxAttempts: 3 }),
|
|
688
|
+
{ signal: AbortSignal.timeout(30_000) },
|
|
689
|
+
);
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### Event Bus with Middleware
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
const bus = new ChatEventBus();
|
|
696
|
+
|
|
697
|
+
// Logging middleware
|
|
698
|
+
bus.use((ctx) => {
|
|
699
|
+
console.log(`[${ctx.event.type}]`);
|
|
700
|
+
ctx.next();
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// Filter out heartbeat events
|
|
704
|
+
bus.use((ctx) => {
|
|
705
|
+
if (ctx.event.type === "heartbeat") ctx.suppress();
|
|
706
|
+
else ctx.next();
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
bus.on("message:delta", (event) => console.log(event.text));
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### Storage Adapters
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
// In-memory (dev/testing)
|
|
716
|
+
const mem = new InMemoryStorage<ChatSession>();
|
|
717
|
+
await mem.create("s1", session);
|
|
718
|
+
const s = await mem.get("s1"); // deep copy, mutation-safe
|
|
719
|
+
|
|
720
|
+
// File-based (persistence)
|
|
721
|
+
const fs = new FileStorage<ChatSession>({ directory: "./data/sessions" });
|
|
722
|
+
await fs.create("s1", session);
|
|
723
|
+
const items = await fs.query({
|
|
724
|
+
filter: (s) => s.metadata.tags.includes("important"),
|
|
725
|
+
sort: (a, b) => b.updatedAt - a.updatedAt,
|
|
726
|
+
limit: 10,
|
|
727
|
+
});
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### Session Store
|
|
731
|
+
|
|
732
|
+
```typescript
|
|
733
|
+
const store = new InMemorySessionStore();
|
|
734
|
+
// or: new FileSessionStore({ directory: "./data/sessions" })
|
|
735
|
+
|
|
736
|
+
const session = await store.createSession({
|
|
737
|
+
config: { model: "gpt-4", backend: "vercel-ai" },
|
|
738
|
+
title: "Code Review",
|
|
739
|
+
tags: ["work"],
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
await store.appendMessage(session.id, message);
|
|
743
|
+
const page = await store.loadMessages(session.id, { limit: 20, offset: 0 });
|
|
744
|
+
// page.messages, page.total, page.hasMore
|
|
745
|
+
|
|
746
|
+
const results = await store.searchSessions({ query: "typescript" });
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
### Context Window Manager
|
|
750
|
+
|
|
751
|
+
```typescript
|
|
752
|
+
const manager = new ContextWindowManager({
|
|
753
|
+
maxTokens: 4096,
|
|
754
|
+
reservedTokens: 500,
|
|
755
|
+
strategy: "truncate-oldest", // or "sliding-window", "summarize-placeholder"
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
const result = manager.fitMessages(messages);
|
|
759
|
+
// result.messages — trimmed to fit budget
|
|
760
|
+
// result.wasTruncated — whether messages were removed
|
|
761
|
+
// result.totalTokens — estimated token usage
|
|
762
|
+
// result.removedCount — how many messages were dropped
|
|
763
|
+
|
|
764
|
+
// Async variant with optional summarizer (summarize-placeholder strategy)
|
|
765
|
+
const asyncManager = new ContextWindowManager({
|
|
766
|
+
maxTokens: 4096,
|
|
767
|
+
strategy: "summarize-placeholder",
|
|
768
|
+
summarizer: async (removed) => {
|
|
769
|
+
// Call LLM or custom logic to summarize removed messages
|
|
770
|
+
return `Summary of ${removed.length} messages: ...`;
|
|
771
|
+
},
|
|
772
|
+
});
|
|
773
|
+
const asyncResult = await asyncManager.fitMessagesAsync(messages);
|
|
774
|
+
|
|
775
|
+
// Per-message estimation
|
|
776
|
+
const tokens = estimateTokens(message); // ~chars/4
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Backend Adapters
|
|
780
|
+
|
|
781
|
+
Backend adapters bridge `IAgentService` to `IChatBackend`, adding session management and resume support:
|
|
782
|
+
|
|
783
|
+
```typescript
|
|
784
|
+
import { CopilotChatAdapter } from "@witqq/agent-sdk/chat/backends";
|
|
785
|
+
|
|
786
|
+
const adapter = new CopilotChatAdapter({
|
|
787
|
+
agentConfig: {
|
|
788
|
+
systemPrompt: "You are a helpful assistant.",
|
|
789
|
+
model: "gpt-4.1",
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// Stream a message (creates persistent session automatically)
|
|
794
|
+
for await (const event of adapter.streamMessage(session, "Hello")) {
|
|
795
|
+
// ChatEvent: text_delta, message_start, message_complete, tool_call_start, etc.
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Resume a previous session
|
|
799
|
+
if (adapter.canResume()) {
|
|
800
|
+
for await (const event of adapter.resume(session, adapter.backendSessionId!)) {
|
|
801
|
+
// Continues the existing conversation
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
adapter.dispose();
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
`IResumableBackend` extends `IChatBackend` with `canResume()`, `resume()`, and `backendSessionId`. Built-in adapters: `CopilotChatAdapter`, `ClaudeChatAdapter`, `VercelAIChatAdapter` (stateless, no resume). Create custom adapters by extending `BaseBackendAdapter`.
|
|
809
|
+
|
|
810
|
+
Service ownership: when `agentService` is passed via options, the adapter does **not** dispose it — the caller retains ownership. When omitted, the adapter creates and owns its service internally.
|
|
811
|
+
|
|
812
|
+
### Chat Transport
|
|
813
|
+
|
|
814
|
+
`IChatTransport` abstracts event delivery to clients. Three built-in implementations:
|
|
815
|
+
|
|
816
|
+
| Transport | Use case |
|
|
817
|
+
|---|---|
|
|
818
|
+
| `SSEChatTransport` | Server-Sent Events over HTTP |
|
|
819
|
+
| `WsChatTransport` | WebSocket via `WebSocketLike` abstraction |
|
|
820
|
+
| `InProcessChatTransport` | Zero-network async iterable for testing/embedded |
|
|
821
|
+
|
|
822
|
+
`streamToTransport()` pipes adapter events to any transport:
|
|
823
|
+
|
|
824
|
+
```typescript
|
|
825
|
+
import { SSEChatTransport, WsChatTransport, streamToTransport } from "@witqq/agent-sdk/chat/backends";
|
|
826
|
+
|
|
827
|
+
const transport = new SSEChatTransport(res);
|
|
828
|
+
await streamToTransport(adapter.streamMessage(session, message), transport);
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
**Interceptors** wrap any transport with composable hooks (logging, metrics, rate limiting):
|
|
832
|
+
|
|
833
|
+
```typescript
|
|
834
|
+
import { withInterceptors, type TransportInterceptor } from "@witqq/agent-sdk/chat/backends";
|
|
835
|
+
|
|
836
|
+
const logger: TransportInterceptor = {
|
|
837
|
+
beforeSend(event) { console.log("send:", event.type); return event; },
|
|
838
|
+
onError(err) { console.error(err); },
|
|
839
|
+
};
|
|
840
|
+
const wrapped = withInterceptors(transport, [logger]);
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
**Stream watchdog** — set `streamTimeoutMs` in runtime options to abort hanging streams:
|
|
844
|
+
|
|
845
|
+
```typescript
|
|
846
|
+
const runtime = createChatRuntime({
|
|
847
|
+
streamTimeoutMs: 30_000, // abort after 30s of inactivity
|
|
848
|
+
// ...
|
|
849
|
+
});
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
See [Custom Transports](docs/chat-sdk/custom-transports.md) for the implementation guide.
|
|
853
|
+
|
|
854
|
+
### Chat Runtime
|
|
855
|
+
|
|
856
|
+
`IChatRuntime<TMetadata>` is the unified facade that orchestrates backend adapters, sessions, context trimming, streaming, and middleware. `createChatRuntime()` builds one from a config:
|
|
857
|
+
|
|
858
|
+
```typescript
|
|
859
|
+
import { createChatRuntime } from "@witqq/agent-sdk/chat/runtime";
|
|
860
|
+
|
|
861
|
+
const runtime = createChatRuntime({
|
|
862
|
+
backends: {
|
|
863
|
+
copilot: async (credentials) => new CopilotChatAdapter({
|
|
864
|
+
agentConfig: { systemPrompt: "Hello" },
|
|
865
|
+
agentService: await createAgentService("copilot", { githubToken: credentials.accessToken }),
|
|
866
|
+
}),
|
|
867
|
+
},
|
|
868
|
+
defaultBackend: "copilot",
|
|
869
|
+
sessionStore: new InMemorySessionStore(),
|
|
870
|
+
context: { maxTokens: 8000 },
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
// Create session, send message, stream events
|
|
874
|
+
const session = await runtime.createSession();
|
|
875
|
+
for await (const event of runtime.send(session.id, "Hello")) {
|
|
876
|
+
console.log(event.type, event);
|
|
877
|
+
}
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
Key capabilities: session delegation (create/get/list/delete), tool registration via `registerTool(def)` / `removeTool(name)` / `registeredTools` (readonly Map, persists across switches), middleware pipeline (`use(middleware)`), state machine (`status` property), abort support (`abort()`), pre-stream retry with `StreamRetryConfig`, session lifecycle events via `onSessionChange(callback)`, generic `<TMetadata>` for typed session metadata, context stats via `getContextStats(sessionId)`, and `dispose()`. Model and backend are passed per-call via `send(sessionId, msg, { model, backend, credentials })`.
|
|
881
|
+
|
|
882
|
+
Context monitoring:
|
|
883
|
+
|
|
884
|
+
```typescript
|
|
885
|
+
// Query context usage after send
|
|
886
|
+
const stats = runtime.getContextStats(session.id);
|
|
887
|
+
// stats: { totalTokens, removedCount, wasTruncated, availableBudget, realPromptTokens?, realCompletionTokens?, modelContextWindow? } | null
|
|
888
|
+
|
|
889
|
+
// Handle trimmed messages via callback
|
|
890
|
+
const runtime = createChatRuntime({
|
|
891
|
+
// ...backends, sessionStore, context
|
|
892
|
+
onContextTrimmed: (sessionId, removedMessages) => {
|
|
893
|
+
db.saveRemovedMessages(sessionId, removedMessages);
|
|
894
|
+
},
|
|
895
|
+
});
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
### Server Utilities
|
|
899
|
+
|
|
900
|
+
Framework-agnostic HTTP handlers for serving `IChatRuntime` over HTTP. Import from `@witqq/agent-sdk/chat/server`.
|
|
901
|
+
|
|
902
|
+
```typescript
|
|
903
|
+
import {
|
|
904
|
+
createChatHandler,
|
|
905
|
+
createAuthHandler,
|
|
906
|
+
FileTokenStore,
|
|
907
|
+
corsMiddleware,
|
|
908
|
+
createChatServer,
|
|
909
|
+
} from "@witqq/agent-sdk/chat/server";
|
|
910
|
+
import { createChatRuntime } from "@witqq/agent-sdk/chat/runtime";
|
|
911
|
+
|
|
912
|
+
const runtime = createChatRuntime({ /* ... */ });
|
|
913
|
+
|
|
914
|
+
// Option 1: Compose handlers manually
|
|
915
|
+
const chatHandler = createChatHandler(runtime, { prefix: "/api/chat" });
|
|
916
|
+
const authHandler = createAuthHandler({
|
|
917
|
+
tokenStore: new FileTokenStore({ directory: "./tokens" }),
|
|
918
|
+
onAuth: (backend, token) => { /* handle auth */ },
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
// Option 2: One-call server factory
|
|
922
|
+
const handler = createChatServer({
|
|
923
|
+
runtime,
|
|
924
|
+
cors: true,
|
|
925
|
+
staticDir: "./public",
|
|
926
|
+
});
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
`createChatHandler` maps all 10 `RemoteChatClient` endpoints (session CRUD, send via SSE, abort, models, backend/model switch). `createAuthHandler` handles Copilot Device Flow, Claude OAuth+PKCE, and API key auth with persistent token storage via `ITokenStore`. `corsMiddleware` supports multi-origin configuration.
|
|
930
|
+
|
|
505
931
|
## Interactive Demo
|
|
506
932
|
|
|
507
|
-
|
|
933
|
+
Complete chat app showcasing the full SDK.
|
|
508
934
|
|
|
509
935
|
```bash
|
|
510
|
-
#
|
|
511
|
-
|
|
936
|
+
npm run demo # Build & start in Docker (http://localhost:3456)
|
|
937
|
+
npm run demo -- stop # Stop
|
|
938
|
+
npm run demo -- logs # Follow logs
|
|
939
|
+
npm run demo -- restart # Rebuild & restart
|
|
940
|
+
npm run demo -- dev # Local dev without Docker
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
Features: multi-backend auth (Copilot Device Flow, Claude OAuth+PKCE, Vercel AI API key), provider management, model selection, SSE streaming with thinking blocks, tool calls with approval, token usage display, error handling, session management, SQLite persistence.
|
|
944
|
+
|
|
945
|
+
Server uses `createChatServer` for zero custom routing with stateless backend factories (credentials per-request). Frontend uses `ChatUI` for zero custom components. See [demo README](examples/demo/README.md) for details.
|
|
946
|
+
|
|
947
|
+
## React Bindings
|
|
948
|
+
|
|
949
|
+
Headless React hooks and components for building chat UIs:
|
|
950
|
+
|
|
951
|
+
```typescript
|
|
952
|
+
import { useChat, Thread, Composer, ChatProvider } from "@witqq/agent-sdk/chat/react";
|
|
953
|
+
|
|
954
|
+
function App() {
|
|
955
|
+
return (
|
|
956
|
+
<ChatProvider runtime={runtime}>
|
|
957
|
+
<Thread />
|
|
958
|
+
<Composer />
|
|
959
|
+
</ChatProvider>
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
For client-server architectures, `useRemoteChat` manages the full auth → runtime → session lifecycle:
|
|
965
|
+
|
|
966
|
+
```typescript
|
|
967
|
+
import { useRemoteChat, ChatProvider, Thread, Composer } from "@witqq/agent-sdk/chat/react";
|
|
968
|
+
|
|
969
|
+
function App() {
|
|
970
|
+
const chat = useRemoteChat({
|
|
971
|
+
chatBaseUrl: "/api/chat",
|
|
972
|
+
authBaseUrl: "/api",
|
|
973
|
+
backend: "copilot",
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
if (chat.phase !== "ready" || !chat.runtime) return <div>Loading...</div>;
|
|
977
|
+
|
|
978
|
+
return (
|
|
979
|
+
<ChatProvider runtime={chat.runtime}>
|
|
980
|
+
<Thread />
|
|
981
|
+
<Composer />
|
|
982
|
+
</ChatProvider>
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
```
|
|
512
986
|
|
|
513
|
-
|
|
514
|
-
|
|
987
|
+
Or use `RemoteChatClient` directly for lower-level control:
|
|
988
|
+
|
|
989
|
+
```typescript
|
|
990
|
+
import { RemoteChatClient } from "@witqq/agent-sdk/chat/react";
|
|
991
|
+
|
|
992
|
+
const runtime = new RemoteChatClient({ baseUrl: "/api/chat" });
|
|
515
993
|
```
|
|
516
994
|
|
|
517
|
-
|
|
995
|
+
Reactive session list (replaces manual polling):
|
|
996
|
+
|
|
997
|
+
```typescript
|
|
998
|
+
import { useSessions } from "@witqq/agent-sdk/chat/react";
|
|
999
|
+
|
|
1000
|
+
function SessionList() {
|
|
1001
|
+
const { sessions, loading } = useSessions();
|
|
1002
|
+
// Auto-updates on create, delete, and message send
|
|
1003
|
+
return sessions.map(s => <div key={s.id}>{s.title}</div>);
|
|
1004
|
+
}
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
Server-delegated authentication (no `node:crypto` in browser):
|
|
1008
|
+
|
|
1009
|
+
```typescript
|
|
1010
|
+
import { useRemoteAuth } from "@witqq/agent-sdk/chat/react";
|
|
1011
|
+
|
|
1012
|
+
const auth = useRemoteAuth({ backend: "copilot", baseUrl: "/api" });
|
|
1013
|
+
// auth.startDeviceFlow(), auth.startOAuthFlow(), auth.submitApiKey()
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
`ContextStatsDisplay` renders context window usage:
|
|
1017
|
+
|
|
1018
|
+
```typescript
|
|
1019
|
+
import { ContextStatsDisplay } from "@witqq/agent-sdk/chat/react";
|
|
1020
|
+
|
|
1021
|
+
// Headless component rendering context window stats
|
|
1022
|
+
// Props: { stats: ContextStats | null }
|
|
1023
|
+
// Data attributes: data-context-stats, data-context-tokens, data-context-budget,
|
|
1024
|
+
// data-context-usage, data-context-removed, data-context-truncated
|
|
1025
|
+
<ContextStatsDisplay stats={runtime.getContextStats(sessionId)} />
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
`ThreadList` supports search:
|
|
1029
|
+
|
|
1030
|
+
```typescript
|
|
1031
|
+
<ThreadList
|
|
1032
|
+
sessions={sessions}
|
|
1033
|
+
onSelect={handleSelect}
|
|
1034
|
+
onDelete={handleDelete}
|
|
1035
|
+
searchQuery={query} // controlled search input
|
|
1036
|
+
onSearchChange={setQuery} // search input change handler
|
|
1037
|
+
/>
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
See [Chat SDK docs](docs/chat-sdk/README.md) for the full React API reference.
|
|
1041
|
+
|
|
1042
|
+
## Documentation
|
|
1043
|
+
|
|
1044
|
+
| Document | Description |
|
|
1045
|
+
|----------|-------------|
|
|
1046
|
+
| [Chat SDK Modules](docs/chat-sdk/README.md) | Module-by-module API docs for chat primitives |
|
|
1047
|
+
| [Custom Transports](docs/chat-sdk/custom-transports.md) | Guide to building custom IChatTransport implementations |
|
|
1048
|
+
| [Custom Renderers](docs/chat-sdk/custom-renderers.md) | Three approaches to customizing React UI components |
|
|
1049
|
+
| [Demo App](examples/demo/README.md) | Full-stack demo with architecture and API reference |
|
|
1050
|
+
| [Changelog](CHANGELOG.md) | Release history and breaking changes |
|
|
518
1051
|
|
|
519
1052
|
## License
|
|
520
1053
|
|