@contractspec/module.ai-chat 4.3.5 → 4.3.7

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 (46) hide show
  1. package/README.md +62 -370
  2. package/dist/adapters/ai-sdk-bundle-adapter.d.ts +1 -1
  3. package/dist/adapters/index.d.ts +1 -1
  4. package/dist/browser/context/index.js +93 -93
  5. package/dist/browser/core/index.js +308 -307
  6. package/dist/browser/index.js +3803 -3801
  7. package/dist/browser/presentation/components/index.js +2617 -2618
  8. package/dist/browser/presentation/hooks/index.js +476 -476
  9. package/dist/browser/presentation/index.js +2474 -2475
  10. package/dist/browser/providers/index.js +7 -7
  11. package/dist/context/index.d.ts +1 -1
  12. package/dist/context/index.js +93 -93
  13. package/dist/core/agent-tools-adapter.d.ts +1 -1
  14. package/dist/core/chat-service.d.ts +4 -4
  15. package/dist/core/create-chat-route.d.ts +1 -1
  16. package/dist/core/create-completion-route.d.ts +15 -0
  17. package/dist/core/export-formatters.d.ts +1 -1
  18. package/dist/core/index.d.ts +6 -6
  19. package/dist/core/index.js +308 -307
  20. package/dist/core/local-storage-conversation-store.d.ts +1 -1
  21. package/dist/core/surface-planner-tools.d.ts +2 -2
  22. package/dist/core/workflow-tools.d.ts +1 -1
  23. package/dist/index.d.ts +8 -8
  24. package/dist/index.js +3803 -3801
  25. package/dist/node/context/index.js +93 -93
  26. package/dist/node/core/index.js +308 -307
  27. package/dist/node/index.js +3803 -3801
  28. package/dist/node/presentation/components/index.js +2617 -2618
  29. package/dist/node/presentation/hooks/index.js +476 -476
  30. package/dist/node/presentation/index.js +2474 -2475
  31. package/dist/node/providers/index.js +7 -7
  32. package/dist/presentation/components/ChainOfThought.d.ts +1 -1
  33. package/dist/presentation/components/ChatExportToolbar.d.ts +1 -1
  34. package/dist/presentation/components/ChatSidebar.d.ts +1 -1
  35. package/dist/presentation/components/ChatWithExport.d.ts +1 -1
  36. package/dist/presentation/components/index.d.ts +11 -11
  37. package/dist/presentation/components/index.js +2617 -2618
  38. package/dist/presentation/hooks/index.d.ts +4 -4
  39. package/dist/presentation/hooks/index.js +476 -476
  40. package/dist/presentation/hooks/useChat.d.ts +6 -6
  41. package/dist/presentation/hooks/useConversations.d.ts +1 -1
  42. package/dist/presentation/hooks/useProviders.d.ts +1 -1
  43. package/dist/presentation/index.js +2474 -2475
  44. package/dist/providers/index.d.ts +2 -2
  45. package/dist/providers/index.js +7 -7
  46. package/package.json +15 -15
package/README.md CHANGED
@@ -1,385 +1,77 @@
1
1
  # @contractspec/module.ai-chat
2
2
 
3
- Website: https://contractspec.io/
3
+ Website: https://contractspec.io
4
4
 
5
+ **AI chat module with context, core runtime, presentation components, hooks, providers, and agent-aware workflows.**
5
6
 
6
- **ContractSpec Vibe Coding Chat** — AI-powered conversational coding assistant for ContractSpec.
7
+ ## What It Provides
7
8
 
8
- ## Overview
9
+ - Provides the packaged chat feature layer used by examples and higher-level applications.
10
+ - Supports MCP tools, provider integration, presentation rendering, forms, and agent-mode workflows.
11
+ - Acts as the main composition layer between the low-level agent runtime and user-facing chat UIs.
12
+ - `src/adapters/` contains runtime, provider, or environment-specific adapters.
13
+ - `src/docs/` contains docblocks and documentation-facing exports.
14
+ - `src/presentation/` contains presentation-layer components and renderers.
9
15
 
10
- This module provides a reusable AI chat system that can be integrated into CLI, VSCode extension, and ContractSpec Studio. It supports multiple LLM providers with full workspace context for vibe coding.
16
+ ## Installation
11
17
 
12
- ## Features
18
+ `npm install @contractspec/module.ai-chat`
13
19
 
14
- - **Multi-Provider Support**: OpenAI, Anthropic, Mistral, Google Gemini, and local Ollama
15
- - **Three Provider Modes**: Local (Ollama), BYOK (Bring Your Own Key), Managed (API proxy)
16
- - **Full Workspace Context**: Access specs, files, and codebase for context-aware assistance
17
- - **Streaming Responses**: Real-time token streaming for responsive UX
18
- - **Usage Tracking**: Integrated metering and cost tracking
19
- - **UI Components**: React components for chat interfaces
20
- - **Export**: Export conversations to Markdown, TXT, or JSON; select one or many messages for export
21
- - **Conversation Management**: New conversation, history sidebar, fork, edit messages, organize with projects and tags
22
- - **Thinking Levels**: Select reasoning depth (instant, thinking, extra thinking, max); maps to Anthropic budgetTokens and OpenAI reasoningEffort
23
- - **Workflow Creation Tools**: Create and modify workflows conversationally via `create_workflow_extension`, `compose_workflow`, and `generate_workflow_spec_code` (requires `@contractspec/lib.workflow-composer`)
24
- - **ModelSelector**: Dynamic model selection by task dimension (reasoning vs latency) when using `@contractspec/lib.ai-providers` ModelSelector
25
- - **Contracts-Spec Context**: Expose agent, data-views, operations, forms, and presentations to the model via `contractsContext`; agent tools can be wired from `AgentToolConfig[]`
26
- - **Surface-Runtime Integration**: Full support for `@contractspec/lib.surface-runtime` — pass `surfacePlanConfig` to enable `propose-patch` tool; chat can propose layout changes for user approval
27
- - **Presentation/Form Rendering**: Pass `presentationRenderer` and `formRenderer` to `ChatWithSidebar`; tool results with `presentationKey` or `formKey` render via host-provided components
28
- - **MCP Tools**: Pass `mcpServers` (from `@contractspec/lib.ai-agent`) to `useChat`; tools from MCP servers are merged into chat tools
29
- - **Agent Mode**: Pass `agentMode: { agent }` with a `ChatAgentAdapter` (use `createChatAgentAdapter` to wrap `ContractSpecAgent`); chat uses the agent for generation instead of ChatService
20
+ or
30
21
 
31
- ## Bundle Spec Alignment (07_ai_native_chat)
32
-
33
- This module aligns with `specs/contractspec_modules_bundle_spec_2026-03-08`. `useChat` and `ChatContainer` provide the assistant slot UI for bundle surfaces. `AiChatFeature` (key `ai-chat`, version `1.0.0`) matches `ModuleBundleSpec.requires`. The `tools` option on `UseChatOptions` is wired to `streamText`; use `requireApproval: true` for tools that need user confirmation (requires server route for full support).
34
-
35
- ## Related Packages
36
-
37
- - `@contractspec/lib.ai-providers` — Shared provider abstraction (types, factory, validation), ModelSelector for dynamic model selection
38
- - `@contractspec/lib.workflow-composer` — Workflow composition and validation (optional; required for workflow creation tools)
39
- - `@contractspec/lib.ai-agent` — Agent orchestration and tool execution; `AgentToolConfig` for agent tools
40
- - `@contractspec/lib.surface-runtime` — Bundle surfaces, planner tools, `AiSdkBundleAdapter`; full integration when used in PM workbench
41
-
42
- ## Providers
43
-
44
- | Provider | Local | BYOK | Managed |
45
- |----------|-------|------|---------|
46
- | Ollama | ✅ | - | - |
47
- | OpenAI | - | ✅ | ✅ |
48
- | Anthropic | - | ✅ | ✅ |
49
- | Mistral | - | ✅ | ✅ |
50
- | Google Gemini | - | ✅ | ✅ |
22
+ `bun add @contractspec/module.ai-chat`
51
23
 
52
24
  ## Usage
53
25
 
54
- ### Basic Chat
55
-
56
- ```typescript
57
- import { createProvider } from '@contractspec/lib.ai-providers';
58
- import { ChatService } from '@contractspec/module.ai-chat';
59
-
60
- const provider = createProvider({
61
- provider: 'openai',
62
- apiKey: process.env.OPENAI_API_KEY,
63
- model: 'gpt-4o',
64
- });
65
-
66
- const chatService = new ChatService({ provider });
67
-
68
- const response = await chatService.send({
69
- content: 'Help me create a new API endpoint',
70
- });
71
- ```
72
-
73
- ### With Workspace Context
74
-
75
- ```typescript
76
- import { createProvider } from '@contractspec/lib.ai-providers';
77
- import { ChatService, WorkspaceContext } from '@contractspec/module.ai-chat';
78
-
79
- const context = await WorkspaceContext.fromPath('/path/to/project');
80
- const provider = createProvider({ provider: 'anthropic', proxyUrl: '/api/chat' });
81
-
82
- const chatService = new ChatService({ provider, context });
83
-
84
- // The chat now has access to specs, files, and can suggest code changes
85
- const response = await chatService.send({
86
- content: 'Add validation to the user.create command',
87
- });
88
- ```
89
-
90
- ### React Components
91
-
92
- **With export and message selection** (recommended):
93
-
94
- ```tsx
95
- import { ChatWithExport, ChatInput, useChat } from '@contractspec/module.ai-chat';
96
-
97
- function VibeCodingChat() {
98
- const { messages, conversation, sendMessage, isLoading } = useChat({
99
- provider: 'openai',
100
- mode: 'managed',
101
- });
102
-
103
- return (
104
- <ChatWithExport messages={messages} conversation={conversation}>
105
- <ChatInput onSend={sendMessage} disabled={isLoading} />
106
- </ChatWithExport>
107
- );
108
- }
109
- ```
110
-
111
- `ChatWithExport` provides an export toolbar (Markdown, TXT, JSON, copy to clipboard) and message selection checkboxes. Select messages to export only those, or export all when none are selected.
112
-
113
- **With sidebar (history, new, fork, edit, project/tags)**:
114
-
115
- ```tsx
116
- import { ChatWithSidebar } from '@contractspec/module.ai-chat';
117
-
118
- function FullChat() {
119
- return (
120
- <ChatWithSidebar
121
- systemPrompt="You are a helpful assistant."
122
- />
123
- );
124
- }
125
- ```
126
-
127
- `ChatWithSidebar` includes a conversation history sidebar (LocalStorage-persisted), New/Fork buttons, message edit, project/tags organization, and a **Thinking Level** picker (instant, thinking, extra thinking, max). Uses `createLocalStorageConversationStore()` by default.
128
-
129
- **Basic (no export)**:
130
-
131
- ```tsx
132
- import { ChatContainer, ChatMessage, ChatInput } from '@contractspec/module.ai-chat/presentation/components';
133
- import { useChat } from '@contractspec/module.ai-chat/presentation/hooks';
134
-
135
- function BasicChat() {
136
- const { messages, sendMessage, isLoading } = useChat();
137
-
138
- return (
139
- <ChatContainer>
140
- {messages.map((msg) => <ChatMessage key={msg.id} message={msg} />)}
141
- <ChatInput onSend={sendMessage} disabled={isLoading} />
142
- </ChatContainer>
143
- );
144
- }
145
- ```
146
-
147
- ### AI SDK Parity
148
-
149
- This module aligns with the [Vercel AI SDK](https://sdk.vercel.ai) and AI Elements feature set:
150
-
151
- - **fullStream**: Reasoning, tools, and sources from `streamText` fullStream
152
- - **Thinking Levels**: `thinkingLevel` option (instant, thinking, extra_thinking, max) maps to Anthropic `budgetTokens` and OpenAI `reasoningEffort`; `ThinkingLevelPicker` in `ChatWithSidebar`
153
- - **Tools**: Pass `tools` to `ChatServiceConfig` or `useChat`; supports `requireApproval` for approval workflow; optional `workflowToolsConfig` adds workflow creation tools
154
- - **Message parts**: `ChatMessage` renders reasoning (collapsible), sources (citations), and tool invocations
155
- - **Markdown**: Inline links and code blocks in message content
156
- - **Export**: `ChatWithExport` and `ChatExportToolbar` support Markdown (.md), Plain Text (.txt), and JSON (.json); select messages for partial export or export all
157
- - **Conversation Management**: `ChatWithSidebar` provides history, new conversation, fork, edit messages; `useChat` exposes `createNewConversation`, `editMessage`, `forkConversation`, `updateConversation`; `useConversations` for listing; `createLocalStorageConversationStore()` for persistence
158
-
159
- ### Server Route (Full AI SDK + Tool Approval)
160
-
161
- For full AI SDK compatibility including tool approval, use `createChatRoute` with `@ai-sdk/react` useChat:
162
-
163
- ```ts
164
- // app/api/chat/route.ts (Next.js App Router)
165
- import {
166
- createChatRoute,
167
- CHAT_ROUTE_MAX_DURATION,
168
- } from '@contractspec/module.ai-chat/core';
169
- import { createProvider } from '@contractspec/lib.ai-providers';
170
-
171
- const provider = createProvider({
172
- provider: 'openai',
173
- apiKey: process.env.OPENAI_API_KEY,
174
- model: 'gpt-4o',
175
- });
176
-
177
- export const POST = createChatRoute({ provider });
178
- export const maxDuration = CHAT_ROUTE_MAX_DURATION;
179
- ```
180
-
181
- `maxDuration` is required for long-running streaming on Vercel/serverless. The route sends sources and reasoning by default (AI Elements parity). Override with `sendSources` and `sendReasoning` in options. Use `getModel` to support a model picker from the request body.
182
-
183
- ```tsx
184
- // Client: use @ai-sdk/react useChat with DefaultChatTransport
185
- import { useChat } from '@ai-sdk/react';
186
-
187
- const { messages, sendMessage } = useChat({
188
- api: '/api/chat',
189
- });
190
- ```
191
-
192
- The custom `useChat` from this module works with `ChatService` for simple deployments (no tools, no approval). For tools with `requireApproval`, use the server route pattern above.
193
-
194
- ### Workflow Creation Tools
195
-
196
- When `workflowToolsConfig` is provided, the chat can create and modify workflows via AI SDK tools:
197
-
198
- ```typescript
199
- import { ChatService, createWorkflowTools } from '@contractspec/module.ai-chat';
200
- import { WorkflowComposer } from '@contractspec/lib.workflow-composer';
201
- import { createProvider } from '@contractspec/lib.ai-providers';
202
-
203
- const baseWorkflows = [/* your WorkflowSpec[] */];
204
- const provider = createProvider({ provider: 'anthropic', model: 'claude-sonnet-4' });
205
-
206
- const chatService = new ChatService({
207
- provider,
208
- workflowToolsConfig: {
209
- baseWorkflows,
210
- composer: new WorkflowComposer(),
211
- },
212
- });
213
-
214
- // User can say: "Add a legal review step after validate-invoice"
215
- // The model will call create_workflow_extension, compose_workflow, generate_workflow_spec_code
216
- ```
217
-
218
- Tools: `create_workflow_extension` (validate extensions), `compose_workflow` (apply extensions to base), `generate_workflow_spec_code` (output TypeScript). Export `createWorkflowTools(baseWorkflows, composer)` for manual wiring.
219
-
220
- ### ModelSelector (Dynamic Model Selection)
221
-
222
- Use `modelSelector` to pick models by task dimension (e.g. reasoning vs latency):
223
-
224
- ```typescript
225
- import { createModelSelector } from '@contractspec/lib.ai-providers';
226
- import { ChatService } from '@contractspec/module.ai-chat';
227
-
228
- const modelSelector = createModelSelector(/* ranking config */);
229
- const chatService = new ChatService({
230
- provider: createProvider({ /* ... */ }),
231
- modelSelector,
232
- });
233
-
234
- // ChatService will call modelSelector.selectAndCreate({ taskDimension: 'reasoning' | 'latency' })
235
- // based on thinking level when modelSelector is set
236
- ```
237
-
238
- ### AI Elements-Style Components
239
-
240
- The module includes native implementations of [Reasoning](https://elements.ai-sdk.dev/components/reasoning), [Sources](https://elements.ai-sdk.dev/components/sources), [Suggestion](https://elements.ai-sdk.dev/components/suggestion), and [Chain of Thought](https://elements.ai-sdk.dev/components/chain-of-thought) patterns. Pass `components` to `ChatMessage` or `ChatWithExport` to override with ai-elements when installed:
241
-
242
- ```tsx
243
- import { ChatWithSidebar, type ChatMessageComponents } from '@contractspec/module.ai-chat';
244
- // When using ai-elements: import { Reasoning, Sources, ... } from '@/components/ai-elements/...';
245
-
246
- <ChatWithSidebar
247
- components={{
248
- Reasoning: AiElementsReasoning,
249
- Sources: AiElementsSources,
250
- SourcesTrigger: AiElementsSourcesTrigger,
251
- Source: AiElementsSource,
252
- ChainOfThought: AiElementsChainOfThought,
253
- ChainOfThoughtStep: AiElementsChainOfThoughtStep,
254
- }}
255
- suggestions={['Explain how X works', 'What is Y?']}
256
- showSuggestionsWhenEmpty
257
- />
258
- ```
259
-
260
- Use `suggestions` and `onSuggestionClick` for clickable prompt chips. Apps can optionally use [AI Elements](https://elements.ai-sdk.dev) for enhanced UI; provide an adapter from `ChatMessage` to `UIMessage` when integrating.
261
-
262
- ### useCompletion (Non-Chat Completion)
263
-
264
- For inline suggestions, single-prompt completion, or other non-conversational use cases:
265
-
266
- ```tsx
267
- import { useCompletion } from '@contractspec/module.ai-chat/presentation/hooks';
268
- // or: import { useCompletion } from '@ai-sdk/react';
269
-
270
- const { completion, complete, isLoading } = useCompletion({
271
- api: '/api/completion',
272
- });
273
- ```
274
-
275
- Use `createCompletionRoute` for the API endpoint (see `createChatRoute` pattern).
276
-
277
- ### Contracts-Spec Context and Surface-Runtime
278
-
279
- Pass `contractsContext` to expose agent, data-views, operations, forms, and presentations to the model. Pass `surfacePlanConfig` when embedding chat in a surface-runtime (e.g. PM workbench) to enable the `propose-patch` tool:
280
-
281
- ```tsx
282
- import { ChatWithSidebar } from '@contractspec/module.ai-chat';
283
- import type { ResolvedSurfacePlan } from '@contractspec/lib.surface-runtime/runtime/resolve-bundle';
284
-
285
- function PmWorkbench({ plan }: { plan: ResolvedSurfacePlan }) {
286
- const [currentPlan, setPlan] = useState(plan);
287
- const onPatchProposal = useCallback((proposal) => {
288
- setPlan(prev => ({
289
- ...prev,
290
- ai: { ...prev.ai, proposals: [...(prev.ai?.proposals ?? []), proposal] },
291
- }));
292
- }, []);
293
-
294
- return (
295
- <ChatWithSidebar
296
- surfacePlanConfig={{ plan: currentPlan, onPatchProposal }}
297
- systemPrompt="You are a PM workbench assistant. Propose layout changes when helpful."
298
- />
299
- );
300
- }
301
- ```
302
-
303
- `createAiSdkBundleAdapter` from `@contractspec/module.ai-chat/adapters` implements `AiSdkBundleAdapter` for surface-runtime planner integration.
304
-
305
- ### streamObject / generateObject
306
-
307
- For structured output (schema-driven generation), use the AI SDK directly: `streamObject` and `generateObject` from `ai`. This module focuses on chat; add `useObject` or equivalent in a separate module when needed.
308
-
309
- ### Voice / Speech
310
-
311
- Speech Input, Transcription, Voice Selector, and related UI are planned as a separate submodule or feature flag. Track via roadmap.
26
+ Import the root entrypoint from `@contractspec/module.ai-chat`, or choose a documented subpath when you only need one part of the package surface.
312
27
 
313
28
  ## Architecture
314
29
 
315
- ```
316
- ┌─────────────────────────────────────────────────────────────┐
317
- │ Consumer Surfaces │
318
- │ ┌─────────┐ ┌──────────────┐ ┌─────────────────┐ │
319
- │ │ CLI │ │ VSCode │ │ Studio │ │
320
- │ └────┬────┘ └──────┬───────┘ └────────┬────────┘ │
321
- └───────┼────────────────┼─────────────────────┼─────────────┘
322
- │ │ │
323
- ▼ ▼ ▼
324
- ┌─────────────────────────────────────────────────────────────┐
325
- │ @contractspec/module.ai-chat │
326
- │ ┌────────────┐ ┌──────────────┐ ┌───────────────────┐ │
327
- │ │ ChatService│ │ Providers │ │ Workspace Context │ │
328
- │ │ │ │ (re-exports) │ │ │ │
329
- │ └────────────┘ └──────┬───────┘ └───────────────────┘ │
330
- │ │ │
331
- │ ┌──────────────────────┼───────────────────────────────┐ │
332
- │ │ UI Components (React) │ │
333
- │ └──────────────────────────────────────────────────────┘ │
334
- └─────────────────────────┼───────────────────────────────────┘
335
-
336
- ┌─────────────────┴─────────────────┐
337
- ▼ ▼
338
- ┌───────────────────┐ ┌──────────────────────────┐
339
- @contractspec/lib. │ │ @contractspec/lib.ai-agent │
340
- ai-providers │ │ │
341
- ┌───────────────┐ │ │ Agent orchestration, │
342
- Provider │ │ │ tool execution, │
343
- abstraction │ │ │ memory framework │
344
- └───────────────┘ │ └──────────────────────────┘
345
- ┌───────────────┐
346
- Model info │
347
- & validation │
348
- │ └───────────────┘ │
349
- └───────────────────┘
350
-
351
-
352
- ┌───────────────────┐ ┌──────────────────────────┐
353
- │ Local Providers │ │ API Proxy │
354
- │ ┌──────────────┐ │ │ ┌──────────────────────┐│
355
- │ │ Ollama │ │ │ │ Metering + Costing ││
356
- │ └──────────────┘ │ │ └──────────────────────┘│
357
- └───────────────────┘ │ ┌──────────────────────┐│
358
- │ │ Cloud Providers ││
359
- │ │ OpenAI/Anthropic/etc ││
360
- │ └──────────────────────┘│
361
- └──────────────────────────┘
362
- ```
363
-
364
- ## Metrics
365
-
366
- The module tracks the following metrics via `@contractspec/lib.metering`:
367
-
368
- | Metric | Description |
369
- |--------|-------------|
370
- | `ai_chat.messages` | Total chat messages sent |
371
- | `ai_chat.tokens_input` | Input tokens consumed |
372
- | `ai_chat.tokens_output` | Output tokens generated |
373
- | `ai_chat.latency_ms` | Response latency |
374
- | `ai_chat.errors` | Failed completions |
375
-
376
- ## Feature Flags
377
-
378
- - `ai_chat.enabled` - Master toggle for the chat feature
379
- - `ai_chat.managed_keys` - Enable managed key mode (API proxy)
380
- - `ai_chat.workspace_context` - Enable workspace read/write access
381
- - `ai_chat.code_execution` - Enable code execution (future)
382
-
383
- ## License
384
-
385
- MIT
30
+ - `src/context/` contains shared chat providers and contextual runtime state.
31
+ - `src/core/` contains chat orchestration, workflows, and non-UI runtime logic.
32
+ - `src/presentation/` exports UI components and React hooks for embedding the chat experience.
33
+ - `src/providers/` exposes provider bindings and provider-facing integration helpers.
34
+ - Top-level feature, capability, operation, schema, and event files define the module contract surface.
35
+ - `src/ai-chat.capability.ts` defines a capability surface.
36
+
37
+ ## Public Entry Points
38
+
39
+ - Exports the root module plus context, core, presentation, presentation/components, presentation/hooks, and providers subpaths.
40
+ - Export `.` resolves through `./src/index.ts`.
41
+ - Export `./context` resolves through `./src/context/index.ts`.
42
+ - Export `./core` resolves through `./src/core/index.ts`.
43
+ - Export `./presentation` resolves through `./src/presentation/index.ts`.
44
+ - Export `./presentation/components` resolves through `./src/presentation/components/index.ts`.
45
+ - Export `./presentation/hooks` resolves through `./src/presentation/hooks/index.ts`.
46
+ - Export `./providers` resolves through `./src/providers/index.ts`.
47
+
48
+ ## Local Commands
49
+
50
+ - `bun run dev` — contractspec-bun-build dev
51
+ - `bun run build` — bun run prebuild && bun run build:bundle && bun run build:types
52
+ - `bun run test` — bun test
53
+ - `bun run lint` — bun lint:fix
54
+ - `bun run lint:check` — biome check .
55
+ - `bun run lint:fix` — biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .
56
+ - `bun run typecheck` — tsc --noEmit
57
+ - `bun run publish:pkg` — bun publish --tolerate-republish --ignore-scripts --verbose
58
+ - `bun run publish:pkg:canary` — bun publish:pkg --tag canary
59
+ - `bun run clean` — rimraf dist .turbo
60
+ - `bun run build:bundle` — contractspec-bun-build transpile
61
+ - `bun run build:types` — contractspec-bun-build types
62
+ - `bun run prebuild` — contractspec-bun-build prebuild
63
+
64
+ ## Recent Updates
65
+
66
+ - Replace eslint+prettier by biomejs to optimize speed.
67
+ - Agentic workflows — subagents, memory tools, and next steps.
68
+ - Vnext ai-native.
69
+ - Backend operations + frontend rendering support.
70
+ - Use browser-safe MCP client stub in client bundles.
71
+ - Add changesets and apply pending fixes.
72
+
73
+ ## Notes
74
+
75
+ - Depends on `lib.ai-agent`, `lib.ai-providers`, `lib.contracts-spec`, `lib.schema`, `lib.metering`, `lib.cost-tracking`, `lib.surface-runtime`.
76
+ - React peer dependency (>=19.2.4); changes here affect all chat surfaces.
77
+ - Metering and cost-tracking are wired in -- never bypass them.
@@ -2,9 +2,9 @@
2
2
  * AiSdkBundleAdapter implementation using ChatService.
3
3
  * Enables surface-runtime to drive chat for planner/requestPatches flows.
4
4
  */
5
+ import type { Provider as ChatProvider } from '@contractspec/lib.ai-providers';
5
6
  import type { AiSdkBundleAdapter } from '@contractspec/lib.surface-runtime/adapters/interfaces';
6
7
  import type { SurfacePatchProposal } from '@contractspec/lib.surface-runtime/spec/types';
7
- import type { Provider as ChatProvider } from '@contractspec/lib.ai-providers';
8
8
  export interface CreateAiSdkBundleAdapterDeps {
9
9
  /** Provider for creating ChatService (from createProvider) */
10
10
  provider: ChatProvider;
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * Adapters for integrating ai-chat with external runtimes.
3
3
  */
4
- export { createAiSdkBundleAdapter, type CreateAiSdkBundleAdapterDeps, } from './ai-sdk-bundle-adapter';
4
+ export { type CreateAiSdkBundleAdapterDeps, createAiSdkBundleAdapter, } from './ai-sdk-bundle-adapter';
@@ -6,99 +6,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  throw Error('Dynamic require of "' + x + '" is not supported');
7
7
  });
8
8
 
9
- // src/context/workspace-context.ts
10
- class WorkspaceContext {
11
- workspacePath;
12
- allowWrites;
13
- specs = [];
14
- files = [];
15
- initialized = false;
16
- constructor(config) {
17
- this.workspacePath = config.workspacePath;
18
- this.allowWrites = config.allowWrites ?? false;
19
- }
20
- async initialize() {
21
- if (this.initialized)
22
- return;
23
- this.initialized = true;
24
- }
25
- getSpecs() {
26
- return this.specs;
27
- }
28
- getFiles() {
29
- return this.files;
30
- }
31
- addSpecs(specs) {
32
- this.specs.push(...specs);
33
- }
34
- addFiles(files) {
35
- this.files.push(...files);
36
- }
37
- getSummary() {
38
- const commands = this.specs.filter((s) => s.type === "command").length;
39
- const queries = this.specs.filter((s) => s.type === "query").length;
40
- const events = this.specs.filter((s) => s.type === "event").length;
41
- const presentations = this.specs.filter((s) => s.type === "presentation").length;
42
- const tsFiles = this.files.filter((f) => f.extension === ".ts").length;
43
- const specFiles = this.files.filter((f) => f.isSpec).length;
44
- return {
45
- name: this.workspacePath.split("/").pop() ?? "workspace",
46
- path: this.workspacePath,
47
- specs: {
48
- total: this.specs.length,
49
- commands,
50
- queries,
51
- events,
52
- presentations
53
- },
54
- files: {
55
- total: this.files.length,
56
- typescript: tsFiles,
57
- specFiles
58
- }
59
- };
60
- }
61
- getContextSummary() {
62
- const summary = this.getSummary();
63
- const parts = [
64
- `Workspace: ${summary.name}`,
65
- `Path: ${summary.path}`,
66
- "",
67
- "### Specs",
68
- `- Commands: ${summary.specs.commands}`,
69
- `- Queries: ${summary.specs.queries}`,
70
- `- Events: ${summary.specs.events}`,
71
- `- Presentations: ${summary.specs.presentations}`
72
- ];
73
- if (this.specs.length > 0) {
74
- parts.push("", "### Available Specs");
75
- for (const spec of this.specs.slice(0, 20)) {
76
- parts.push(`- ${spec.name} (${spec.type})`);
77
- }
78
- if (this.specs.length > 20) {
79
- parts.push(`- ... and ${this.specs.length - 20} more`);
80
- }
81
- }
82
- return parts.join(`
83
- `);
84
- }
85
- findSpecs(query) {
86
- const lowerQuery = query.toLowerCase();
87
- return this.specs.filter((s) => s.name.toLowerCase().includes(lowerQuery) || s.description?.toLowerCase().includes(lowerQuery) || s.tags?.some((t) => t.toLowerCase().includes(lowerQuery)));
88
- }
89
- findFiles(query) {
90
- const lowerQuery = query.toLowerCase();
91
- return this.files.filter((f) => f.path.toLowerCase().includes(lowerQuery) || f.name.toLowerCase().includes(lowerQuery));
92
- }
93
- }
94
- async function createWorkspaceContext(path, options) {
95
- const context = new WorkspaceContext({
96
- workspacePath: path,
97
- ...options
98
- });
99
- await context.initialize();
100
- return context;
101
- }
102
9
  // src/context/context-builder.ts
103
10
  function estimateTokens(text) {
104
11
  return Math.ceil(text.length / 4);
@@ -405,6 +312,99 @@ function createNodeFileOperations(workspacePath, allowWrites = false) {
405
312
  };
406
313
  return new FileOperations(fs, workspacePath, allowWrites);
407
314
  }
315
+ // src/context/workspace-context.ts
316
+ class WorkspaceContext {
317
+ workspacePath;
318
+ allowWrites;
319
+ specs = [];
320
+ files = [];
321
+ initialized = false;
322
+ constructor(config) {
323
+ this.workspacePath = config.workspacePath;
324
+ this.allowWrites = config.allowWrites ?? false;
325
+ }
326
+ async initialize() {
327
+ if (this.initialized)
328
+ return;
329
+ this.initialized = true;
330
+ }
331
+ getSpecs() {
332
+ return this.specs;
333
+ }
334
+ getFiles() {
335
+ return this.files;
336
+ }
337
+ addSpecs(specs) {
338
+ this.specs.push(...specs);
339
+ }
340
+ addFiles(files) {
341
+ this.files.push(...files);
342
+ }
343
+ getSummary() {
344
+ const commands = this.specs.filter((s) => s.type === "command").length;
345
+ const queries = this.specs.filter((s) => s.type === "query").length;
346
+ const events = this.specs.filter((s) => s.type === "event").length;
347
+ const presentations = this.specs.filter((s) => s.type === "presentation").length;
348
+ const tsFiles = this.files.filter((f) => f.extension === ".ts").length;
349
+ const specFiles = this.files.filter((f) => f.isSpec).length;
350
+ return {
351
+ name: this.workspacePath.split("/").pop() ?? "workspace",
352
+ path: this.workspacePath,
353
+ specs: {
354
+ total: this.specs.length,
355
+ commands,
356
+ queries,
357
+ events,
358
+ presentations
359
+ },
360
+ files: {
361
+ total: this.files.length,
362
+ typescript: tsFiles,
363
+ specFiles
364
+ }
365
+ };
366
+ }
367
+ getContextSummary() {
368
+ const summary = this.getSummary();
369
+ const parts = [
370
+ `Workspace: ${summary.name}`,
371
+ `Path: ${summary.path}`,
372
+ "",
373
+ "### Specs",
374
+ `- Commands: ${summary.specs.commands}`,
375
+ `- Queries: ${summary.specs.queries}`,
376
+ `- Events: ${summary.specs.events}`,
377
+ `- Presentations: ${summary.specs.presentations}`
378
+ ];
379
+ if (this.specs.length > 0) {
380
+ parts.push("", "### Available Specs");
381
+ for (const spec of this.specs.slice(0, 20)) {
382
+ parts.push(`- ${spec.name} (${spec.type})`);
383
+ }
384
+ if (this.specs.length > 20) {
385
+ parts.push(`- ... and ${this.specs.length - 20} more`);
386
+ }
387
+ }
388
+ return parts.join(`
389
+ `);
390
+ }
391
+ findSpecs(query) {
392
+ const lowerQuery = query.toLowerCase();
393
+ return this.specs.filter((s) => s.name.toLowerCase().includes(lowerQuery) || s.description?.toLowerCase().includes(lowerQuery) || s.tags?.some((t) => t.toLowerCase().includes(lowerQuery)));
394
+ }
395
+ findFiles(query) {
396
+ const lowerQuery = query.toLowerCase();
397
+ return this.files.filter((f) => f.path.toLowerCase().includes(lowerQuery) || f.name.toLowerCase().includes(lowerQuery));
398
+ }
399
+ }
400
+ async function createWorkspaceContext(path, options) {
401
+ const context = new WorkspaceContext({
402
+ workspacePath: path,
403
+ ...options
404
+ });
405
+ await context.initialize();
406
+ return context;
407
+ }
408
408
  export {
409
409
  createWorkspaceContext,
410
410
  createNodeFileOperations,