@strand-js/core 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # @strand-js/core
2
+
3
+ Core types, client, and state primitives for [Strand](https://github.com/strand-js/strand) — AI state management for React.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @strand-js/core zod
9
+ ```
10
+
11
+ ## What's in here
12
+
13
+ - `createStrandClient(config)` — creates the HTTP client used by all React hooks
14
+ - `SessionStateMachine` — 4-state streaming lifecycle (`idle → submitting → streaming → done`)
15
+ - `ToolCallStore` — per-tool observable state for parallel tool call tracking
16
+ - `processWireEvent` — routes SSE wire events to session and tool state
17
+ - `validateMessages` — input sanitization for server handlers
18
+ - `tool(definition)` — Zod-typed tool definition helper
19
+ - All shared TypeScript types (`Session`, `Message`, `ToolCall`, `WireEvent`, etc.)
20
+
21
+ ## Usage
22
+
23
+ ```ts
24
+ import { createStrandClient } from '@strand-js/core'
25
+
26
+ const client = createStrandClient({
27
+ baseUrl: '/api/strand',
28
+ retry: { maxAttempts: 3, backoff: 'exponential' },
29
+ contextWindow: { strategy: 'truncate-oldest', maxTokens: 100_000 },
30
+ })
31
+ ```
32
+
33
+ ## Part of Strand
34
+
35
+ | Package | Description |
36
+ |---|---|
37
+ | `@strand-js/core` | Core types and client |
38
+ | `@strand-js/react` | React hooks |
39
+ | `@strand-js/anthropic` | Anthropic server handler |
40
+ | `@strand-js/openai` | OpenAI server handler |
41
+ | `@strand-js/react-native` | React Native streaming patch |
42
+
43
+ [Full documentation →](https://github.com/strand-js/strand)
@@ -0,0 +1,184 @@
1
+ import { ZodSchema } from 'zod';
2
+
3
+ type StreamingStatus = 'idle' | 'submitting' | 'streaming' | 'done' | 'error';
4
+ type ToolCallStatus = 'idle' | 'pending' | 'running' | 'done' | 'failed';
5
+ type AgentStatus = 'idle' | 'running' | 'paused' | 'done' | 'failed';
6
+ interface ToolCall {
7
+ id: string;
8
+ name: string;
9
+ input: Record<string, unknown>;
10
+ output: unknown | null;
11
+ status: ToolCallStatus;
12
+ error: Error | null;
13
+ }
14
+ interface Message {
15
+ id: string;
16
+ role: 'user' | 'assistant';
17
+ content: string;
18
+ toolCalls?: ToolCall[];
19
+ createdAt: Date;
20
+ }
21
+ interface TokenUsage {
22
+ input: number;
23
+ output: number;
24
+ total: number;
25
+ }
26
+ interface Session {
27
+ id: string;
28
+ messages: Message[];
29
+ status: StreamingStatus;
30
+ tokenUsage: TokenUsage;
31
+ error: Error | null;
32
+ }
33
+ interface ToolDefinition {
34
+ name: string;
35
+ description: string;
36
+ parameters: ZodSchema;
37
+ }
38
+ type RetryableError = 'rate_limit' | 'server_error' | 'timeout';
39
+ interface RetryConfig {
40
+ maxAttempts?: number;
41
+ backoff?: 'exponential' | 'linear' | 'none';
42
+ retryOn?: RetryableError[];
43
+ }
44
+ type ContextWindowStrategy = 'truncate-oldest' | 'sliding-window' | 'none';
45
+ interface ContextWindowConfig {
46
+ strategy?: ContextWindowStrategy;
47
+ maxTokens?: number;
48
+ }
49
+ interface StrandClientConfig {
50
+ baseUrl: string;
51
+ retry?: RetryConfig;
52
+ contextWindow?: ContextWindowConfig;
53
+ }
54
+ interface SendOptions {
55
+ context?: Record<string, unknown>;
56
+ signal?: AbortSignal;
57
+ }
58
+ interface StrandClient {
59
+ readonly config: StrandClientConfig;
60
+ send(messages: Message[], options?: SendOptions): AsyncGenerator<WireEvent>;
61
+ }
62
+ interface ToolCallState<TInput = unknown, TOutput = unknown> {
63
+ id: string | null;
64
+ toolName: string | null;
65
+ status: ToolCallStatus;
66
+ input: TInput | null;
67
+ output: TOutput | null;
68
+ error: Error | null;
69
+ }
70
+ interface AgentStep {
71
+ index: number;
72
+ description: string;
73
+ toolCalls: ToolCall[];
74
+ output: string | null;
75
+ status: 'running' | 'done' | 'failed';
76
+ }
77
+ type WireEvent = {
78
+ type: 'strand:start';
79
+ sessionId: string;
80
+ requestId: string;
81
+ } | {
82
+ type: 'strand:text-delta';
83
+ delta: string;
84
+ } | {
85
+ type: 'strand:tool-start';
86
+ toolCallId: string;
87
+ toolName: string;
88
+ } | {
89
+ type: 'strand:tool-input-delta';
90
+ toolCallId: string;
91
+ delta: string;
92
+ } | {
93
+ type: 'strand:tool-input-done';
94
+ toolCallId: string;
95
+ input: Record<string, unknown>;
96
+ } | {
97
+ type: 'strand:tool-result';
98
+ toolCallId: string;
99
+ result: unknown;
100
+ } | {
101
+ type: 'strand:tool-error';
102
+ toolCallId: string;
103
+ error: string;
104
+ } | {
105
+ type: 'strand:done';
106
+ usage: TokenUsage;
107
+ } | {
108
+ type: 'strand:error';
109
+ code: string;
110
+ message: string;
111
+ retryAfter?: number;
112
+ };
113
+
114
+ declare function createStrandClient(config: StrandClientConfig): StrandClient;
115
+
116
+ declare function tool(definition: ToolDefinition): ToolDefinition;
117
+
118
+ declare class SessionStateMachine {
119
+ private _session;
120
+ private _listeners;
121
+ private _resetTimer;
122
+ constructor(sessionId?: string);
123
+ get session(): Readonly<Session>;
124
+ subscribe(listener: (session: Session) => void): () => void;
125
+ private emit;
126
+ transition(status: StreamingStatus, error?: Error): void;
127
+ addUserMessage(content: string): void;
128
+ appendTextDelta(delta: string): void;
129
+ beginToolCall(toolCallId: string, toolName: string): void;
130
+ updateToolCall(toolCallId: string, update: Partial<Omit<ToolCall, 'id' | 'name'>>): void;
131
+ updateTokenUsage(usage: TokenUsage): void;
132
+ clear(): void;
133
+ }
134
+ declare function generateId(): string;
135
+
136
+ declare function applyContextWindow(messages: Message[], config: ContextWindowConfig): Message[];
137
+
138
+ declare class StrandError extends Error {
139
+ readonly status: number;
140
+ readonly code: string;
141
+ constructor(status: number, message: string, code: string);
142
+ }
143
+ declare function withRetry<T>(fn: () => Promise<T>, config: RetryConfig): Promise<T>;
144
+
145
+ declare function parseSSEStream(body: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncGenerator<WireEvent>;
146
+
147
+ declare class ToolCallStore {
148
+ private _idToName;
149
+ private _states;
150
+ private _listeners;
151
+ getState<TInput = unknown, TOutput = unknown>(toolName: string): ToolCallState<TInput, TOutput>;
152
+ subscribe(toolName: string, listener: (state: ToolCallState) => void): () => void;
153
+ onToolStart(toolCallId: string, toolName: string): void;
154
+ onToolInputDone(toolCallId: string, input: Record<string, unknown>): void;
155
+ onToolResult(toolCallId: string, result: unknown): void;
156
+ onToolError(toolCallId: string, error: Error): void;
157
+ resetAll(): void;
158
+ private _set;
159
+ }
160
+
161
+ declare function processWireEvent(event: WireEvent, session: SessionStateMachine, toolStore: ToolCallStore): void;
162
+
163
+ interface JsonSchema {
164
+ type: string;
165
+ properties?: Record<string, unknown>;
166
+ required?: string[];
167
+ [key: string]: unknown;
168
+ }
169
+ declare function toolToJsonSchema(tool: ToolDefinition): JsonSchema;
170
+
171
+ interface RequestValidationConfig {
172
+ maxMessages?: number;
173
+ maxMessageLength?: number;
174
+ }
175
+ type ValidationResult = {
176
+ ok: true;
177
+ } | {
178
+ ok: false;
179
+ status: number;
180
+ error: string;
181
+ };
182
+ declare function validateMessages(messages: unknown, config?: RequestValidationConfig): ValidationResult;
183
+
184
+ export { type AgentStatus, type AgentStep, type ContextWindowConfig, type ContextWindowStrategy, type JsonSchema, type Message, type RequestValidationConfig, type RetryConfig, type SendOptions, type Session, SessionStateMachine, type StrandClient, type StrandClientConfig, StrandError, type StreamingStatus, type TokenUsage, type ToolCall, type ToolCallState, type ToolCallStatus, ToolCallStore, type ToolDefinition, type ValidationResult, type WireEvent, applyContextWindow, createStrandClient, generateId, parseSSEStream, processWireEvent, tool, toolToJsonSchema, validateMessages, withRetry };
@@ -0,0 +1,184 @@
1
+ import { ZodSchema } from 'zod';
2
+
3
+ type StreamingStatus = 'idle' | 'submitting' | 'streaming' | 'done' | 'error';
4
+ type ToolCallStatus = 'idle' | 'pending' | 'running' | 'done' | 'failed';
5
+ type AgentStatus = 'idle' | 'running' | 'paused' | 'done' | 'failed';
6
+ interface ToolCall {
7
+ id: string;
8
+ name: string;
9
+ input: Record<string, unknown>;
10
+ output: unknown | null;
11
+ status: ToolCallStatus;
12
+ error: Error | null;
13
+ }
14
+ interface Message {
15
+ id: string;
16
+ role: 'user' | 'assistant';
17
+ content: string;
18
+ toolCalls?: ToolCall[];
19
+ createdAt: Date;
20
+ }
21
+ interface TokenUsage {
22
+ input: number;
23
+ output: number;
24
+ total: number;
25
+ }
26
+ interface Session {
27
+ id: string;
28
+ messages: Message[];
29
+ status: StreamingStatus;
30
+ tokenUsage: TokenUsage;
31
+ error: Error | null;
32
+ }
33
+ interface ToolDefinition {
34
+ name: string;
35
+ description: string;
36
+ parameters: ZodSchema;
37
+ }
38
+ type RetryableError = 'rate_limit' | 'server_error' | 'timeout';
39
+ interface RetryConfig {
40
+ maxAttempts?: number;
41
+ backoff?: 'exponential' | 'linear' | 'none';
42
+ retryOn?: RetryableError[];
43
+ }
44
+ type ContextWindowStrategy = 'truncate-oldest' | 'sliding-window' | 'none';
45
+ interface ContextWindowConfig {
46
+ strategy?: ContextWindowStrategy;
47
+ maxTokens?: number;
48
+ }
49
+ interface StrandClientConfig {
50
+ baseUrl: string;
51
+ retry?: RetryConfig;
52
+ contextWindow?: ContextWindowConfig;
53
+ }
54
+ interface SendOptions {
55
+ context?: Record<string, unknown>;
56
+ signal?: AbortSignal;
57
+ }
58
+ interface StrandClient {
59
+ readonly config: StrandClientConfig;
60
+ send(messages: Message[], options?: SendOptions): AsyncGenerator<WireEvent>;
61
+ }
62
+ interface ToolCallState<TInput = unknown, TOutput = unknown> {
63
+ id: string | null;
64
+ toolName: string | null;
65
+ status: ToolCallStatus;
66
+ input: TInput | null;
67
+ output: TOutput | null;
68
+ error: Error | null;
69
+ }
70
+ interface AgentStep {
71
+ index: number;
72
+ description: string;
73
+ toolCalls: ToolCall[];
74
+ output: string | null;
75
+ status: 'running' | 'done' | 'failed';
76
+ }
77
+ type WireEvent = {
78
+ type: 'strand:start';
79
+ sessionId: string;
80
+ requestId: string;
81
+ } | {
82
+ type: 'strand:text-delta';
83
+ delta: string;
84
+ } | {
85
+ type: 'strand:tool-start';
86
+ toolCallId: string;
87
+ toolName: string;
88
+ } | {
89
+ type: 'strand:tool-input-delta';
90
+ toolCallId: string;
91
+ delta: string;
92
+ } | {
93
+ type: 'strand:tool-input-done';
94
+ toolCallId: string;
95
+ input: Record<string, unknown>;
96
+ } | {
97
+ type: 'strand:tool-result';
98
+ toolCallId: string;
99
+ result: unknown;
100
+ } | {
101
+ type: 'strand:tool-error';
102
+ toolCallId: string;
103
+ error: string;
104
+ } | {
105
+ type: 'strand:done';
106
+ usage: TokenUsage;
107
+ } | {
108
+ type: 'strand:error';
109
+ code: string;
110
+ message: string;
111
+ retryAfter?: number;
112
+ };
113
+
114
+ declare function createStrandClient(config: StrandClientConfig): StrandClient;
115
+
116
+ declare function tool(definition: ToolDefinition): ToolDefinition;
117
+
118
+ declare class SessionStateMachine {
119
+ private _session;
120
+ private _listeners;
121
+ private _resetTimer;
122
+ constructor(sessionId?: string);
123
+ get session(): Readonly<Session>;
124
+ subscribe(listener: (session: Session) => void): () => void;
125
+ private emit;
126
+ transition(status: StreamingStatus, error?: Error): void;
127
+ addUserMessage(content: string): void;
128
+ appendTextDelta(delta: string): void;
129
+ beginToolCall(toolCallId: string, toolName: string): void;
130
+ updateToolCall(toolCallId: string, update: Partial<Omit<ToolCall, 'id' | 'name'>>): void;
131
+ updateTokenUsage(usage: TokenUsage): void;
132
+ clear(): void;
133
+ }
134
+ declare function generateId(): string;
135
+
136
+ declare function applyContextWindow(messages: Message[], config: ContextWindowConfig): Message[];
137
+
138
+ declare class StrandError extends Error {
139
+ readonly status: number;
140
+ readonly code: string;
141
+ constructor(status: number, message: string, code: string);
142
+ }
143
+ declare function withRetry<T>(fn: () => Promise<T>, config: RetryConfig): Promise<T>;
144
+
145
+ declare function parseSSEStream(body: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncGenerator<WireEvent>;
146
+
147
+ declare class ToolCallStore {
148
+ private _idToName;
149
+ private _states;
150
+ private _listeners;
151
+ getState<TInput = unknown, TOutput = unknown>(toolName: string): ToolCallState<TInput, TOutput>;
152
+ subscribe(toolName: string, listener: (state: ToolCallState) => void): () => void;
153
+ onToolStart(toolCallId: string, toolName: string): void;
154
+ onToolInputDone(toolCallId: string, input: Record<string, unknown>): void;
155
+ onToolResult(toolCallId: string, result: unknown): void;
156
+ onToolError(toolCallId: string, error: Error): void;
157
+ resetAll(): void;
158
+ private _set;
159
+ }
160
+
161
+ declare function processWireEvent(event: WireEvent, session: SessionStateMachine, toolStore: ToolCallStore): void;
162
+
163
+ interface JsonSchema {
164
+ type: string;
165
+ properties?: Record<string, unknown>;
166
+ required?: string[];
167
+ [key: string]: unknown;
168
+ }
169
+ declare function toolToJsonSchema(tool: ToolDefinition): JsonSchema;
170
+
171
+ interface RequestValidationConfig {
172
+ maxMessages?: number;
173
+ maxMessageLength?: number;
174
+ }
175
+ type ValidationResult = {
176
+ ok: true;
177
+ } | {
178
+ ok: false;
179
+ status: number;
180
+ error: string;
181
+ };
182
+ declare function validateMessages(messages: unknown, config?: RequestValidationConfig): ValidationResult;
183
+
184
+ export { type AgentStatus, type AgentStep, type ContextWindowConfig, type ContextWindowStrategy, type JsonSchema, type Message, type RequestValidationConfig, type RetryConfig, type SendOptions, type Session, SessionStateMachine, type StrandClient, type StrandClientConfig, StrandError, type StreamingStatus, type TokenUsage, type ToolCall, type ToolCallState, type ToolCallStatus, ToolCallStore, type ToolDefinition, type ValidationResult, type WireEvent, applyContextWindow, createStrandClient, generateId, parseSSEStream, processWireEvent, tool, toolToJsonSchema, validateMessages, withRetry };