@abloatai/ablo 0.3.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 (278) hide show
  1. package/CHANGELOG.md +208 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +12 -0
  4. package/README.md +230 -0
  5. package/dist/BaseSyncedStore.d.ts +709 -0
  6. package/dist/BaseSyncedStore.js +1843 -0
  7. package/dist/Database.d.ts +344 -0
  8. package/dist/Database.js +1259 -0
  9. package/dist/LazyReferenceCollection.d.ts +181 -0
  10. package/dist/LazyReferenceCollection.js +460 -0
  11. package/dist/Model.d.ts +339 -0
  12. package/dist/Model.js +715 -0
  13. package/dist/ModelRegistry.d.ts +200 -0
  14. package/dist/ModelRegistry.js +535 -0
  15. package/dist/NetworkMonitor.d.ts +27 -0
  16. package/dist/NetworkMonitor.js +73 -0
  17. package/dist/ObjectPool.d.ts +202 -0
  18. package/dist/ObjectPool.js +1106 -0
  19. package/dist/SyncClient.d.ts +489 -0
  20. package/dist/SyncClient.js +1555 -0
  21. package/dist/SyncEngineContext.d.ts +46 -0
  22. package/dist/SyncEngineContext.js +74 -0
  23. package/dist/adapters/alwaysOnline.d.ts +16 -0
  24. package/dist/adapters/alwaysOnline.js +19 -0
  25. package/dist/adapters/inMemoryStorage.d.ts +30 -0
  26. package/dist/adapters/inMemoryStorage.js +94 -0
  27. package/dist/agent/Agent.d.ts +358 -0
  28. package/dist/agent/Agent.js +500 -0
  29. package/dist/agent/index.d.ts +115 -0
  30. package/dist/agent/index.js +128 -0
  31. package/dist/agent/session.d.ts +90 -0
  32. package/dist/agent/session.js +156 -0
  33. package/dist/agent/types.d.ts +73 -0
  34. package/dist/agent/types.js +10 -0
  35. package/dist/ai-sdk/coordination-context.d.ts +51 -0
  36. package/dist/ai-sdk/coordination-context.js +107 -0
  37. package/dist/ai-sdk/index.d.ts +68 -0
  38. package/dist/ai-sdk/index.js +68 -0
  39. package/dist/ai-sdk/intent-broadcast.d.ts +77 -0
  40. package/dist/ai-sdk/intent-broadcast.js +72 -0
  41. package/dist/ai-sdk/wrap.d.ts +67 -0
  42. package/dist/ai-sdk/wrap.js +45 -0
  43. package/dist/api/index.d.ts +10 -0
  44. package/dist/api/index.js +9 -0
  45. package/dist/auth/index.d.ts +137 -0
  46. package/dist/auth/index.js +246 -0
  47. package/dist/client/Ablo.d.ts +835 -0
  48. package/dist/client/Ablo.js +1440 -0
  49. package/dist/client/ApiClient.d.ts +200 -0
  50. package/dist/client/ApiClient.js +659 -0
  51. package/dist/client/auth.d.ts +79 -0
  52. package/dist/client/auth.js +81 -0
  53. package/dist/client/createInternalComponents.d.ts +44 -0
  54. package/dist/client/createInternalComponents.js +88 -0
  55. package/dist/client/createModelProxy.d.ts +152 -0
  56. package/dist/client/createModelProxy.js +199 -0
  57. package/dist/client/identity.d.ts +63 -0
  58. package/dist/client/identity.js +156 -0
  59. package/dist/client/index.d.ts +36 -0
  60. package/dist/client/index.js +33 -0
  61. package/dist/client/persistence.d.ts +7 -0
  62. package/dist/client/persistence.js +11 -0
  63. package/dist/client/validateAbloOptions.d.ts +42 -0
  64. package/dist/client/validateAbloOptions.js +43 -0
  65. package/dist/config/index.d.ts +10 -0
  66. package/dist/config/index.js +12 -0
  67. package/dist/context.d.ts +27 -0
  68. package/dist/context.js +58 -0
  69. package/dist/core/DatabaseManager.d.ts +108 -0
  70. package/dist/core/DatabaseManager.js +361 -0
  71. package/dist/core/QueryProcessor.d.ts +77 -0
  72. package/dist/core/QueryProcessor.js +262 -0
  73. package/dist/core/QueryView.d.ts +64 -0
  74. package/dist/core/QueryView.js +219 -0
  75. package/dist/core/StoreManager.d.ts +131 -0
  76. package/dist/core/StoreManager.js +334 -0
  77. package/dist/core/ViewRegistry.d.ts +20 -0
  78. package/dist/core/ViewRegistry.js +55 -0
  79. package/dist/core/index.d.ts +34 -0
  80. package/dist/core/index.js +59 -0
  81. package/dist/core/openIDBWithTimeout.d.ts +27 -0
  82. package/dist/core/openIDBWithTimeout.js +63 -0
  83. package/dist/core/query-utils.d.ts +37 -0
  84. package/dist/core/query-utils.js +60 -0
  85. package/dist/errors.d.ts +235 -0
  86. package/dist/errors.js +243 -0
  87. package/dist/index.d.ts +41 -0
  88. package/dist/index.js +82 -0
  89. package/dist/interfaces/headless.d.ts +95 -0
  90. package/dist/interfaces/headless.js +41 -0
  91. package/dist/interfaces/index.d.ts +321 -0
  92. package/dist/interfaces/index.js +8 -0
  93. package/dist/mutators/RecordingTransaction.d.ts +36 -0
  94. package/dist/mutators/RecordingTransaction.js +216 -0
  95. package/dist/mutators/Transaction.d.ts +48 -0
  96. package/dist/mutators/Transaction.js +64 -0
  97. package/dist/mutators/UndoManager.d.ts +114 -0
  98. package/dist/mutators/UndoManager.js +143 -0
  99. package/dist/mutators/defineMutators.d.ts +55 -0
  100. package/dist/mutators/defineMutators.js +28 -0
  101. package/dist/policy/index.d.ts +19 -0
  102. package/dist/policy/index.js +18 -0
  103. package/dist/policy/types.d.ts +74 -0
  104. package/dist/policy/types.js +17 -0
  105. package/dist/principal.d.ts +44 -0
  106. package/dist/principal.js +49 -0
  107. package/dist/query/client.d.ts +43 -0
  108. package/dist/query/client.js +84 -0
  109. package/dist/query/index.d.ts +6 -0
  110. package/dist/query/index.js +5 -0
  111. package/dist/query/types.d.ts +143 -0
  112. package/dist/query/types.js +36 -0
  113. package/dist/react/AbloProvider.d.ts +205 -0
  114. package/dist/react/AbloProvider.js +398 -0
  115. package/dist/react/ClientSideSuspense.d.ts +36 -0
  116. package/dist/react/ClientSideSuspense.js +17 -0
  117. package/dist/react/DefaultFallback.d.ts +24 -0
  118. package/dist/react/DefaultFallback.js +43 -0
  119. package/dist/react/SyncGroupProvider.d.ts +19 -0
  120. package/dist/react/SyncGroupProvider.js +44 -0
  121. package/dist/react/context.d.ts +161 -0
  122. package/dist/react/context.js +35 -0
  123. package/dist/react/index.d.ts +64 -0
  124. package/dist/react/index.js +73 -0
  125. package/dist/react/internalContext.d.ts +35 -0
  126. package/dist/react/internalContext.js +3 -0
  127. package/dist/react/useAblo.d.ts +72 -0
  128. package/dist/react/useAblo.js +63 -0
  129. package/dist/react/useCurrentUserId.d.ts +21 -0
  130. package/dist/react/useCurrentUserId.js +33 -0
  131. package/dist/react/useErrorListener.d.ts +20 -0
  132. package/dist/react/useErrorListener.js +39 -0
  133. package/dist/react/useIntent.d.ts +29 -0
  134. package/dist/react/useIntent.js +42 -0
  135. package/dist/react/useMutate.d.ts +83 -0
  136. package/dist/react/useMutate.js +122 -0
  137. package/dist/react/useMutationFailureListener.d.ts +26 -0
  138. package/dist/react/useMutationFailureListener.js +38 -0
  139. package/dist/react/useMutators.d.ts +56 -0
  140. package/dist/react/useMutators.js +66 -0
  141. package/dist/react/usePresence.d.ts +32 -0
  142. package/dist/react/usePresence.js +41 -0
  143. package/dist/react/useQuery.d.ts +123 -0
  144. package/dist/react/useQuery.js +145 -0
  145. package/dist/react/useReactive.d.ts +35 -0
  146. package/dist/react/useReactive.js +111 -0
  147. package/dist/react/useReader.d.ts +69 -0
  148. package/dist/react/useReader.js +73 -0
  149. package/dist/react/useSyncStatus.d.ts +61 -0
  150. package/dist/react/useSyncStatus.js +76 -0
  151. package/dist/react/useUndoScope.d.ts +36 -0
  152. package/dist/react/useUndoScope.js +73 -0
  153. package/dist/realtime/index.d.ts +10 -0
  154. package/dist/realtime/index.js +9 -0
  155. package/dist/schema/field.d.ts +134 -0
  156. package/dist/schema/field.js +264 -0
  157. package/dist/schema/index.d.ts +29 -0
  158. package/dist/schema/index.js +38 -0
  159. package/dist/schema/model.d.ts +326 -0
  160. package/dist/schema/model.js +89 -0
  161. package/dist/schema/queries.d.ts +203 -0
  162. package/dist/schema/queries.js +145 -0
  163. package/dist/schema/relation.d.ts +172 -0
  164. package/dist/schema/relation.js +104 -0
  165. package/dist/schema/schema.d.ts +259 -0
  166. package/dist/schema/schema.js +188 -0
  167. package/dist/schema/sugar.d.ts +129 -0
  168. package/dist/schema/sugar.js +94 -0
  169. package/dist/source/index.d.ts +423 -0
  170. package/dist/source/index.js +320 -0
  171. package/dist/source/pushQueue.d.ts +112 -0
  172. package/dist/source/pushQueue.js +249 -0
  173. package/dist/stores/ObjectStore.d.ts +103 -0
  174. package/dist/stores/ObjectStore.js +371 -0
  175. package/dist/stores/ObjectStoreContract.d.ts +39 -0
  176. package/dist/stores/ObjectStoreContract.js +1 -0
  177. package/dist/stores/SyncActionStore.d.ts +101 -0
  178. package/dist/stores/SyncActionStore.js +481 -0
  179. package/dist/sync/BootstrapHelper.d.ts +127 -0
  180. package/dist/sync/BootstrapHelper.js +434 -0
  181. package/dist/sync/ConnectionManager.d.ts +136 -0
  182. package/dist/sync/ConnectionManager.js +465 -0
  183. package/dist/sync/HydrationCoordinator.d.ts +137 -0
  184. package/dist/sync/HydrationCoordinator.js +468 -0
  185. package/dist/sync/NetworkProbe.d.ts +43 -0
  186. package/dist/sync/NetworkProbe.js +113 -0
  187. package/dist/sync/OfflineFlush.d.ts +9 -0
  188. package/dist/sync/OfflineFlush.js +22 -0
  189. package/dist/sync/OfflineTransactionStore.d.ts +37 -0
  190. package/dist/sync/OfflineTransactionStore.js +263 -0
  191. package/dist/sync/SyncWebSocket.d.ts +663 -0
  192. package/dist/sync/SyncWebSocket.js +1336 -0
  193. package/dist/sync/createIntentStream.d.ts +33 -0
  194. package/dist/sync/createIntentStream.js +243 -0
  195. package/dist/sync/createPresenceStream.d.ts +46 -0
  196. package/dist/sync/createPresenceStream.js +192 -0
  197. package/dist/sync/createSnapshot.d.ts +33 -0
  198. package/dist/sync/createSnapshot.js +124 -0
  199. package/dist/sync/participants.d.ts +114 -0
  200. package/dist/sync/participants.js +336 -0
  201. package/dist/sync/schemas.d.ts +79 -0
  202. package/dist/sync/schemas.js +78 -0
  203. package/dist/testing/fixtures/bootstrap.d.ts +45 -0
  204. package/dist/testing/fixtures/bootstrap.js +53 -0
  205. package/dist/testing/fixtures/deltas.d.ts +86 -0
  206. package/dist/testing/fixtures/deltas.js +139 -0
  207. package/dist/testing/fixtures/models.d.ts +82 -0
  208. package/dist/testing/fixtures/models.js +270 -0
  209. package/dist/testing/helpers/react-wrapper.d.ts +66 -0
  210. package/dist/testing/helpers/react-wrapper.js +64 -0
  211. package/dist/testing/helpers/sync-engine-harness.d.ts +55 -0
  212. package/dist/testing/helpers/sync-engine-harness.js +70 -0
  213. package/dist/testing/helpers/wait.d.ts +25 -0
  214. package/dist/testing/helpers/wait.js +44 -0
  215. package/dist/testing/index.d.ts +21 -0
  216. package/dist/testing/index.js +32 -0
  217. package/dist/testing/mocks/MockMutationExecutor.d.ts +65 -0
  218. package/dist/testing/mocks/MockMutationExecutor.js +139 -0
  219. package/dist/testing/mocks/MockNetworkMonitor.d.ts +20 -0
  220. package/dist/testing/mocks/MockNetworkMonitor.js +46 -0
  221. package/dist/testing/mocks/MockSyncContext.d.ts +64 -0
  222. package/dist/testing/mocks/MockSyncContext.js +100 -0
  223. package/dist/testing/mocks/MockSyncStore.d.ts +88 -0
  224. package/dist/testing/mocks/MockSyncStore.js +171 -0
  225. package/dist/testing/mocks/MockWebSocket.d.ts +66 -0
  226. package/dist/testing/mocks/MockWebSocket.js +117 -0
  227. package/dist/transactions/OptimisticEchoTracker.d.ts +82 -0
  228. package/dist/transactions/OptimisticEchoTracker.js +104 -0
  229. package/dist/transactions/TransactionQueue.d.ts +499 -0
  230. package/dist/transactions/TransactionQueue.js +1895 -0
  231. package/dist/transactions/index.d.ts +16 -0
  232. package/dist/transactions/index.js +7 -0
  233. package/dist/transactions/mutation-error-handler.d.ts +5 -0
  234. package/dist/transactions/mutation-error-handler.js +39 -0
  235. package/dist/types/global.d.ts +107 -0
  236. package/dist/types/global.js +38 -0
  237. package/dist/types/index.d.ts +241 -0
  238. package/dist/types/index.js +70 -0
  239. package/dist/types/streams.d.ts +495 -0
  240. package/dist/types/streams.js +11 -0
  241. package/dist/utils/asyncIterator.d.ts +41 -0
  242. package/dist/utils/asyncIterator.js +142 -0
  243. package/dist/utils/duration.d.ts +28 -0
  244. package/dist/utils/duration.js +47 -0
  245. package/dist/utils/mobx-setup.d.ts +42 -0
  246. package/dist/utils/mobx-setup.js +381 -0
  247. package/docs/api-keys.md +24 -0
  248. package/docs/api.md +230 -0
  249. package/docs/audit.md +81 -0
  250. package/docs/capabilities.md +163 -0
  251. package/docs/client-behavior.md +202 -0
  252. package/docs/data-sources.md +214 -0
  253. package/docs/examples/agent-human.md +84 -0
  254. package/docs/examples/ai-sdk-tool.md +92 -0
  255. package/docs/examples/existing-python-backend.md +249 -0
  256. package/docs/examples/nextjs.md +88 -0
  257. package/docs/examples/server-agent.md +86 -0
  258. package/docs/guarantees.md +148 -0
  259. package/docs/index.md +97 -0
  260. package/docs/integration-guide.md +493 -0
  261. package/docs/interaction-model.md +140 -0
  262. package/docs/mcp/claude-code.md +43 -0
  263. package/docs/mcp/cursor.md +53 -0
  264. package/docs/mcp/windsurf.md +46 -0
  265. package/docs/mcp.md +59 -0
  266. package/docs/quickstart.md +152 -0
  267. package/docs/react.md +115 -0
  268. package/docs/roadmap.md +45 -0
  269. package/examples/README.md +54 -0
  270. package/examples/data-source/README.md +102 -0
  271. package/examples/data-source/ablo-driver.ts +89 -0
  272. package/examples/data-source/customer-server.ts +208 -0
  273. package/examples/data-source/run.ts +101 -0
  274. package/examples/data-source/schema.ts +25 -0
  275. package/examples/quickstart.ts +54 -0
  276. package/examples/tsconfig.json +16 -0
  277. package/llms.txt +143 -0
  278. package/package.json +147 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * SyncEngineContext — Runtime dependency container
3
+ *
4
+ * All SDK classes receive this context at construction time.
5
+ * It bundles every injectable dependency so constructors stay clean.
6
+ */
7
+ import type { SyncLogger, SyncObservabilityProvider, SyncAnalytics, SessionErrorDetector, OnlineStatusProvider, ModelDebugLoggerContract, MutationExecutor, MutationDispatcher, SyncEngineConfig } from './interfaces/index.js';
8
+ export interface SyncEngineContext {
9
+ /** Structured logger */
10
+ logger: SyncLogger;
11
+ /** Error tracking & performance monitoring */
12
+ observability: SyncObservabilityProvider;
13
+ /** Product analytics (optional) */
14
+ analytics?: SyncAnalytics;
15
+ /** Session error detection for auth redirect decisions */
16
+ sessionErrorDetector: SessionErrorDetector;
17
+ /** Network connectivity detection */
18
+ onlineStatus: OnlineStatusProvider;
19
+ /** Model operation debug logging (optional, dev-only) */
20
+ modelDebugLogger?: ModelDebugLoggerContract;
21
+ /** Backend mutation transport (GraphQL, REST, etc.) */
22
+ mutationExecutor: MutationExecutor;
23
+ /** Offline mutation replay dispatcher */
24
+ mutationDispatcher: MutationDispatcher;
25
+ /** Application-specific sync configuration */
26
+ config: SyncEngineConfig;
27
+ }
28
+ /** No-op logger — silently discards all log calls */
29
+ export declare const noopLogger: SyncLogger;
30
+ /** No-op observability — silently discards all observability calls */
31
+ export declare const noopObservability: SyncObservabilityProvider;
32
+ /** No-op analytics — silently discards all analytics calls */
33
+ export declare const noopAnalytics: SyncAnalytics;
34
+ /** Browser-native online status provider */
35
+ export declare const browserOnlineStatus: OnlineStatusProvider;
36
+ /** Permissive session error detector — treats 401/403 as session errors */
37
+ export declare const defaultSessionErrorDetector: SessionErrorDetector;
38
+ /**
39
+ * Fallback config used when the context is read before
40
+ * `createSyncEngine(...)` has initialized it (tests, early-boot code
41
+ * paths). An empty `modelCreatePriority` means every model falls through
42
+ * to `defaultCreatePriority`, so ordering is flat — fine for tests that
43
+ * never exercise FK ordering; consumers who do rely on it should finish
44
+ * wiring the engine before the first `create()` fires.
45
+ */
46
+ export declare const emptyConfig: SyncEngineConfig;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * SyncEngineContext — Runtime dependency container
3
+ *
4
+ * All SDK classes receive this context at construction time.
5
+ * It bundles every injectable dependency so constructors stay clean.
6
+ */
7
+ // ─────────────────────────────────────────────
8
+ // No-op defaults for optional dependencies
9
+ // ─────────────────────────────────────────────
10
+ /** No-op logger — silently discards all log calls */
11
+ export const noopLogger = {
12
+ debug() { },
13
+ info() { },
14
+ warn() { },
15
+ error() { },
16
+ };
17
+ /** No-op observability — silently discards all observability calls */
18
+ export const noopObservability = {
19
+ setContext() { },
20
+ setConnectionState() { },
21
+ breadcrumb() { },
22
+ captureRollback() { },
23
+ captureTransactionFailure() { },
24
+ captureBootstrapFailure() { },
25
+ captureReconciliation() { },
26
+ captureDeltaRetryExhausted() { },
27
+ captureWebSocketError() { },
28
+ captureOfflineFlushFailure() { },
29
+ captureSelfHealing() { },
30
+ captureCommitZeroSyncId() { },
31
+ startSpan(_name, _op, fn, _attributes) {
32
+ return fn();
33
+ },
34
+ async startSpanAsync(_name, _op, fn, _attributes) {
35
+ return fn();
36
+ },
37
+ };
38
+ /** No-op analytics — silently discards all analytics calls */
39
+ export const noopAnalytics = {
40
+ capture() { },
41
+ };
42
+ /** Browser-native online status provider */
43
+ export const browserOnlineStatus = {
44
+ isOnline() {
45
+ return typeof navigator !== 'undefined' ? navigator.onLine : true;
46
+ },
47
+ };
48
+ /** Permissive session error detector — treats 401/403 as session errors */
49
+ export const defaultSessionErrorDetector = {
50
+ isSessionError(error) {
51
+ if (error && typeof error === 'object' && 'isSessionError' in error) {
52
+ return error.isSessionError === true;
53
+ }
54
+ return false;
55
+ },
56
+ isSessionErrorResponse(status) {
57
+ return status === 401 || status === 403;
58
+ },
59
+ };
60
+ /**
61
+ * Fallback config used when the context is read before
62
+ * `createSyncEngine(...)` has initialized it (tests, early-boot code
63
+ * paths). An empty `modelCreatePriority` means every model falls through
64
+ * to `defaultCreatePriority`, so ordering is flat — fine for tests that
65
+ * never exercise FK ordering; consumers who do rely on it should finish
66
+ * wiring the engine before the first `create()` fires.
67
+ */
68
+ export const emptyConfig = {
69
+ modelCreatePriority: new Map(),
70
+ defaultCreatePriority: 40,
71
+ defaultNonCreatePriority: 50,
72
+ essentialFields: {},
73
+ classNameFallbackMap: {},
74
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Always-online network provider — for Node.js / agent / sidecar.
3
+ *
4
+ * The server IS the network — it doesn't go offline. If the Postgres
5
+ * connection drops, that's a database error, not a network error.
6
+ * The "online/offline" concept only applies to browser clients that
7
+ * can lose their WiFi connection.
8
+ *
9
+ * Implements OnlineStatusProvider (same interface as browserOnlineStatus).
10
+ */
11
+ import type { OnlineStatusProvider } from '../interfaces/index.js';
12
+ /**
13
+ * Returns an OnlineStatusProvider that always reports online.
14
+ * `onStatusChange` never fires — the network never transitions.
15
+ */
16
+ export declare function alwaysOnline(): OnlineStatusProvider;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Always-online network provider — for Node.js / agent / sidecar.
3
+ *
4
+ * The server IS the network — it doesn't go offline. If the Postgres
5
+ * connection drops, that's a database error, not a network error.
6
+ * The "online/offline" concept only applies to browser clients that
7
+ * can lose their WiFi connection.
8
+ *
9
+ * Implements OnlineStatusProvider (same interface as browserOnlineStatus).
10
+ */
11
+ /**
12
+ * Returns an OnlineStatusProvider that always reports online.
13
+ * `onStatusChange` never fires — the network never transitions.
14
+ */
15
+ export function alwaysOnline() {
16
+ return {
17
+ isOnline: () => true,
18
+ };
19
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * In-memory storage adapter — replaces IndexedDB for Node.js / agent / test.
3
+ *
4
+ * Implements {@link ObjectStoreContract}, the shared surface that
5
+ * IDB-backed `ObjectStore` also satisfies. Centralized contract so a
6
+ * future drift between the two trips a typecheck error here, not
7
+ * silently in a caller.
8
+ *
9
+ * No persistence — cleared on process restart. This is intentional:
10
+ * the Node sync-server and agent workers get their state from the
11
+ * server's delta stream, not from a local cache.
12
+ */
13
+ import type { ObjectStoreContract } from '../stores/ObjectStoreContract.js';
14
+ export declare class InMemoryObjectStore implements ObjectStoreContract {
15
+ readonly modelName: string;
16
+ readonly storeName: string;
17
+ private data;
18
+ private indexes;
19
+ constructor(modelName: string, storeName: string, indexNames?: string[]);
20
+ put(record: Record<string, unknown>): Promise<void>;
21
+ get(id: string): Promise<Record<string, unknown> | undefined>;
22
+ getAll(): Promise<Record<string, unknown>[]>;
23
+ delete(id: string): Promise<void>;
24
+ getAllFromIndex(indexName: string, value: IDBValidKey): Promise<Record<string, unknown>[]>;
25
+ clear(): Promise<void>;
26
+ /** No-op — in-memory stores don't need closing. */
27
+ markAsClosing(): void;
28
+ private addToIndexes;
29
+ private removeFromIndexes;
30
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * In-memory storage adapter — replaces IndexedDB for Node.js / agent / test.
3
+ *
4
+ * Implements {@link ObjectStoreContract}, the shared surface that
5
+ * IDB-backed `ObjectStore` also satisfies. Centralized contract so a
6
+ * future drift between the two trips a typecheck error here, not
7
+ * silently in a caller.
8
+ *
9
+ * No persistence — cleared on process restart. This is intentional:
10
+ * the Node sync-server and agent workers get their state from the
11
+ * server's delta stream, not from a local cache.
12
+ */
13
+ export class InMemoryObjectStore {
14
+ modelName;
15
+ storeName;
16
+ data = new Map();
17
+ indexes = new Map();
18
+ constructor(modelName, storeName, indexNames = []) {
19
+ this.modelName = modelName;
20
+ this.storeName = storeName;
21
+ for (const name of indexNames) {
22
+ this.indexes.set(name, new Map());
23
+ }
24
+ }
25
+ async put(record) {
26
+ const id = record.id;
27
+ if (!id)
28
+ return;
29
+ // Remove from old index entries if updating
30
+ const existing = this.data.get(id);
31
+ if (existing) {
32
+ this.removeFromIndexes(id, existing);
33
+ }
34
+ this.data.set(id, { ...record });
35
+ this.addToIndexes(id, record);
36
+ }
37
+ async get(id) {
38
+ return this.data.get(id);
39
+ }
40
+ async getAll() {
41
+ return [...this.data.values()];
42
+ }
43
+ async delete(id) {
44
+ const existing = this.data.get(id);
45
+ if (existing) {
46
+ this.removeFromIndexes(id, existing);
47
+ this.data.delete(id);
48
+ }
49
+ }
50
+ async getAllFromIndex(indexName, value) {
51
+ const index = this.indexes.get(indexName);
52
+ if (!index)
53
+ return [];
54
+ // The in-memory index stores values as strings (it doesn't support
55
+ // the full IDB key range — Date / BufferSource / arrays). For the
56
+ // overwhelmingly-common case of string FK ids, coercing through
57
+ // String() preserves the existing behavior while satisfying the
58
+ // shared `ObjectStoreContract` signature.
59
+ const ids = index.get(String(value));
60
+ if (!ids)
61
+ return [];
62
+ return [...ids]
63
+ .map((id) => this.data.get(id))
64
+ .filter((r) => r != null);
65
+ }
66
+ async clear() {
67
+ this.data.clear();
68
+ for (const index of this.indexes.values()) {
69
+ index.clear();
70
+ }
71
+ }
72
+ /** No-op — in-memory stores don't need closing. */
73
+ markAsClosing() { }
74
+ addToIndexes(id, record) {
75
+ for (const [indexName, indexMap] of this.indexes) {
76
+ const value = record[indexName];
77
+ if (value != null) {
78
+ const key = String(value);
79
+ if (!indexMap.has(key))
80
+ indexMap.set(key, new Set());
81
+ indexMap.get(key).add(id);
82
+ }
83
+ }
84
+ }
85
+ removeFromIndexes(id, record) {
86
+ for (const [indexName, indexMap] of this.indexes) {
87
+ const value = record[indexName];
88
+ if (value != null) {
89
+ const key = String(value);
90
+ indexMap.get(key)?.delete(id);
91
+ }
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Agent — AI SDK v6 native hooks for agent awareness.
3
+ *
4
+ * Slots directly into generateText / streamText / ToolLoopAgent via three
5
+ * hooks: prepareStep (inject awareness before each step), onStepFinish
6
+ * (announce activity after each step), and wrapTool (wrap mutation tools
7
+ * with freshness checks). Stateless REST under the hood — works with any
8
+ * API model, no WebSocket.
9
+ *
10
+ * ```ts
11
+ * import { generateText, tool, stepCountIs } from 'ai';
12
+ * import { Agent } from '@ablo/sync-engine-internal/agent';
13
+ *
14
+ * const perception = new Agent({
15
+ * syncServerUrl: 'http://localhost:8080',
16
+ * agentId: 'researcher-1',
17
+ * organizationId: 'org-1',
18
+ * syncGroups: ['deal:abc'],
19
+ * });
20
+ *
21
+ * const result = await generateText({
22
+ * model: 'anthropic/claude-sonnet-4.5',
23
+ * messages,
24
+ * stopWhen: stepCountIs(10),
25
+ * tools: {
26
+ * updateSlide: perception.wrapTool(
27
+ * tool({
28
+ * inputSchema: z.object({ id: z.string(), title: z.string() }),
29
+ * execute: async ({ id, title }) => { ... },
30
+ * }),
31
+ * { entityType: 'Slide', getEntityId: (args) => args.id },
32
+ * ),
33
+ * },
34
+ * prepareStep: perception.prepareStep(), // injects awareness
35
+ * onStepFinish: perception.onStepFinish(), // announces activity
36
+ * });
37
+ * ```
38
+ *
39
+ * Low-level primitives (gather, checkFreshness, announce) are also exposed
40
+ * for custom integrations outside the AI SDK.
41
+ */
42
+ import type { PresenceAnnouncer, AgentContext } from './types.js';
43
+ import type { Activity, IntentClaim } from '../types/streams.js';
44
+ import { createAgentSession } from './session.js';
45
+ export type { AgentContext } from './types.js';
46
+ export type { IntentClaim } from '../types/streams.js';
47
+ /**
48
+ * Shape returned by the sync server's REST `/api/presence` endpoint.
49
+ *
50
+ * Local to this module, NOT exported. The server still speaks the
51
+ * legacy vocabulary (`userId`, `isAgent`, `updatedAt`); the engine has
52
+ * moved on to `participantId` / `participantKind` / `lastActive`.
53
+ * `gather()` returns this shape verbatim today; once the server
54
+ * adopts the engine names this interface deletes and the response is
55
+ * typed as the canonical `Peer`.
56
+ */
57
+ interface WirePeer {
58
+ userId: string;
59
+ isAgent?: boolean;
60
+ status: 'online' | 'away' | 'offline' | (string & {});
61
+ syncGroups?: string[];
62
+ activity?: Activity;
63
+ updatedAt?: number;
64
+ organizationId?: string;
65
+ activeIntents?: IntentClaim[];
66
+ }
67
+ import type { SyncLogger } from '../interfaces/index.js';
68
+ export interface AgentOptions {
69
+ /** Base URL of the sync server, e.g. `http://localhost:8080`. */
70
+ syncServerUrl: string;
71
+ /** Unique agent identifier — without the `agent:` prefix. */
72
+ agentId: string;
73
+ /** Organization this agent belongs to. */
74
+ organizationId: string;
75
+ /** Sync groups determine which other participants are visible. */
76
+ syncGroups: string[];
77
+ /** Optional bearer token for authenticated requests. */
78
+ authToken?: string;
79
+ /** Custom fetch — defaults to global fetch. Useful for testing. */
80
+ fetch?: typeof fetch;
81
+ /** Timeout per request in ms. Default 5000. */
82
+ timeoutMs?: number;
83
+ /**
84
+ * Optional presence announcer — route `announce()` through this instead
85
+ * of REST. Pass a connected SyncAgent here to reuse its WebSocket and
86
+ * avoid per-step HTTP round trips.
87
+ */
88
+ announcer?: PresenceAnnouncer;
89
+ /**
90
+ * Optional logger. The agent SDK runs in standalone Node processes
91
+ * that don't share the SyncEngineContext, so Agent takes
92
+ * its own logger handle. Defaults to a console-backed logger; pass
93
+ * your structured logger (Pino, Winston, etc.) to get consistent
94
+ * agent-worker log routing.
95
+ */
96
+ logger?: SyncLogger;
97
+ }
98
+ export interface GatherOptions {
99
+ /** Focus context on these entities — format: "ModelName:id". */
100
+ focusEntities?: string[];
101
+ /** Maximum output characters for the formatted prompt. Default 2000. */
102
+ maxChars?: number;
103
+ /** Include presence of other participants. Default true. */
104
+ includePresence?: boolean;
105
+ /** Exclude this agent's own presence from the output. Default true. */
106
+ excludeSelf?: boolean;
107
+ }
108
+ export interface AgentSnapshot {
109
+ timestamp: number;
110
+ presence: WirePeer[];
111
+ }
112
+ export interface GatherResult {
113
+ /** Natural-language summary ready to inject as a system message. */
114
+ prompt: string;
115
+ /** Structured data for programmatic use. */
116
+ snapshot: AgentSnapshot;
117
+ }
118
+ export interface FreshnessCheck {
119
+ stale: boolean;
120
+ reason?: 'ok' | 'not_found' | 'modified';
121
+ /** Current entity state from the server. */
122
+ currentState?: Record<string, unknown>;
123
+ lastModifiedBy?: string;
124
+ lastModifiedAt?: number;
125
+ /** Human-readable summary — feed this back to the LLM when stale. */
126
+ summary?: string;
127
+ /**
128
+ * Pending-mutation intents from OTHER participants targeting this
129
+ * entity (self-intents filtered out). Empty = no one else is
130
+ * currently generating against this entity. Non-empty is ADVISORY
131
+ * — the agent can proceed, wait, or defer. Stale-read protection
132
+ * that predates committed deltas.
133
+ */
134
+ pendingIntents?: IntentClaim[];
135
+ }
136
+ /** Subset of AI SDK's ModelMessage — structural. */
137
+ export interface AgentMessage {
138
+ role: 'system' | 'user' | 'assistant' | 'tool';
139
+ content: unknown;
140
+ }
141
+ /** Subset of AI SDK's prepareStep context. */
142
+ export interface PrepareStepContext<M extends AgentMessage = AgentMessage> {
143
+ stepNumber: number;
144
+ steps: ReadonlyArray<{
145
+ toolCalls?: ReadonlyArray<{
146
+ toolName: string;
147
+ input?: unknown;
148
+ args?: unknown;
149
+ }>;
150
+ toolResults?: ReadonlyArray<unknown>;
151
+ }>;
152
+ messages: M[];
153
+ model?: unknown;
154
+ }
155
+ /** Subset of AI SDK's prepareStep return shape. */
156
+ export interface PrepareStepResult<M extends AgentMessage = AgentMessage> {
157
+ messages?: M[];
158
+ model?: unknown;
159
+ toolChoice?: unknown;
160
+ activeTools?: string[];
161
+ }
162
+ /** Subset of AI SDK's onStepFinish context. */
163
+ export interface StepFinishContext {
164
+ stepType?: 'initial' | 'continue' | 'tool-result';
165
+ finishReason?: string;
166
+ text?: string;
167
+ toolCalls?: ReadonlyArray<{
168
+ toolName: string;
169
+ input?: unknown;
170
+ args?: unknown;
171
+ }>;
172
+ toolResults?: ReadonlyArray<unknown>;
173
+ usage?: {
174
+ inputTokens?: number;
175
+ outputTokens?: number;
176
+ totalTokens?: number;
177
+ };
178
+ }
179
+ /** Subset of AI SDK's ToolExecutionOptions. */
180
+ export interface ToolExecutionOptions {
181
+ toolCallId?: string;
182
+ messages?: AgentMessage[];
183
+ abortSignal?: AbortSignal;
184
+ experimental_context?: unknown;
185
+ }
186
+ /** Minimal tool shape — a subset of AI SDK's Tool type. */
187
+ export interface AgentTool<TArgs = unknown, TResult = unknown> {
188
+ description?: string;
189
+ inputSchema?: unknown;
190
+ execute?: (args: TArgs, options: ToolExecutionOptions) => Promise<TResult> | TResult;
191
+ [extra: string]: unknown;
192
+ }
193
+ export interface PrepareStepOptions {
194
+ /** Max characters of awareness context to inject. Default 1500. */
195
+ maxChars?: number;
196
+ /**
197
+ * Derive focus entities from recent tool calls so the LLM sees participants
198
+ * working on the same entities. Requires a mapper from tool call to entity
199
+ * tokens (format: "ModelName:id"). Default: no auto-focus.
200
+ */
201
+ focusFromToolCalls?: (toolCall: {
202
+ toolName: string;
203
+ input?: unknown;
204
+ args?: unknown;
205
+ }) => string[] | undefined;
206
+ /** Skip awareness injection for step 0 (initial prompt). Default false. */
207
+ skipFirstStep?: boolean;
208
+ }
209
+ export interface OnStepFinishOptions {
210
+ /**
211
+ * Derive an activity announcement from the finished step. Return null to
212
+ * skip announcing for this step. Default: announces the last tool name
213
+ * if any tool was called.
214
+ */
215
+ activity?: (ctx: StepFinishContext) => Activity | null;
216
+ }
217
+ export interface WrapToolOptions<TArgs> {
218
+ /** Entity type the tool mutates — e.g. "Slide", "Sheet". */
219
+ entityType: string;
220
+ /** Extract the entity id from the tool's args. */
221
+ getEntityId: (args: TArgs) => string | undefined;
222
+ /**
223
+ * Resolve the timestamp the LLM last saw this entity. If omitted or it
224
+ * returns 0, the freshness check is skipped (no baseline to compare).
225
+ */
226
+ lastSeenAt?: (args: TArgs, options: ToolExecutionOptions) => number | undefined;
227
+ /**
228
+ * Announce that the agent is about to work on this entity before executing.
229
+ * Default true — the agent announces `action: "editing"` automatically.
230
+ */
231
+ announceOnExecute?: boolean;
232
+ }
233
+ export declare class Agent implements PresenceAnnouncer {
234
+ private readonly opts;
235
+ constructor(options: AgentOptions);
236
+ /**
237
+ * Build a long-lived agent session — caches `Ablo({kind:'agent'})`
238
+ * instances per `(org, user, surface, target)` and refreshes capability
239
+ * tokens before TTL elapses. Use on the server when the same agent
240
+ * identity handles many requests.
241
+ *
242
+ * Returns the cache, NOT an `Agent` instance: the long-lived path
243
+ * uses `Ablo({kind:'agent'})` over WebSocket, while the `Agent` class
244
+ * itself is the short-lived REST helper for AI SDK tool loops. The
245
+ * static method lives here so consumers reach for everything
246
+ * agent-related under one namespace.
247
+ *
248
+ * ```ts
249
+ * const session = Agent.session({ syncServerUrl, schema, issueToken });
250
+ * const ablo = await session.getAgent({ userId, organizationId, surfaceClass });
251
+ * ```
252
+ */
253
+ static session: typeof createAgentSession;
254
+ /** The fully-qualified userId used on the wire: `agent:<agentId>`. */
255
+ get userId(): string;
256
+ /**
257
+ * Extract the Agent instance from an AI SDK tool's
258
+ * `experimental_context`. Use inside tool `execute` functions to reach
259
+ * the perception without closure-capturing it.
260
+ *
261
+ * ```ts
262
+ * execute: async (args, { experimental_context }) => {
263
+ * const perception = Agent.fromContext(experimental_context);
264
+ * const check = await perception.checkFreshness('Slide', args.id, lastSeenAt);
265
+ * // ...
266
+ * }
267
+ * ```
268
+ *
269
+ * Throws if the context is missing or doesn't contain an Agent.
270
+ * @param ctx The `experimental_context` passed to the tool.
271
+ * @param toolName Optional tool name for error messages.
272
+ */
273
+ static fromContext(ctx: unknown, toolName?: string): Agent;
274
+ /**
275
+ * Narrower variant of {@link fromContext} that returns `undefined` instead
276
+ * of throwing when perception isn't in context. Useful for tools where
277
+ * awareness is optional (e.g., read-only tools that work without it).
278
+ */
279
+ static tryFromContext(ctx: unknown): Agent | undefined;
280
+ /**
281
+ * Announce this agent's presence/activity. Fire-and-forget — logs errors
282
+ * but never throws (presence failures must not block the agent loop).
283
+ *
284
+ * If a `announcer` was provided (e.g. a connected SyncAgent), routes
285
+ * through it to reuse the WebSocket. Otherwise falls back to REST POST.
286
+ */
287
+ announce(status: 'online' | 'away' | 'offline', activity?: Activity): Promise<void>;
288
+ /**
289
+ * Gather a snapshot of current activity by peers and format it as
290
+ * natural-language context for injection into the next LLM prompt.
291
+ */
292
+ gather(options?: GatherOptions): Promise<GatherResult>;
293
+ /**
294
+ * Check if an entity was modified since `lastSeenAt`. Use before
295
+ * executing a mutation to detect stale state.
296
+ *
297
+ * Returns `{ stale: true, summary }` when the entity changed — feed
298
+ * `summary` back to the LLM as a tool result so it can adjust its plan.
299
+ */
300
+ checkFreshness(entityType: string, entityId: string, lastSeenAt: number): Promise<FreshnessCheck>;
301
+ /**
302
+ * Pull the org's presence, filter to intents targeting the given
303
+ * entity (self-intents excluded). Advisory — returns empty on any
304
+ * error so `checkFreshness` stays usable when the presence endpoint
305
+ * is down. Case-insensitive match on entityType + entityId to absorb
306
+ * PascalCase / lowercase divergence.
307
+ */
308
+ private fetchPendingIntentsFor;
309
+ /**
310
+ * Build a `prepareStep` hook for AI SDK's generateText / streamText /
311
+ * ToolLoopAgent. Called before each step — injects a system message
312
+ * summarizing what other agents are doing right now.
313
+ *
314
+ * ```ts
315
+ * const result = await generateText({
316
+ * // ...
317
+ * prepareStep: perception.prepareStep({ maxChars: 1500 }),
318
+ * });
319
+ * ```
320
+ */
321
+ prepareStep<M extends AgentMessage = AgentMessage>(options?: PrepareStepOptions): (ctx: PrepareStepContext<M>) => Promise<PrepareStepResult<M> | undefined>;
322
+ /**
323
+ * Build an `onStepFinish` hook for AI SDK. Called after each step —
324
+ * announces the agent's activity based on the tool calls that just ran.
325
+ *
326
+ * ```ts
327
+ * const result = await generateText({
328
+ * // ...
329
+ * onStepFinish: perception.onStepFinish(),
330
+ * });
331
+ * ```
332
+ */
333
+ onStepFinish(options?: OnStepFinishOptions): (ctx: StepFinishContext) => Promise<void>;
334
+ /**
335
+ * Wrap an AI SDK tool to check entity freshness before executing. If the
336
+ * entity was modified by another actor since the LLM last saw it, returns
337
+ * a diff summary as the tool result instead of executing — the LLM adjusts
338
+ * its plan rather than blindly overwriting.
339
+ *
340
+ * ```ts
341
+ * tools: {
342
+ * updateSlide: perception.wrapTool(
343
+ * tool({ inputSchema: ..., execute: ... }),
344
+ * { entityType: 'Slide', getEntityId: (args) => args.id },
345
+ * ),
346
+ * }
347
+ * ```
348
+ */
349
+ wrapTool<TArgs, TResult, TTool extends AgentTool<TArgs, TResult>>(originalTool: TTool, config: WrapToolOptions<TArgs>): TTool;
350
+ private fetchPresence;
351
+ private formatPrompt;
352
+ private request;
353
+ }
354
+ export declare namespace Agent {
355
+ type Options = AgentOptions;
356
+ type Context = AgentContext;
357
+ type SessionOptions = import('./session.js').AgentSessionOptions;
358
+ }