@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.
Files changed (145) hide show
  1. package/README.md +539 -6
  2. package/dist/{types-BvwNzZCj.d.cts → agent-CW9XbmG_.d.ts} +148 -95
  3. package/dist/{types-BvwNzZCj.d.ts → agent-DxY68NZL.d.cts} +148 -95
  4. package/dist/auth/index.cjs +260 -2
  5. package/dist/auth/index.cjs.map +1 -1
  6. package/dist/auth/index.d.cts +21 -138
  7. package/dist/auth/index.d.ts +21 -138
  8. package/dist/auth/index.js +260 -3
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/backends/claude.cjs +653 -140
  11. package/dist/backends/claude.cjs.map +1 -1
  12. package/dist/backends/claude.d.cts +4 -1
  13. package/dist/backends/claude.d.ts +4 -1
  14. package/dist/backends/claude.js +653 -140
  15. package/dist/backends/claude.js.map +1 -1
  16. package/dist/backends/copilot.cjs +428 -88
  17. package/dist/backends/copilot.cjs.map +1 -1
  18. package/dist/backends/copilot.d.cts +13 -4
  19. package/dist/backends/copilot.d.ts +13 -4
  20. package/dist/backends/copilot.js +428 -88
  21. package/dist/backends/copilot.js.map +1 -1
  22. package/dist/backends/vercel-ai.cjs +349 -77
  23. package/dist/backends/vercel-ai.cjs.map +1 -1
  24. package/dist/backends/vercel-ai.d.cts +3 -1
  25. package/dist/backends/vercel-ai.d.ts +3 -1
  26. package/dist/backends/vercel-ai.js +349 -77
  27. package/dist/backends/vercel-ai.js.map +1 -1
  28. package/dist/backends-BSrsBYFn.d.cts +39 -0
  29. package/dist/backends-BSrsBYFn.d.ts +39 -0
  30. package/dist/chat/accumulator.cjs +147 -0
  31. package/dist/chat/accumulator.cjs.map +1 -0
  32. package/dist/chat/accumulator.d.cts +64 -0
  33. package/dist/chat/accumulator.d.ts +64 -0
  34. package/dist/chat/accumulator.js +145 -0
  35. package/dist/chat/accumulator.js.map +1 -0
  36. package/dist/chat/backends.cjs +3524 -0
  37. package/dist/chat/backends.cjs.map +1 -0
  38. package/dist/chat/backends.d.cts +66 -0
  39. package/dist/chat/backends.d.ts +66 -0
  40. package/dist/chat/backends.js +3512 -0
  41. package/dist/chat/backends.js.map +1 -0
  42. package/dist/chat/context.cjs +280 -0
  43. package/dist/chat/context.cjs.map +1 -0
  44. package/dist/chat/context.d.cts +191 -0
  45. package/dist/chat/context.d.ts +191 -0
  46. package/dist/chat/context.js +277 -0
  47. package/dist/chat/context.js.map +1 -0
  48. package/dist/chat/core.cjs +305 -0
  49. package/dist/chat/core.cjs.map +1 -0
  50. package/dist/chat/core.d.cts +84 -0
  51. package/dist/chat/core.d.ts +84 -0
  52. package/dist/chat/core.js +282 -0
  53. package/dist/chat/core.js.map +1 -0
  54. package/dist/chat/errors.cjs +273 -0
  55. package/dist/chat/errors.cjs.map +1 -0
  56. package/dist/chat/errors.d.cts +97 -0
  57. package/dist/chat/errors.d.ts +97 -0
  58. package/dist/chat/errors.js +266 -0
  59. package/dist/chat/errors.js.map +1 -0
  60. package/dist/chat/events.cjs +203 -0
  61. package/dist/chat/events.cjs.map +1 -0
  62. package/dist/chat/events.d.cts +245 -0
  63. package/dist/chat/events.d.ts +245 -0
  64. package/dist/chat/events.js +196 -0
  65. package/dist/chat/events.js.map +1 -0
  66. package/dist/chat/index.cjs +5550 -0
  67. package/dist/chat/index.cjs.map +1 -0
  68. package/dist/chat/index.d.cts +77 -0
  69. package/dist/chat/index.d.ts +77 -0
  70. package/dist/chat/index.js +5505 -0
  71. package/dist/chat/index.js.map +1 -0
  72. package/dist/chat/react/theme.css +2517 -0
  73. package/dist/chat/react.cjs +3589 -0
  74. package/dist/chat/react.cjs.map +1 -0
  75. package/dist/chat/react.d.cts +1088 -0
  76. package/dist/chat/react.d.ts +1088 -0
  77. package/dist/chat/react.js +3547 -0
  78. package/dist/chat/react.js.map +1 -0
  79. package/dist/chat/runtime.cjs +1245 -0
  80. package/dist/chat/runtime.cjs.map +1 -0
  81. package/dist/chat/runtime.d.cts +182 -0
  82. package/dist/chat/runtime.d.ts +182 -0
  83. package/dist/chat/runtime.js +1243 -0
  84. package/dist/chat/runtime.js.map +1 -0
  85. package/dist/chat/server.cjs +2668 -0
  86. package/dist/chat/server.cjs.map +1 -0
  87. package/dist/chat/server.d.cts +648 -0
  88. package/dist/chat/server.d.ts +648 -0
  89. package/dist/chat/server.js +2628 -0
  90. package/dist/chat/server.js.map +1 -0
  91. package/dist/chat/sessions.cjs +380 -0
  92. package/dist/chat/sessions.cjs.map +1 -0
  93. package/dist/chat/sessions.d.cts +158 -0
  94. package/dist/chat/sessions.d.ts +158 -0
  95. package/dist/chat/sessions.js +376 -0
  96. package/dist/chat/sessions.js.map +1 -0
  97. package/dist/chat/sqlite.cjs +441 -0
  98. package/dist/chat/sqlite.cjs.map +1 -0
  99. package/dist/chat/sqlite.d.cts +128 -0
  100. package/dist/chat/sqlite.d.ts +128 -0
  101. package/dist/chat/sqlite.js +435 -0
  102. package/dist/chat/sqlite.js.map +1 -0
  103. package/dist/chat/state.cjs +190 -0
  104. package/dist/chat/state.cjs.map +1 -0
  105. package/dist/chat/state.d.cts +95 -0
  106. package/dist/chat/state.d.ts +95 -0
  107. package/dist/chat/state.js +180 -0
  108. package/dist/chat/state.js.map +1 -0
  109. package/dist/chat/storage.cjs +249 -0
  110. package/dist/chat/storage.cjs.map +1 -0
  111. package/dist/chat/storage.d.cts +197 -0
  112. package/dist/chat/storage.d.ts +197 -0
  113. package/dist/chat/storage.js +245 -0
  114. package/dist/chat/storage.js.map +1 -0
  115. package/dist/errors-C-so0M4t.d.cts +33 -0
  116. package/dist/errors-C-so0M4t.d.ts +33 -0
  117. package/dist/errors-CmVvczxZ.d.cts +28 -0
  118. package/dist/errors-CmVvczxZ.d.ts +28 -0
  119. package/dist/in-process-transport-C1JnJGVR.d.ts +228 -0
  120. package/dist/in-process-transport-C7DSqPyX.d.cts +228 -0
  121. package/dist/index.cjs +365 -59
  122. package/dist/index.cjs.map +1 -1
  123. package/dist/index.d.cts +322 -125
  124. package/dist/index.d.ts +322 -125
  125. package/dist/index.js +359 -60
  126. package/dist/index.js.map +1 -1
  127. package/dist/provider-types-PTSlRPNB.d.cts +39 -0
  128. package/dist/provider-types-PTSlRPNB.d.ts +39 -0
  129. package/dist/refresh-manager-B81PpYBr.d.cts +153 -0
  130. package/dist/refresh-manager-Dlv_iNZi.d.ts +153 -0
  131. package/dist/testing.cjs +383 -0
  132. package/dist/testing.cjs.map +1 -0
  133. package/dist/testing.d.cts +132 -0
  134. package/dist/testing.d.ts +132 -0
  135. package/dist/testing.js +377 -0
  136. package/dist/testing.js.map +1 -0
  137. package/dist/token-store-CSUBgYwn.d.ts +48 -0
  138. package/dist/token-store-CuC4hB9Z.d.cts +48 -0
  139. package/dist/transport-Cdh3M0tS.d.cts +68 -0
  140. package/dist/transport-Ciap4PWK.d.ts +68 -0
  141. package/dist/types-4vbcmPTp.d.cts +143 -0
  142. package/dist/types-BxggH0Yh.d.ts +143 -0
  143. package/dist/types-DRgd_9R7.d.cts +363 -0
  144. package/dist/types-ajANVzf7.d.ts +363 -0
  145. 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
- An interactive demo for testing multi-turn conversations across all backends is in `examples/auth-demo/`.
933
+ Complete chat app showcasing the full SDK.
508
934
 
509
935
  ```bash
510
- # CLI mode
511
- npx tsx examples/auth-demo/index.ts
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
- # Web UI via Docker (http://localhost:3456)
514
- docker compose -f examples/auth-demo/docker-compose.yml up
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
- Features: backend selection, auth flows (Device Flow, OAuth+PKCE, API key), multi-turn chat with persistent sessions, 7 keyboard shortcuts for common test messages, 3 demo tools (search, calculator, format with approval), streaming event display with ANSI colors, per-turn statistics, and provider switching.
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