@hotmeshio/hotmesh 0.5.0 β†’ 0.5.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.
Files changed (49) hide show
  1. package/README.md +234 -237
  2. package/build/modules/errors.d.ts +9 -0
  3. package/build/modules/errors.js +9 -0
  4. package/build/package.json +16 -14
  5. package/build/services/hotmesh/index.d.ts +9 -11
  6. package/build/services/hotmesh/index.js +9 -11
  7. package/build/services/memflow/entity.d.ts +168 -4
  8. package/build/services/memflow/entity.js +177 -15
  9. package/build/services/memflow/index.d.ts +8 -0
  10. package/build/services/memflow/index.js +8 -0
  11. package/build/services/memflow/worker.js +25 -0
  12. package/build/services/memflow/workflow/execChild.js +1 -0
  13. package/build/services/memflow/workflow/execHook.d.ts +2 -2
  14. package/build/services/memflow/workflow/execHook.js +19 -9
  15. package/build/services/memflow/workflow/index.d.ts +2 -4
  16. package/build/services/memflow/workflow/index.js +2 -4
  17. package/build/services/memflow/workflow/interruption.d.ts +28 -0
  18. package/build/services/memflow/workflow/interruption.js +43 -0
  19. package/build/services/memflow/workflow/proxyActivities.js +1 -0
  20. package/build/services/memflow/workflow/sleepFor.js +1 -0
  21. package/build/services/memflow/workflow/waitFor.js +4 -4
  22. package/build/services/search/index.d.ts +10 -0
  23. package/build/services/search/providers/postgres/postgres.d.ts +12 -0
  24. package/build/services/search/providers/postgres/postgres.js +209 -0
  25. package/build/services/search/providers/redis/ioredis.d.ts +4 -0
  26. package/build/services/search/providers/redis/ioredis.js +13 -0
  27. package/build/services/search/providers/redis/redis.d.ts +4 -0
  28. package/build/services/search/providers/redis/redis.js +13 -0
  29. package/build/services/store/providers/postgres/kvsql.d.ts +13 -37
  30. package/build/services/store/providers/postgres/kvsql.js +2 -2
  31. package/build/services/store/providers/postgres/kvtypes/hash/basic.d.ts +16 -0
  32. package/build/services/store/providers/postgres/kvtypes/hash/basic.js +480 -0
  33. package/build/services/store/providers/postgres/kvtypes/hash/expire.d.ts +5 -0
  34. package/build/services/store/providers/postgres/kvtypes/hash/expire.js +33 -0
  35. package/build/services/store/providers/postgres/kvtypes/hash/index.d.ts +29 -0
  36. package/build/services/store/providers/postgres/kvtypes/hash/index.js +190 -0
  37. package/build/services/store/providers/postgres/kvtypes/hash/jsonb.d.ts +14 -0
  38. package/build/services/store/providers/postgres/kvtypes/hash/jsonb.js +699 -0
  39. package/build/services/store/providers/postgres/kvtypes/hash/scan.d.ts +10 -0
  40. package/build/services/store/providers/postgres/kvtypes/hash/scan.js +91 -0
  41. package/build/services/store/providers/postgres/kvtypes/hash/types.d.ts +19 -0
  42. package/build/services/store/providers/postgres/kvtypes/hash/types.js +2 -0
  43. package/build/services/store/providers/postgres/kvtypes/hash/utils.d.ts +18 -0
  44. package/build/services/store/providers/postgres/kvtypes/hash/utils.js +90 -0
  45. package/build/types/memflow.d.ts +1 -1
  46. package/build/types/meshdata.d.ts +1 -1
  47. package/package.json +16 -14
  48. package/build/services/store/providers/postgres/kvtypes/hash.d.ts +0 -60
  49. package/build/services/store/providers/postgres/kvtypes/hash.js +0 -1287
package/README.md CHANGED
@@ -1,44 +1,52 @@
1
1
  # HotMesh
2
2
 
3
- **Permanent-Memory Workflows & AI Agents**
3
+ **🧠 Workflow That Remembers**
4
4
 
5
5
  ![beta release](https://img.shields.io/badge/release-beta-blue.svg) ![made with typescript](https://img.shields.io/badge/built%20with-typescript-lightblue.svg)
6
6
 
7
- **HotMesh** is a Temporal-style workflow engine that runs natively on PostgreSQL β€” with a powerful twist: every workflow maintains permanent, state that persists independently of the workflow itself.
7
+ HotMesh brings a **memory model** to your automation: durable entities that hold context, support concurrency, and evolve across runs. Built on PostgreSQL, it treats your database not just as storageβ€”but as the runtime hub for agents, pipelines, and long-lived processes.
8
8
 
9
- This means:
9
+ Use HotMesh to:
10
10
 
11
- * Any number of lightweight, thread-safe **hook workers** can attach to the same workflow record at any time.
12
- * These hooks can safely **read and write** to shared state.
13
- * The result is a **durable execution model** with **evolving memory**, ideal for **human-in-the-loop processes** and **AI agents that learn over time**.
11
+ * **Store Evolving State** – Retain memory/state between executions
12
+ * **Coordinate Distributed Work** – Safely allow multiple workers to act on shared state
13
+ * **Track and Replay** – Full audit history and replay support by default
14
14
 
15
15
  ---
16
16
 
17
17
  ## Table of Contents
18
18
 
19
- 1. πŸš€ Quick Start
20
- 2. 🧠 How Permanent Memory Works
21
- 3. πŸ”Œ Hooks & Entity API
22
- 4. πŸ€– Building Durable AI Agents
23
- 5. πŸ”¬ Advanced Patterns & Recipes
24
- 6. πŸ“š Documentation & Links
19
+ 1. [Quick Start](#quick-start)
20
+ 2. [Permanent Memory Architecture](#permanent-memory-architecture)
21
+ 3. [Durable AI Agents](#durable-ai-agents)
22
+ 4. [Building Pipelines with State](#building-pipelines-with-state)
23
+ 5. [Documentation & Links](#documentation--links)
25
24
 
26
25
  ---
27
26
 
28
- ## πŸš€ Quick Start
27
+ ## Quick Start
28
+
29
+ ### Prerequisites
30
+
31
+ * PostgreSQL (or Supabase)
32
+ * Node.js 16+
29
33
 
30
34
  ### Install
35
+
31
36
  ```bash
32
37
  npm install @hotmeshio/hotmesh
33
38
  ```
34
39
 
35
- ### Start a workflow
36
- ```typescript
37
- // index.ts
40
+ ### Connect to a Database
41
+
42
+ HotMesh leverages Temporal.io's developer-friendly syntax for authoring workers, workflows, and clients. The `init` and `start` methods should look familiar.
43
+
44
+ ```ts
38
45
  import { MemFlow } from '@hotmeshio/hotmesh';
39
46
  import { Client as Postgres } from 'pg';
40
47
 
41
48
  async function main() {
49
+ // MemFlow will auto-provision the database upon init
42
50
  const mf = await MemFlow.init({
43
51
  appId: 'my-app',
44
52
  engine: {
@@ -49,13 +57,13 @@ async function main() {
49
57
  }
50
58
  });
51
59
 
52
- // Kick off a workflow
60
+ // Start a workflow with an assigned ID and arguments
53
61
  const handle = await mf.workflow.start({
54
- entity: 'user',
55
- workflowName: 'userExample',
56
- workflowId: 'jane@hotmesh.com',
57
- args: ['Jane'],
58
- taskQueue: 'entityqueue'
62
+ entity: 'research-agent',
63
+ workflowName: 'researchAgent',
64
+ workflowId: 'agent-session-jane-001',
65
+ args: ['What are the long-term impacts of renewable energy subsidies?'],
66
+ taskQueue: 'agents'
59
67
  });
60
68
 
61
69
  console.log('Result:', await handle.result());
@@ -64,168 +72,53 @@ async function main() {
64
72
  main().catch(console.error);
65
73
  ```
66
74
 
67
- ---
68
-
69
- ## 🧠 How Permanent Memory Works
75
+ ### System Benefits
70
76
 
71
- * **Entity = persistent JSON record** – each workflow's memory is stored as a JSONB row in your Postgres database
72
- * **Atomic operations** (`set`, `merge`, `append`, `increment`, `toggle`, `delete`, …)
73
- * **Transactional** – every update participates in the workflow/DB transaction
74
- * **Time-travel-safe** – full replay compatibility; side-effect detector guarantees determinism
75
- * **Hook-friendly** – any worker with the record ID can attach and mutate its slice of the JSON
76
- * **Index-friendly** - entity data is stored as JSONB; add partial indexes for improved query analysis.
77
-
78
- **Example: Adding a Partial Index for Specific Entity Types**
79
- ```sql
80
- -- Create a partial index for 'user' entities with specific entity values
81
- CREATE INDEX idx_user_premium ON your_app.jobs (id)
82
- WHERE entity = 'user' AND (context->>'isPremium')::boolean = true;
83
- ```
84
- This index will only be used for queries that match both conditions, making lookups for premium users much faster.
77
+ * **No Setup Required** – Tables and indexes are provisioned automatically
78
+ * **Shared State** – Every worker shares access to the same entity memory
79
+ * **Coordination by Design** – PostgreSQL handles consistency and isolation
80
+ * **Tenant Isolation** – Each app maintains its own schema
81
+ * **Scalable Defaults** – Partitioned tables and index support included
85
82
 
86
83
  ---
87
84
 
88
- ## πŸ”Œ Hooks & Entity API – Full Example
85
+ ## Permanent Memory Architecture
89
86
 
90
- HotMesh hooks are powerful because they can be called both internally (from within a workflow) and externally (from outside, even after the workflow completes). This means you can:
87
+ Every workflow in HotMesh is backed by an "entity": a versioned, JSONB record that tracks its memory and state transitions.
91
88
 
92
- * Start a workflow that sets up initial state
93
- * Have the workflow call some hooks internally
94
- * Let the workflow complete
95
- * Continue to update the workflow's entity state from the outside via hooks
96
- * Build long-running processes that evolve over time
89
+ * **Entities** – Represent long-lived state for a workflow or agent
90
+ * **Commands** – Modify state with methods like `set`, `merge`, `append`, `increment`
91
+ * **Consistency** – All updates are transactional with Postgres
92
+ * **Replay Safety** – Protects against duplicated side effects during re-execution
93
+ * **Partial Indexing** – Optimized querying of fields within large JSON structures
97
94
 
98
- Here's a complete example showing both internal and external hook usage:
99
-
100
- ```typescript
101
- import { MemFlow } from '@hotmeshio/hotmesh';
95
+ ### Example: Partial Index for Premium Users
102
96
 
103
- /* ------------ Main workflow ------------ */
104
- export async function userExample(name: string): Promise<any> {
105
- //the entity method provides transactional, replayable access to shared job state
106
- const entity = await MemFlow.workflow.entity();
107
-
108
- //create the initial entity (even arrays are supported)
109
- await entity.set({
110
- user: { name },
111
- hooks: {},
112
- metrics: { count: 0 }
113
- });
114
-
115
- // Call one hook internally
116
- const result1 = await MemFlow.workflow.execHook({
117
- taskQueue: 'entityqueue',
118
- workflowName: 'hook1',
119
- args: [name, 'hook1'],
120
- signalId: 'hook1-complete'
121
- });
122
-
123
- // merge the result
124
- await entity.merge({ hooks: { r1: result1 } });
125
- await entity.increment('metrics.count', 1);
126
-
127
- return "The main has completed; the db record persists and can be hydrated; hook in from the outside!";
128
- }
129
-
130
- /* ------------ Hook 1 (hooks have access to methods like sleepFor) ------------ */
131
- export async function hook1(name: string, kind: string): Promise<any> {
132
- await MemFlow.workflow.sleepFor('2 seconds');
133
- const res = { kind, processed: true, at: Date.now() };
134
- await MemFlow.workflow.signal('hook1-complete', res);
135
- }
136
-
137
- /* ------------ Hook 2 (hooks can access shared job entity) ------------ */
138
- export async function hook2(name: string, kind: string): Promise<void> {
139
- const entity = await MemFlow.workflow.entity();
140
- await entity.merge({ user: { lastSeen: new Date().toISOString() } });
141
- await MemFlow.workflow.signal('hook2-complete', { ok: true });
142
- }
143
-
144
- /* ------------ Worker/Hook Registration ------------ */
145
- async function startWorker() {
146
- const mf = await MemFlow.init({
147
- appId: 'my-app',
148
- engine: {
149
- connection: {
150
- class: Postgres,
151
- options: { connectionString: process.env.DATABASE_URL }
152
- }
153
- }
154
- });
155
-
156
- const worker = await mf.worker.create({
157
- taskQueue: 'entityqueue',
158
- workflow: example
159
- });
160
-
161
- await mf.worker.create({
162
- taskQueue: 'entityqueue',
163
- workflow: hook1
164
- });
165
-
166
- await mf.worker.create({
167
- taskQueue: 'entityqueue',
168
- workflow: hook2
169
- });
170
-
171
- console.log('Workers and hooks started and listening...');
172
- }
97
+ ```sql
98
+ -- Index only those user entities that are marked as premium
99
+ CREATE INDEX idx_user_premium ON your_app.jobs (id)
100
+ WHERE entity = 'user' AND (context->>'isPremium')::boolean = true;
173
101
  ```
174
102
 
175
- ### The Power of External Hooks
176
-
177
- One of HotMesh's most powerful features is that workflow entities remain accessible even after the main workflow completes. By providing the original workflow ID, any authorized client can:
178
-
179
- * Hook into existing workflow entities
180
- * Update state and trigger new processing
181
- * Build evolving, long-running processes
182
- * Enable human-in-the-loop workflows
183
- * Create AI agents that learn over time
184
-
185
- Here's how to hook into an existing workflow from the outside:
186
-
187
- ```typescript
188
- /* ------------ External Hook Example ------------ */
189
- async function externalHookExample() {
190
- const client = new MemFlow.Client({
191
- appId: 'my-app',
192
- engine: {
193
- connection: {
194
- class: Postgres,
195
- options: { connectionString: process.env.DATABASE_URL }
196
- }
197
- }
198
- });
199
-
200
- // Start hook2 externally by providing the original workflow ID
201
- await client.workflow.hook({
202
- workflowId: 'jane@hotmesh.com', //id of the target workflow
203
- taskQueue: 'entityqueue',
204
- workflowName: 'hook2',
205
- args: [name, 'external-hook']
206
- });
207
- }
208
- ```
103
+ This index improves performance for filtered queries while reducing index size.
209
104
 
210
105
  ---
211
106
 
212
- ## πŸ€– Building Durable AI Agents
107
+ ## Durable AI Agents
213
108
 
214
- HotMesh's permanent memory enables a revolutionary approach to AI agents: **perspective-oriented intelligence**. Instead of monolithic agents, you build **multi-faceted agents** where:
109
+ Agents often require memoryβ€”context that persists between invocations, spans multiple perspectives, or outlives a single process. HotMesh supports that natively.
215
110
 
216
- * **Entity state** = the agent's living, evolving context
217
- * **Hooks** = different perspectives that operate on that context
218
- * **Child workflows** = specialized co-agents with their own entity types
219
- * **Shared memory** = enables rich self-reflection and multi-agent collaboration
111
+ The following example builds a "research agent" that runs sub-flows and self-reflects on the results.
220
112
 
221
- ### Example: A Research Agent with Multiple Perspectives
113
+ ### Research Agent Example
222
114
 
223
- ```typescript
224
- /* ------------ Main Research Agent ------------ */
115
+ #### Main Coordinator Agent
116
+
117
+ ```ts
225
118
  export async function researchAgent(query: string): Promise<any> {
226
119
  const entity = await MemFlow.workflow.entity();
227
-
228
- // Initialize agent context
120
+
121
+ // Set up shared memory for this agent session
229
122
  await entity.set({
230
123
  query,
231
124
  findings: [],
@@ -234,20 +127,20 @@ export async function researchAgent(query: string): Promise<any> {
234
127
  status: 'researching'
235
128
  });
236
129
 
237
- // Spawn perspective hooks that operate on shared context
238
- const optimisticView = MemFlow.workflow.execHook({
239
- taskQueue: 'perspectives',
130
+ // Launch perspective hooks in parallel (no need to await here)
131
+ const optimistic = MemFlow.workflow.execHook({
132
+ taskQueue: 'agents',
240
133
  workflowName: 'optimisticPerspective',
241
134
  args: [query]
242
135
  });
243
136
 
244
- const skepticalView = MemFlow.workflow.execHook({
245
- taskQueue: 'perspectives',
137
+ const skeptical = MemFlow.workflow.execHook({
138
+ taskQueue: 'agents',
246
139
  workflowName: 'skepticalPerspective',
247
140
  args: [query]
248
141
  });
249
142
 
250
- // Spawn a fact-checker child agent with its own entity type
143
+ // Launch a child workflow with its own isolated entity/state
251
144
  const factChecker = await MemFlow.workflow.execChild({
252
145
  entity: 'fact-checker',
253
146
  workflowName: 'factCheckAgent',
@@ -256,121 +149,225 @@ export async function researchAgent(query: string): Promise<any> {
256
149
  taskQueue: 'agents'
257
150
  });
258
151
 
259
- // Wait for all perspectives to contribute
260
- await Promise.all([optimisticView, skepticalView, factChecker]);
152
+ // Wait for all views to complete before analyzing
153
+ await Promise.all([optimistic, skeptical, factChecker]);
261
154
 
262
- // Self-reflection: analyze conflicting perspectives
155
+ // Final synthesis: aggregate and compare all perspectives
263
156
  await MemFlow.workflow.execHook({
264
157
  taskQueue: 'perspectives',
265
158
  workflowName: 'synthesizePerspectives',
266
159
  args: []
267
160
  });
268
161
 
269
- const finalContext = await entity.get();
270
- return finalContext;
162
+ return await entity.get();
271
163
  }
164
+ ```
272
165
 
273
- /* ------------ Optimistic Perspective Hook ------------ */
274
- export async function optimisticPerspective(query: string): Promise<void> {
166
+ > πŸ’‘ **Developer Note**: A complete implementation of this Research Agent example with tests, OpenAI integration, and multi-perspective analysis can be found in the [test suite](./tests/memflow/agent).
167
+
168
+ #### Hooks: Perspectives
169
+
170
+ ```ts
171
+ // Optimistic hook looks for affirming evidence
172
+ export async function optimisticPerspective(query: string, config: {signal: string}): Promise<void> {
275
173
  const entity = await MemFlow.workflow.entity();
276
-
277
- // Optimistic perspective: look for supporting evidence
278
174
  const findings = await searchForSupportingEvidence(query);
279
-
280
- await entity.merge({
281
- perspectives: {
282
- optimistic: {
283
- findings,
284
- confidence: 0.8,
285
- bias: 'Tends to emphasize positive evidence'
286
- }
287
- }
288
- });
175
+ await entity.merge({ perspectives: { optimistic: { findings, confidence: 0.8 }}});
176
+ //signal the caller to notify all done
177
+ await MemFlow.workflow.signal(config.signal, {});
289
178
  }
290
179
 
291
- /* ------------ Skeptical Perspective Hook ------------ */
292
- export async function skepticalPerspective(query: string): Promise<void> {
180
+ // Skeptical hook seeks out contradictions and counterpoints
181
+ export async function skepticalPerspective(query: string, config: {signal: string}): Promise<void> {
293
182
  const entity = await MemFlow.workflow.entity();
294
-
295
- // Skeptical perspective: challenge assumptions
296
183
  const counterEvidence = await searchForCounterEvidence(query);
297
-
298
- await entity.merge({
299
- perspectives: {
300
- skeptical: {
301
- counterEvidence,
302
- confidence: 0.6,
303
- bias: 'Challenges assumptions and seeks contradictory evidence'
304
- }
305
- }
306
- });
184
+ await entity.merge({ perspectives: { skeptical: { counterEvidence, confidence: 0.6 }}});
185
+ await MemFlow.workflow.signal(config.signal, {});
307
186
  }
187
+ ```
188
+
189
+ #### Child Agent: Fact Checker
308
190
 
309
- /* ------------ Fact-Checker Child Agent ------------ */
191
+ ```ts
192
+ // A dedicated child agent with its own entity type and context
310
193
  export async function factCheckAgent(query: string): Promise<any> {
311
- // This child has its own entity type for specialized fact-checking context
312
194
  const entity = await MemFlow.workflow.entity();
313
-
314
- await entity.set({
315
- query,
316
- sources: [],
317
- verifications: [],
318
- credibilityScore: 0
319
- });
195
+ await entity.set({ query, sources: [], verifications: [] });
320
196
 
321
- // Fact-checker can spawn its own specialized hooks
322
197
  await MemFlow.workflow.execHook({
323
- taskQueue: 'verification',
198
+ taskQueue: 'agents',
324
199
  workflowName: 'verifySourceCredibility',
325
200
  args: [query]
326
201
  });
327
202
 
328
203
  return await entity.get();
329
204
  }
205
+ ```
330
206
 
331
- /* ------------ Synthesis Perspective Hook ------------ */
332
- export async function synthesizePerspectives(): Promise<void> {
207
+ #### Synthesis
208
+
209
+ ```ts
210
+ // Synthesis hook aggregates different viewpoints
211
+ export async function synthesizePerspectives(config: {signal: string}): Promise<void> {
333
212
  const entity = await MemFlow.workflow.entity();
334
213
  const context = await entity.get();
335
-
336
- // Analyze conflicting perspectives and synthesize
337
- const synthesis = await analyzePerspectives(context.perspectives);
338
-
214
+
215
+ const result = await analyzePerspectives(context.perspectives);
216
+
339
217
  await entity.merge({
340
218
  perspectives: {
341
219
  synthesis: {
342
- finalAssessment: synthesis,
343
- confidence: calculateConfidence(context.perspectives),
344
- reasoning: 'Balanced analysis of optimistic and skeptical viewpoints'
220
+ finalAssessment: result,
221
+ confidence: calculateConfidence(context.perspectives)
345
222
  }
346
223
  },
347
224
  status: 'completed'
348
225
  });
226
+ await MemFlow.workflow.signal(config.signal, {});
227
+ }
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Building Pipelines with State
233
+
234
+ HotMesh treats pipelines as long-lived records, not ephemeral jobs. Every pipeline run is stateful, resumable, and traceable.
235
+
236
+ ### Setup a Data Pipeline
237
+
238
+ ```ts
239
+ export async function dataPipeline(source: string): Promise<void> {
240
+ const entity = await MemFlow.workflow.entity();
241
+
242
+ // Initial policy and tracking setup
243
+ await entity.set({
244
+ source,
245
+ pipeline: { version: 1, policy: { refreshInterval: '24 hours' } },
246
+ changeLog: []
247
+ });
248
+
249
+ // Trigger the recurring orchestration pipeline
250
+ await MemFlow.workflow.execHook({
251
+ taskQueue: 'pipeline',
252
+ workflowName: 'runPipeline',
253
+ args: [true]
254
+ });
255
+ }
256
+ ```
257
+
258
+ ### Orchestration Hook
259
+
260
+ ```ts
261
+ export async function runPipeline(repeat = false): Promise<void> {
262
+ do {
263
+ // Perform transformation step
264
+ await MemFlow.workflow.execHook({
265
+ taskQueue: 'transform',
266
+ workflowName: 'cleanData',
267
+ args: []
268
+ });
269
+
270
+ if (repeat) {
271
+ // Schedule next execution
272
+ await MemFlow.workflow.execHook({
273
+ taskQueue: 'scheduler',
274
+ workflowName: 'scheduleRefresh',
275
+ args: []
276
+ });
277
+ }
278
+ } while (repeat)
279
+ }
280
+
281
+ /**
282
+ * Hook to clean and transform data
283
+ */
284
+ export async function cleanData(signalInfo?: { signal: string }): Promise<void> {
285
+ const entity = await MemFlow.workflow.entity();
286
+
287
+ // Simulate data cleaning
288
+ await entity.merge({
289
+ status: 'cleaning',
290
+ lastCleanedAt: new Date().toISOString()
291
+ });
292
+
293
+ // Add to changelog
294
+ await entity.append('changeLog', {
295
+ action: 'clean',
296
+ timestamp: new Date().toISOString()
297
+ });
298
+
299
+ // Signal completion if called via execHook
300
+ if (signalInfo?.signal) {
301
+ await MemFlow.workflow.signal(signalInfo.signal, {
302
+ status: 'cleaned',
303
+ timestamp: new Date().toISOString()
304
+ });
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Hook to schedule the next refresh based on policy
310
+ */
311
+ export async function scheduleRefresh(signalInfo?: { signal: string }): Promise<void> {
312
+ const entity = await MemFlow.workflow.entity();
313
+
314
+ // Get refresh interval from policy
315
+ const currentEntity = await entity.get();
316
+ const refreshInterval = currentEntity.pipeline.policy.refreshInterval;
317
+
318
+ // Sleep for the configured interval
319
+ await MemFlow.workflow.sleepFor(refreshInterval);
320
+
321
+ // Update status after sleep
322
+ await entity.merge({
323
+ status: 'ready_for_refresh',
324
+ nextRefreshAt: new Date().toISOString()
325
+ });
326
+
327
+ // Add to changelog
328
+ await entity.append('changeLog', {
329
+ action: 'schedule_refresh',
330
+ timestamp: new Date().toISOString(),
331
+ nextRefresh: new Date().toISOString()
332
+ });
333
+
334
+ // Signal completion if called via execHook
335
+ if (signalInfo?.signal) {
336
+ await MemFlow.workflow.signal(signalInfo.signal, {
337
+ status: 'scheduled',
338
+ nextRefresh: new Date().toISOString()
339
+ });
340
+ }
349
341
  }
350
342
  ```
351
343
 
352
- ### The Power of Perspective-Oriented Agents
344
+ ### Trigger from Outside
353
345
 
354
- This approach enables agents that can:
346
+ ```ts
347
+ // External systems can trigger a single pipeline run
348
+ export async function triggerRefresh() {
349
+ const client = new MemFlow.Client({/*...*/});
355
350
 
356
- * **Question themselves** – different hooks challenge each other's assumptions
357
- * **Spawn specialized co-agents** – child workflows with their own entity types tackle specific domains
358
- * **Maintain rich context** – all perspectives contribute to a shared, evolving knowledge base
359
- * **Scale horizontally** – each perspective can run on different workers
360
- * **Learn continuously** – entity state accumulates insights across all interactions
361
- * **Self-reflect** – synthesis hooks analyze conflicting perspectives and improve decision-making
351
+ await client.workflow.hook({
352
+ workflowId: 'pipeline-123',
353
+ taskQueue: 'pipeline',
354
+ workflowName: 'runPipeline',
355
+ args: []
356
+ });
357
+ }
358
+ ```
362
359
 
363
- Because every perspective operates on the same durable entity, agents can pause, restart, and evolve their world-model indefinitely while maintaining coherent, multi-faceted intelligence.
360
+ > πŸ’‘ **Developer Note**: A complete implementation of this Pipeline example with OpenAI Vision integration, processing hooks, and document workflow automation can be found in the [test suite](./tests/memflow/pipeline).
364
361
 
365
362
  ---
366
363
 
367
- ## πŸ“š Documentation & Links
364
+ ## Documentation & Links
368
365
 
369
- * SDK API – [https://hotmeshio.github.io/sdk-typescript](https://hotmeshio.github.io/sdk-typescript)
370
- * Examples – [https://github.com/hotmeshio/samples-typescript](https://github.com/hotmeshio/samples-typescript)
366
+ * SDK Reference – [hotmeshio.github.io/sdk-typescript](https://hotmeshio.github.io/sdk-typescript)
367
+ * Examples – [github.com/hotmeshio/samples-typescript](https://github.com/hotmeshio/samples-typescript)
371
368
 
372
369
  ---
373
370
 
374
371
  ## License
375
372
 
376
- Apache 2.0 – see `LICENSE` for details.
373
+ Apache 2.0 – See `LICENSE` for details.
@@ -15,6 +15,7 @@ declare class MemFlowWaitForError extends Error {
15
15
  workflowId: string;
16
16
  index: number;
17
17
  workflowDimension: string;
18
+ type: string;
18
19
  constructor(params: MemFlowWaitForErrorType);
19
20
  }
20
21
  declare class MemFlowProxyError extends Error {
@@ -31,6 +32,7 @@ declare class MemFlowProxyError extends Error {
31
32
  workflowDimension: string;
32
33
  workflowId: string;
33
34
  workflowTopic: string;
35
+ type: string;
34
36
  constructor(params: MemFlowProxyErrorType);
35
37
  }
36
38
  declare class MemFlowChildError extends Error {
@@ -50,6 +52,7 @@ declare class MemFlowChildError extends Error {
50
52
  parentWorkflowId: string;
51
53
  workflowId: string;
52
54
  workflowTopic: string;
55
+ type: string;
53
56
  constructor(params: MemFlowChildErrorType);
54
57
  }
55
58
  declare class MemFlowWaitForAllError extends Error {
@@ -62,6 +65,7 @@ declare class MemFlowWaitForAllError extends Error {
62
65
  parentWorkflowId: string;
63
66
  workflowId: string;
64
67
  workflowTopic: string;
68
+ type: string;
65
69
  constructor(params: MemFlowWaitForAllErrorType);
66
70
  }
67
71
  declare class MemFlowSleepError extends Error {
@@ -70,22 +74,27 @@ declare class MemFlowSleepError extends Error {
70
74
  duration: number;
71
75
  index: number;
72
76
  workflowDimension: string;
77
+ type: string;
73
78
  constructor(params: MemFlowSleepErrorType);
74
79
  }
75
80
  declare class MemFlowTimeoutError extends Error {
76
81
  code: number;
82
+ type: string;
77
83
  constructor(message: string, stack?: string);
78
84
  }
79
85
  declare class MemFlowMaxedError extends Error {
80
86
  code: number;
87
+ type: string;
81
88
  constructor(message: string, stackTrace?: string);
82
89
  }
83
90
  declare class MemFlowFatalError extends Error {
84
91
  code: number;
92
+ type: string;
85
93
  constructor(message: string, stackTrace?: string);
86
94
  }
87
95
  declare class MemFlowRetryError extends Error {
88
96
  code: number;
97
+ type: string;
89
98
  constructor(message: string, stackTrace?: string);
90
99
  }
91
100
  declare class MapDataError extends Error {