freesail 0.0.1 → 0.1.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 (54) hide show
  1. package/README.md +190 -5
  2. package/docs/A2UX_Protocol.md +183 -0
  3. package/docs/Agents.md +218 -0
  4. package/docs/Architecture.md +285 -0
  5. package/docs/CatalogReference.md +377 -0
  6. package/docs/GettingStarted.md +230 -0
  7. package/examples/demo/package.json +21 -0
  8. package/examples/demo/public/index.html +381 -0
  9. package/examples/demo/server.js +253 -0
  10. package/package.json +38 -5
  11. package/packages/core/package.json +48 -0
  12. package/packages/core/src/functions.ts +403 -0
  13. package/packages/core/src/index.ts +214 -0
  14. package/packages/core/src/parser.ts +270 -0
  15. package/packages/core/src/protocol.ts +254 -0
  16. package/packages/core/src/store.ts +452 -0
  17. package/packages/core/src/transport.ts +439 -0
  18. package/packages/core/src/types.ts +209 -0
  19. package/packages/core/tsconfig.json +10 -0
  20. package/packages/lit-ui/package.json +44 -0
  21. package/packages/lit-ui/src/catalogs/standard/catalog.json +405 -0
  22. package/packages/lit-ui/src/catalogs/standard/elements/Badge.ts +96 -0
  23. package/packages/lit-ui/src/catalogs/standard/elements/Button.ts +147 -0
  24. package/packages/lit-ui/src/catalogs/standard/elements/Card.ts +78 -0
  25. package/packages/lit-ui/src/catalogs/standard/elements/Checkbox.ts +94 -0
  26. package/packages/lit-ui/src/catalogs/standard/elements/Column.ts +66 -0
  27. package/packages/lit-ui/src/catalogs/standard/elements/Divider.ts +59 -0
  28. package/packages/lit-ui/src/catalogs/standard/elements/Image.ts +54 -0
  29. package/packages/lit-ui/src/catalogs/standard/elements/Input.ts +125 -0
  30. package/packages/lit-ui/src/catalogs/standard/elements/Progress.ts +79 -0
  31. package/packages/lit-ui/src/catalogs/standard/elements/Row.ts +68 -0
  32. package/packages/lit-ui/src/catalogs/standard/elements/Select.ts +110 -0
  33. package/packages/lit-ui/src/catalogs/standard/elements/Spacer.ts +37 -0
  34. package/packages/lit-ui/src/catalogs/standard/elements/Spinner.ts +76 -0
  35. package/packages/lit-ui/src/catalogs/standard/elements/Text.ts +86 -0
  36. package/packages/lit-ui/src/catalogs/standard/elements/index.ts +18 -0
  37. package/packages/lit-ui/src/catalogs/standard/index.ts +17 -0
  38. package/packages/lit-ui/src/index.ts +84 -0
  39. package/packages/lit-ui/src/renderer.ts +211 -0
  40. package/packages/lit-ui/src/types.ts +49 -0
  41. package/packages/lit-ui/src/utils/define-props.ts +157 -0
  42. package/packages/lit-ui/src/utils/index.ts +2 -0
  43. package/packages/lit-ui/src/utils/registry.ts +139 -0
  44. package/packages/lit-ui/tsconfig.json +11 -0
  45. package/packages/server/package.json +61 -0
  46. package/packages/server/src/adapters/index.ts +5 -0
  47. package/packages/server/src/adapters/langchain.ts +175 -0
  48. package/packages/server/src/adapters/openai.ts +209 -0
  49. package/packages/server/src/catalog-loader.ts +311 -0
  50. package/packages/server/src/index.ts +142 -0
  51. package/packages/server/src/stream.ts +329 -0
  52. package/packages/server/tsconfig.json +11 -0
  53. package/tsconfig.base.json +23 -0
  54. package/index.js +0 -3
package/README.md CHANGED
@@ -1,7 +1,192 @@
1
- # Freesail
1
+ # Freesail SDK
2
2
 
3
- The official Freesail protocol implementation.
3
+ **Industrial Strength Generative UI SDK** - Decouple AI Agents from the Frontend
4
4
 
5
- ## Status
6
- This project is currently under active development by Thendral AI.
7
- Please follow updates at [thendral.ai](https://thendral.ai).
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ ## Overview
8
+
9
+ Freesail is a **Headless**, **Direct-Transport**, and **Schema-Driven** SDK that enables ANY AI Agent framework to drive ANY UI through a standardized JSON protocol (A2UX) over HTTP SSE.
10
+
11
+ ```
12
+ ┌─────────────────┐ SSE Stream ┌─────────────────┐
13
+ │ AI Agent │ ───────────────► │ Frontend │
14
+ │ (Any LLM) │ A2UX Protocol │ (Any Framework)│
15
+ │ │ ◄─────────────── │ │
16
+ │ LangChain │ User Actions │ React/Lit/Vue │
17
+ │ OpenAI │ │ Angular/Vanilla│
18
+ │ LlamaIndex │ │ │
19
+ └─────────────────┘ └─────────────────┘
20
+ ```
21
+
22
+ ## Key Features
23
+
24
+ - 🔌 **Framework Agnostic** - Works with LangChain, LlamaIndex, OpenAI, or vanilla Node.js
25
+ - 🎨 **UI Agnostic** - Drive React, Angular, Vue, or Web Components
26
+ - 📡 **SSE Transport** - Resilient streaming with auto-reconnect and offline support
27
+ - 📋 **Schema-Driven** - Component catalogs are the single source of truth
28
+ - 🔒 **Enterprise Ready** - Built for self-hosted / air-gapped deployments
29
+
30
+ ## Packages
31
+
32
+ | Package | Description |
33
+ |---------|-------------|
34
+ | `@freesail/core` | A2UX Protocol parser, transport layer, state management |
35
+ | `@freesail/lit-ui` | Lit-based Web Components with standard catalog |
36
+ | `@freesail/server` | SSE stream engine and framework adapters |
37
+
38
+ ## Quick Start
39
+
40
+ ### Installation
41
+
42
+ ```bash
43
+ npm install @freesail/core @freesail/server
44
+ ```
45
+
46
+ ### Server Setup (Express)
47
+
48
+ ```typescript
49
+ import express from 'express';
50
+ import { createSSEHandler, CatalogToToolConverter } from '@freesail/server';
51
+
52
+ const app = express();
53
+
54
+ app.get('/api/stream', createSSEHandler(async (stream) => {
55
+ // Create a UI surface
56
+ stream.createSurface('main', 'standard_catalog_v1');
57
+
58
+ // Send components
59
+ stream.send({
60
+ updateComponents: {
61
+ surfaceId: 'main',
62
+ components: [
63
+ { id: 'root', component: 'Column', children: ['greeting'] },
64
+ { id: 'greeting', component: 'Text', text: 'Hello from AI!' }
65
+ ]
66
+ }
67
+ });
68
+ }));
69
+
70
+ app.listen(3000);
71
+ ```
72
+
73
+ ### Client Setup
74
+
75
+ ```html
76
+ <script type="module">
77
+ import { FreesailClient } from '@freesail/core';
78
+ import { registerStandardCatalog, createSurfaceRenderer } from '@freesail/lit-ui';
79
+
80
+ // Register UI components
81
+ registerStandardCatalog();
82
+
83
+ // Connect to server
84
+ const client = new FreesailClient({ url: '/api/stream' });
85
+ await client.connect();
86
+
87
+ // Create renderer
88
+ const renderer = createSurfaceRenderer({
89
+ container: document.getElementById('app'),
90
+ surfaceId: 'main',
91
+ store: client.getStore()
92
+ });
93
+ </script>
94
+ ```
95
+
96
+ ## The A2UX Protocol
97
+
98
+ Freesail uses the **A2UX Protocol**, an extension of Google's A2UI specification for agent-to-UI communication.
99
+
100
+ ### Server → Client Messages
101
+
102
+ | Message | Purpose |
103
+ |---------|---------|
104
+ | `createSurface` | Initialize a UI container with a component catalog |
105
+ | `updateComponents` | Stream component structure (flat adjacency list) |
106
+ | `updateDataModel` | Push data changes without re-rendering |
107
+ | `deleteSurface` | Remove a surface |
108
+ | `watchSurface` | Request periodic state updates |
109
+
110
+ ### Client → Server Messages
111
+
112
+ | Message | Purpose |
113
+ |---------|---------|
114
+ | `userAction` | Report user interactions with full context |
115
+ | `watchSurfaceResponse` | Periodic state snapshot |
116
+
117
+ ## Architecture
118
+
119
+ ```
120
+ /freesail
121
+ ├── /packages
122
+ │ ├── /core # Protocol, Transport, State
123
+ │ ├── /lit-ui # Web Components
124
+ │ └── /server # SSE Engine, Adapters
125
+ ├── /examples
126
+ │ └── /demo # Demo application
127
+ └── /docs # Documentation
128
+ ```
129
+
130
+ ## Framework Adapters
131
+
132
+ ### LangChain
133
+
134
+ ```typescript
135
+ import { z } from 'zod';
136
+ import { DynamicStructuredTool } from 'langchain/tools';
137
+ import { createLangChainTools } from '@freesail/server/adapters/langchain';
138
+
139
+ const toolConfigs = createLangChainTools(converter, catalogId, { stream, catalogId }, z);
140
+ const tools = toolConfigs.map(config => new DynamicStructuredTool(config));
141
+ ```
142
+
143
+ ### OpenAI
144
+
145
+ ```typescript
146
+ import { createOpenAIAdapter, processOpenAIResponse } from '@freesail/server/adapters/openai';
147
+
148
+ const adapter = createOpenAIAdapter({ stream, catalogId, converter });
149
+
150
+ const response = await openai.chat.completions.create({
151
+ model: 'gpt-4',
152
+ messages,
153
+ tools: adapter.getTools(),
154
+ });
155
+
156
+ const results = processOpenAIResponse(response, adapter);
157
+ ```
158
+
159
+ ## Standard Catalog Components
160
+
161
+ The standard catalog includes cross-compatible A2UI components:
162
+
163
+ - **Layout**: `Column`, `Row`, `Card`, `Spacer`, `Divider`
164
+ - **Text**: `Text` (with variants h1-h6, body, caption)
165
+ - **Input**: `Input`, `Select`, `Checkbox`
166
+ - **Feedback**: `Button`, `Badge`, `Spinner`, `Progress`
167
+ - **Media**: `Image`
168
+
169
+ ## Development
170
+
171
+ ```bash
172
+ # Install dependencies
173
+ npm install
174
+
175
+ # Build all packages
176
+ npm run build
177
+
178
+ # Run demo
179
+ cd examples/demo
180
+ npm run dev
181
+ ```
182
+
183
+ ## Design Principles
184
+
185
+ 1. **The Contract is the Code** - No duplicated logic between schema and implementation
186
+ 2. **Schema-First** - Component catalogs are the single source of truth
187
+ 3. **Orchestrator Agnostic** - Core logic never imports framework-specific code
188
+ 4. **Stateless Agent** - Client sends full context with every action
189
+
190
+ ## License
191
+
192
+ MIT © Freesail Team
@@ -0,0 +1,183 @@
1
+ The A2UX protocol is a standardized JSON schema that decouples AI agents from the frontend code, allowing them to render interfaces remotely. It extends the Google A2UI protocol.
2
+
3
+ A2UX supports the below Server to Client Messages:
4
+
5
+ 1. createSurface: Initializes a UI container and loads a specific "Catalog" (vocabulary of allowed components). Sending a createSurface message allows the UI to start rendering the UI container so the user knows that the LLM is working behind the scenes to render the UI.
6
+
7
+ Properties:
8
+
9
+ - surfaceId (string, required): The unique identifier for the UI surface to
10
+ be rendered. It should be unique to the session.
11
+
12
+ - catalogId (string, required): A string that uniquely identifies the component catalog used for this surface.
13
+
14
+ Example:
15
+ ```json
16
+ {
17
+ "createSurface": {
18
+ "surfaceId": "user_profile_card",
19
+ "catalogId":"https://thendral.ai/standard_catalog_v1.json"
20
+ }
21
+ }
22
+ ```
23
+ 2. updateComponents: Streams the structural definition of the UI to add or update in a surface.
24
+
25
+ Properties:
26
+
27
+ - surfaceId (string, required): The unique identifier for the UI surface to be updated.
28
+
29
+ - components (array, required): A list of component objects. The components are provided as a flat list, and their relationships are defined by ID references in an adjacency list.
30
+
31
+ Example:
32
+ ```json
33
+ {
34
+ "updateComponents": {
35
+ "surfaceId": "user_profile_card",
36
+ "components": [
37
+ {
38
+ "id": "root",
39
+ "component": "Column",
40
+ "children": ["user_name", "user_title"]
41
+ },
42
+ {
43
+ "id": "user_name",
44
+ "component": "Text",
45
+ "text": "John Doe"
46
+ },
47
+ {
48
+ "id": "user_title",
49
+ "component": "Text",
50
+ "text": "Software Engineer"
51
+ }
52
+ ]
53
+ }
54
+ }
55
+ ```
56
+ 3. updateDataModel: This message is used to send or update the data that populates the UI components (e.g., to update temperature to 25C). It allows the server to change the UI's content without resending the entire component structure.
57
+
58
+ Properties:
59
+
60
+ - surfaceId (string, required): The unique identifier for the UI surface this data model update applies to.
61
+
62
+ - path (string, optional): A JSON Pointer to a specific location within the data model (e.g., /user/name ). If omitted or set to / , the entire data model for the surface will be replaced.
63
+
64
+ - op (string, optional): The operation to perform on the data model. Must be 'add', 'replace' or remove'. If omitted, defaults to 'replace'.
65
+
66
+ - value (object): The data to be updated in the data model. This can be any valid JSON object. Required if op is 'add' or 'replace'. Not allowed if op is 'remove'.
67
+
68
+ Example:
69
+
70
+ ```json
71
+ {
72
+ "updateDataModel": {
73
+ "surfaceId": "user_profile_card",
74
+ "path": "/user",
75
+ "op": "replace",
76
+ "value": {
77
+ "name": "Jane Doe",
78
+ "title": "Software Engineer"
79
+ }
80
+ }
81
+ }
82
+ ```
83
+ 4. deleteSurface : This message instructs the client to remove a surface and all its associated components and data from the UI.
84
+
85
+ Properties:
86
+
87
+ - surfaceId (string, required): The unique identifier for the UI surface to be deleted.
88
+
89
+ Example:
90
+ ```json
91
+ {
92
+ "deleteSurface": {
93
+ "surfaceId": "user_profile_card"
94
+ }
95
+ }
96
+ ```
97
+ 5. watchSurface: Sometimes the agent may want to know about the current state of the surface. The watchSurface message is used to watch the surface at set intervals for a set period of time. The client will monitor the surface and send the current state of the surface to the server. This mechanism provides automatic updates to the agent without relying on user action.
98
+
99
+ Properties:
100
+
101
+ - surfaceId (string, required): The unique identifier of the UI surface where this watcher should be attached.
102
+
103
+ - interval (number, optional): Time in seconds to wait before sending a surfaceState message to the server. This avoids frequent updates. The default value is 10 seconds.
104
+
105
+ - expiresIn (number, optional): Time in seconds when the client should remove the watch. The default value is 300 seconds.
106
+
107
+ Example:
108
+
109
+ ```json
110
+ {
111
+ "watchSurface": {
112
+ "surfaceId": "user_profile_card",
113
+ "interval": 30,
114
+ "expiresIn": 100
115
+ }
116
+ }
117
+ ```
118
+
119
+ 6. unwatchSurface: Removes a watch that was previously set.
120
+
121
+ Properties:
122
+
123
+ - surfaceId (string, required): The unique identifier for the UI surface for which the watch should be stopped.
124
+
125
+ Example:
126
+
127
+ ```json
128
+ {
129
+ "unwatchSurface": {
130
+ "surfaceId": "user_profile_card"
131
+ }
132
+ }
133
+
134
+ ### Client to Server messages
135
+
136
+ 6. userAction: This message is sent from the client (UI) to the server (agent) to report a user action. It serves as the primary way the agent learns about the user's intent.
137
+
138
+ Properties:
139
+
140
+ - surfaceId (string, required): The unique identifier for the UI surface where the action originated.
141
+
142
+ - action (string, required): A semantic label identifying what happened. For standard actions, it describes the intent (e.g., submit_form, Maps_next).
143
+
144
+ - context (object, required): A JSON object that contains the data relevant to the action.
145
+
146
+ Example:
147
+
148
+ ```json
149
+ {
150
+ "userAction": {
151
+ "surfaceId": "user_profile_card",
152
+ "action": "save_profile_click",
153
+ "context": {
154
+ "name": "Jane Doe",
155
+ "title": "Senior Engineer",
156
+ "email": "jane@example.com",
157
+ "newsletter_opt_in": true
158
+ }
159
+ }
160
+ }
161
+ ```
162
+ 7. watchSurfaceResponse: This message is sent from the client (UI) to the agent to report the current state of the surface data model, in response to a watchSurface request. It serves as a way for the agent to get information about the state of the suface data automatically.
163
+
164
+ Properties:
165
+
166
+ - surfaceId (string, required): The unique identifier for the UI surface.
167
+
168
+ - data (object, required): A JSON object that contains the data relevant to the action.
169
+
170
+ Example:
171
+
172
+ ```json
173
+ {
174
+ "watchSurfaceResponse": {
175
+ "surfaceId": "user_profile_card",
176
+ "data": {
177
+ "/user/name": "Jack Ryan",
178
+ "/user/email": "jack@example.com",
179
+ "/user/postcode": "W1T 2EW"
180
+ }
181
+ }
182
+ }
183
+ ```
package/docs/Agents.md ADDED
@@ -0,0 +1,218 @@
1
+ # **Agent Configuration: Freesail Architect**
2
+
3
+ ## **1\. Project Introduction**
4
+
5
+ **Freesail** is an "Industrial Strength" Generative UI SDK designed to decouple AI Agents from the Frontend.
6
+
7
+ Unlike frameworks that rely on heavy React wrappers or standardized agent protocols, Freesail is **Headless**, **Direct-Transport**, and **Schema-Driven**.
8
+
9
+ * **The Goal:** Allow **ANY Agent Framework** (LangChain, LlamaIndex, Vercel AI, or Vanilla Node.js) to drive any UI (Legacy, Angular, React, Vanilla) using a standardized stream of JSON events over HTTP SSE.
10
+ * **The "Moat":** We provide the resilient streaming runtime, state management, and protocol handling that enterprise clients require for self-hosted / air-gapped applications.
11
+
12
+ ## **2\. Role & Vision**
13
+
14
+ You are the **Lead Architect for Freesail**.
15
+
16
+ * **Philosophy:** "The Contract is the Code." We do not duplicate logic. If it exists in the JSON Schema, it exists in the App.
17
+ * **Mission:** Build a system where the **Component Catalog** (standard\_catalog.json \+ custom catalogs) is the single source of truth for both the **AI's System Prompt** (Server) and the **UI's Properties** (Client).
18
+ * **Strategy:** Prioritize "Below the Radar" tech (Web Components, SSE, Node.js) to ensure maximum compatibility and zero vendor lock-in.
19
+
20
+ ## **3\. Tech Stack & Constraints**
21
+
22
+ | Layer | Choice | Rationale |
23
+ | :---- | :---- | :---- |
24
+ | **Frontend** | **Lit** (Web Components) | Native browser support, Shadow DOM isolation, works everywhere. |
25
+ | **Backend** | **Node.js** (Express) | Custom runtime for managing SSE streams and Agent state. |
26
+ | **Transport** | **Freesail Stream** | Raw Server-Sent Events (SSE) carrying A2UX payloads. |
27
+ | **Orchestrator** | **Agnostic** (Pluggable) | Supports LangChain, LlamaIndex, or raw OpenAI via **Adapters**. |
28
+ | **Protocol** | **A2UX** (A2UI-Extended) | Strict JSON schema for UI definition (createSurface, updateComponents) and Interaction (userAction). |
29
+ | **Std Lib** | **A2UI Standard** | We implement the standard Row, Column, Text, Input components to ensure cross-compatibility. |
30
+
31
+ ### **⛔ Strict Anti-Patterns**
32
+
33
+ * **NO Framework Lock-in:** Core logic must never import langchain or llamaindex directly. Use dependency injection or adapters.
34
+ * **NO Duplicate Property Definitions:** Do not define properties in TypeScript if they are already in the loaded catalog. Use the defineProps() helper.
35
+ * **NO Raw HTML Injection:** Never use innerHTML. All rendering must go through the Registry.
36
+ * **NO "Delta" State Patching on Client:** The client blindly accepts state replacements. The Agent is the source of truth.
37
+
38
+ ## **4\. Architecture Standards (The "Golden Stack")**
39
+
40
+ 1. **The Source of Truth (Catalogs):** Catalogs are self-contained modules located in packages/lit-ui/src/catalogs/\<name\>/. Each folder contains the JSON definition and the TypeScript elements implementation.
41
+ 2. **The Context Injector (Server):** Reads the **selected** Catalog(s) at runtime and converts them into **Standard JSON Schema Tools** compatible with any LLM.
42
+ 3. **The Renderer (Lit):** Dynamically loads component definitions from the catalogId specified in createSurface.
43
+ 4. **The Stream Engine:** A dedicated StreamStore class that manages SSE connections (res.write) and handles backpressure.
44
+
45
+ ## **5\. Implementation Rules**
46
+
47
+ ### **Rule \#1: Schema-First Components (The "Dry" Law)**
48
+
49
+ * **Pattern:** Lit components must hydrate their properties directly from the Catalog JSON located **in the same folder**.
50
+ * **Requirement:** Never manually write @property decorators for data fields. Use the defineProps utility.
51
+
52
+ // GOOD: Colocated Import
53
+ import catalog from '../catalog.json';
54
+
55
+ export class FreesailText extends LitElement {
56
+ // Auto-generates properties based on the JSON definition
57
+ static properties \= defineProps(catalog, 'Text');
58
+ render() { return html\`\<span\>${this.text}\</span\>\`; }
59
+ }
60
+
61
+ ### **Rule \#2: Multi-Catalog Support**
62
+
63
+ * **Requirement:** The system must support loading different catalogs for different surfaces.
64
+ * **Mechanism:**
65
+ * **Server:** The Agent decides which catalog to use (e.g., finance\_v1) and sends it in createSurface(catalogId="finance\_v1").
66
+ * **Client:** The Registry maps catalogId to a specific bundle of Web Components.
67
+ * **Validation:** The System Prompt generator must only include components from the *active* catalog to prevent hallucinations.
68
+
69
+ ### **Rule \#3: Orchestrator Agnosticism (The Adapter Pattern)**
70
+
71
+ * **Pattern:** Core logic is pure TypeScript. Framework specifics are Adapters.
72
+ * **Requirement:**
73
+ * **Core:** CatalogToToolConverter takes a catalog and outputs a standard JSON Schema Tool definition ({ name: "render\_ui", parameters: ... }).
74
+ * **Adapters:** Create thin wrappers in server/src/adapters/ for specific frameworks:
75
+ * LangChainAdapter: Converts the standard tool to a DynamicStructuredTool.
76
+ * LlamaIndexAdapter: Converts the standard tool to a FunctionTool.
77
+ * OpenAIAdapter: Converts the standard tool to an OpenAI API tools array.
78
+
79
+ ### **Rule \#4: Client-Side Logic (The Function Registry)**
80
+
81
+ * **Pattern:** Do not force the Agent to format data. Use A2UI Standard Functions for **Data Logic**.
82
+ * **Requirement:** Implement the standard function list (formatCurrency, pluralize, regex) in @freesail/core.
83
+ * **Execution:** The Parser must detect template strings ${...} and execute the logic locally against the Data Model.
84
+
85
+ ### **Rule \#5: The "Full Context" Action Protocol**
86
+
87
+ * **Pattern:** Stateless Communication.
88
+ * **Requirement:** When a user action occurs, the client must gather the **entire state object** for that surfaceId and send it to the agent via the context property.
89
+ * **Why:** The Agent is stateless. It relies on the context payload to know the current state.
90
+
91
+ ### **Rule \#6: The "Build & Validate" Loop (CRITICAL)**
92
+
93
+ * **Pattern:** Never assume code works. Prove it.
94
+ * **Requirement:** After generating any code (Client or Server):
95
+ 1. **Run Build:** Execute npm run build in the relevant package.
96
+ 2. **Fix Errors:** If the build fails, read the error log, fix the code, and retry.
97
+ 3. **Validate Schema:** Ensure generated JSON payloads match standard\_catalog.json exactly.
98
+
99
+ ### **Rule \#7: Transport Resilience (The "Iron Pipe")**
100
+
101
+ * **Pattern:** Robust Retry & Reconnect (SDK Responsibility).
102
+ * **Requirement:** The @freesail/core SDK handles network faults transparently.
103
+ * **SSE Reconnection:** Automatically reconnect the event stream with exponential backoff if the connection drops.
104
+ * **Action Queueing:** If the user performs an action (userAction) while offline, queue it in indexedDB and flush when online.
105
+ * **Idempotency:** The Server must handle duplicate userAction messages gracefully.
106
+
107
+ ## **6\. The A2UX Protocol Specification**
108
+
109
+ We use the **A2UX Protocol**, an extension of Google A2UI v0.9.
110
+
111
+ ### **Downstream (Server → Client)**
112
+
113
+ | Message | Purpose | Schema Key |
114
+ | :---- | :---- | :---- |
115
+ | **createSurface** | Init UI container & select Catalog. | createSurface |
116
+ | **updateComponents** | Stream DOM structure. | updateComponents |
117
+ | **updateDataModel** | Push state changes. | updateDataModel |
118
+ | **deleteSurface** | Cleanup/Destroy. | deleteSurface |
119
+ | **watchSurface** | Config client triggers. | watchSurface |
120
+ | **unwatchSurface** | Remove triggers. | unwatchSurface |
121
+
122
+ **Schema: watchSurface**
123
+
124
+ Configures the client to monitor the surface and report state automatically.
125
+
126
+ {
127
+ "watchSurface": {
128
+ "surfaceId": "user\_profile\_card",
129
+ "interval": 30, // seconds
130
+ "expiresIn": 100 // seconds
131
+ }
132
+ }
133
+
134
+ ### **Upstream (Client → Server)**
135
+
136
+ | Message | Purpose | Schema Key |
137
+ | :---- | :---- | :---- |
138
+ | **userAction** | Explicit User Intent. | userAction |
139
+ | **watchSurfaceResponse** | Passive State Event. | watchSurfaceResponse |
140
+
141
+ **Schema: userAction**
142
+
143
+ Note: usage of context to carry the data model.
144
+
145
+ {
146
+ "userAction": {
147
+ "surfaceId": "user\_profile\_card",
148
+ "action": "save\_profile\_click",
149
+ "context": {
150
+ "name": "Jane Doe",
151
+ "title": "Senior Engineer",
152
+ "email": "jane@example.com",
153
+ "newsletter\_opt\_in": true
154
+ }
155
+ }
156
+ }
157
+
158
+ **Schema: watchSurfaceResponse**
159
+
160
+ Note: usage of data to carry the state.
161
+
162
+ {
163
+ "watchSurfaceResponse": {
164
+ "surfaceId": "user\_profile\_card",
165
+ "data": {
166
+ "/user/name": "Jack Ryan",
167
+ "/user/email": "jack@example.com",
168
+ "/user/postcode": "W1T 2EW"
169
+ }
170
+ }
171
+ }
172
+
173
+ ## **7\. Directory Structure**
174
+
175
+ Maintain this structure to enforce the **Vertical Slice** pattern where catalogs and their elements are colocated, and the Server logic is decoupled from specific frameworks.
176
+
177
+ /freesail
178
+ ├── /packages
179
+ │ ├── /core \# @freesail/core (The Brain \- Protocol Parser & Store)
180
+ │ │ ├── src/
181
+ │ │ │ ├── protocol.ts \# A2UX Listener
182
+ │ │ │ ├── parser.ts \# JSON Stream Parser
183
+ │ │ │ ├── transport.ts \# ⭐️ Network Retry & Queue Logic
184
+ │ │ │ ├── functions.ts \# Standard Lib (formatCurrency, regex)
185
+ │ │ │ └── store.ts \# Reactive Data Model
186
+ │ │
187
+ │ ├── /lit-ui \# @freesail/lit (The Face \- Web Components)
188
+ │ │ ├── src/
189
+ │ │ │ ├── utils/ \# defineProps helper
190
+ │ │ │ ├── catalogs/ \# ⭐️ COLOCATED CATALOGS
191
+ │ │ │ │ ├── standard/
192
+ │ │ │ │ │ ├── catalog.json \# Standard A2UI Definition
193
+ │ │ │ │ │ └── elements/ \# Implementation
194
+ │ │ │ │ │ ├── Text.ts
195
+ │ │ │ │ │ └── Button.ts
196
+ │ │ │ │ │
197
+ │ │ │ │ └── finance/
198
+ │ │ │ │ ├── catalog.json \# Finance Domain Definition
199
+ │ │ │ │ └── elements/ \# Implementation
200
+ │ │ │ │ ├── Ticker.ts
201
+ │ │ │ │ └── Chart.ts
202
+ │ │
203
+ │ └── /server \# @freesail/server (The Orchestrator)
204
+ │ ├── src/
205
+ │ │ ├── stream.ts \# SSE Implementation (res.write)
206
+ │ │ ├── catalog-loader.ts \# Generic JSON Schema Generator
207
+ │ │ └── adapters/ \# ⭐️ FRAMEWORK ADAPTERS
208
+ │ │ ├── langchain.ts \# LangChain Tool Wrapper
209
+ │ │ ├── llamaindex.ts \# LlamaIndex Tool Wrapper
210
+ │ │ └── openai.ts \# OpenAI Function Definition
211
+
212
+ ├── /docs \# ⭐️ DEVELOPER GUIDES
213
+ │ ├── GettingStarted.md
214
+ │ ├── Architecture.md
215
+ │ └── CatalogReference.md
216
+
217
+ └── /examples
218
+ └── /langchain-demo \# Proof of "Any-Stack" with LangChain