@hotmeshio/hotmesh 0.4.3 → 0.5.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.
package/README.md CHANGED
@@ -211,15 +211,156 @@ async function externalHookExample() {
211
211
 
212
212
  ## 🤖 Building Durable AI Agents
213
213
 
214
- Permanent memory unlocks a straightforward pattern for agentic systems:
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:
215
215
 
216
- 1. **Planner workflow** sketches a task list, seeds entity state.
217
- 2. **Tool hooks** execute individual tasks, feeding intermediate results back into state.
218
- 3. **Reflector hook** periodically summarizes state into long-term memory embeddings.
219
- 4. **Supervisor workflow** monitors metrics stored in state and decides when to finish.
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
220
220
 
221
- Because every step is durable *and* shares the same knowledge object, agents can pause,
222
- restart, scale horizontally, and keep evolving their world-model indefinitely.
221
+ ### Example: A Research Agent with Multiple Perspectives
222
+
223
+ ```typescript
224
+ /* ------------ Main Research Agent ------------ */
225
+ export async function researchAgent(query: string): Promise<any> {
226
+ const entity = await MemFlow.workflow.entity();
227
+
228
+ // Initialize agent context
229
+ await entity.set({
230
+ query,
231
+ findings: [],
232
+ perspectives: {},
233
+ confidence: 0,
234
+ status: 'researching'
235
+ });
236
+
237
+ // Spawn perspective hooks that operate on shared context
238
+ const optimisticView = MemFlow.workflow.execHook({
239
+ taskQueue: 'perspectives',
240
+ workflowName: 'optimisticPerspective',
241
+ args: [query]
242
+ });
243
+
244
+ const skepticalView = MemFlow.workflow.execHook({
245
+ taskQueue: 'perspectives',
246
+ workflowName: 'skepticalPerspective',
247
+ args: [query]
248
+ });
249
+
250
+ // Spawn a fact-checker child agent with its own entity type
251
+ const factChecker = await MemFlow.workflow.execChild({
252
+ entity: 'fact-checker',
253
+ workflowName: 'factCheckAgent',
254
+ workflowId: `fact-check-${Date.now()}`,
255
+ args: [query],
256
+ taskQueue: 'agents'
257
+ });
258
+
259
+ // Wait for all perspectives to contribute
260
+ await Promise.all([optimisticView, skepticalView, factChecker]);
261
+
262
+ // Self-reflection: analyze conflicting perspectives
263
+ await MemFlow.workflow.execHook({
264
+ taskQueue: 'perspectives',
265
+ workflowName: 'synthesizePerspectives',
266
+ args: []
267
+ });
268
+
269
+ const finalContext = await entity.get();
270
+ return finalContext;
271
+ }
272
+
273
+ /* ------------ Optimistic Perspective Hook ------------ */
274
+ export async function optimisticPerspective(query: string): Promise<void> {
275
+ const entity = await MemFlow.workflow.entity();
276
+
277
+ // Optimistic perspective: look for supporting evidence
278
+ 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
+ });
289
+ }
290
+
291
+ /* ------------ Skeptical Perspective Hook ------------ */
292
+ export async function skepticalPerspective(query: string): Promise<void> {
293
+ const entity = await MemFlow.workflow.entity();
294
+
295
+ // Skeptical perspective: challenge assumptions
296
+ 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
+ });
307
+ }
308
+
309
+ /* ------------ Fact-Checker Child Agent ------------ */
310
+ export async function factCheckAgent(query: string): Promise<any> {
311
+ // This child has its own entity type for specialized fact-checking context
312
+ const entity = await MemFlow.workflow.entity();
313
+
314
+ await entity.set({
315
+ query,
316
+ sources: [],
317
+ verifications: [],
318
+ credibilityScore: 0
319
+ });
320
+
321
+ // Fact-checker can spawn its own specialized hooks
322
+ await MemFlow.workflow.execHook({
323
+ taskQueue: 'verification',
324
+ workflowName: 'verifySourceCredibility',
325
+ args: [query]
326
+ });
327
+
328
+ return await entity.get();
329
+ }
330
+
331
+ /* ------------ Synthesis Perspective Hook ------------ */
332
+ export async function synthesizePerspectives(): Promise<void> {
333
+ const entity = await MemFlow.workflow.entity();
334
+ const context = await entity.get();
335
+
336
+ // Analyze conflicting perspectives and synthesize
337
+ const synthesis = await analyzePerspectives(context.perspectives);
338
+
339
+ await entity.merge({
340
+ perspectives: {
341
+ synthesis: {
342
+ finalAssessment: synthesis,
343
+ confidence: calculateConfidence(context.perspectives),
344
+ reasoning: 'Balanced analysis of optimistic and skeptical viewpoints'
345
+ }
346
+ },
347
+ status: 'completed'
348
+ });
349
+ }
350
+ ```
351
+
352
+ ### The Power of Perspective-Oriented Agents
353
+
354
+ This approach enables agents that can:
355
+
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
362
+
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.
223
364
 
224
365
  ---
225
366
 
@@ -35,6 +35,7 @@ declare class MemFlowProxyError extends Error {
35
35
  }
36
36
  declare class MemFlowChildError extends Error {
37
37
  await: boolean;
38
+ entity: string;
38
39
  arguments: string[];
39
40
  backoffCoefficient: number;
40
41
  code: number;
@@ -56,6 +56,7 @@ class MemFlowChildError extends Error {
56
56
  this.persistent = params.persistent;
57
57
  this.signalIn = params.signalIn;
58
58
  this.originJobId = params.originJobId;
59
+ this.entity = params.entity;
59
60
  this.index = params.index;
60
61
  this.workflowDimension = params.workflowDimension;
61
62
  this.code = enums_1.HMSH_CODE_MEMFLOW_CHILD;
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Permanent-Memory Workflows & AI Agents",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
7
- "homepage": "https://hotmesh.io/",
7
+ "homepage": "https://github.com/hotmeshio/sdk-typescript/",
8
8
  "publishConfig": {
9
9
  "access": "public"
10
10
  },
@@ -31,7 +31,7 @@ declare class Trigger extends Activity {
31
31
  getJobStatus(): number;
32
32
  resolveJobId(context: Partial<JobState>): string;
33
33
  resolveJobKey(context: Partial<JobState>): string;
34
- setStateNX(status?: number, entity?: string): Promise<void>;
34
+ setStateNX(status?: number, entity?: string | undefined): Promise<void>;
35
35
  setStats(transaction?: ProviderTransaction): Promise<void>;
36
36
  }
37
37
  export { Trigger };
@@ -9,6 +9,7 @@ const reporter_1 = require("../reporter");
9
9
  const serializer_1 = require("../serializer");
10
10
  const telemetry_1 = require("../telemetry");
11
11
  const activity_1 = require("./activity");
12
+ const mapper_1 = require("../mapper");
12
13
  class Trigger extends activity_1.Activity {
13
14
  constructor(config, data, metadata, hook, engine, context) {
14
15
  super(config, data, metadata, hook, engine, context);
@@ -27,7 +28,9 @@ class Trigger extends activity_1.Activity {
27
28
  this.mapJobData();
28
29
  this.adjacencyList = await this.filterAdjacent();
29
30
  const initialStatus = this.initStatus(options, this.adjacencyList.length);
30
- await this.setStateNX(initialStatus, options?.entity);
31
+ //config.entity is a pipe expression; if 'entity' exists, it will resolve
32
+ const resolvedEntity = new mapper_1.MapperService({ entity: this.config.entity }, this.context).mapRules()?.entity;
33
+ await this.setStateNX(initialStatus, options?.entity || resolvedEntity);
31
34
  await this.setStatus(initialStatus);
32
35
  this.bindSearchData(options);
33
36
  this.bindMarkerData(options);
@@ -17,7 +17,7 @@
17
17
  * * Service Meshes
18
18
  * * Master Data Management systems
19
19
  */
20
- declare const APP_VERSION = "4";
20
+ declare const APP_VERSION = "5";
21
21
  declare const APP_ID = "memflow";
22
22
  /**
23
23
  * returns a new memflow workflow schema
@@ -20,7 +20,7 @@
20
20
  */
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.APP_ID = exports.APP_VERSION = exports.getWorkflowYAML = void 0;
23
- const APP_VERSION = '4';
23
+ const APP_VERSION = '5';
24
24
  exports.APP_VERSION = APP_VERSION;
25
25
  const APP_ID = 'memflow';
26
26
  exports.APP_ID = APP_ID;
@@ -86,6 +86,9 @@ const getWorkflowYAML = (app, version) => {
86
86
  signalIn:
87
87
  description: if false, the job will not support subordinated hooks
88
88
  type: boolean
89
+ entity:
90
+ description: the entity type for this workflow instance
91
+ type: string
89
92
 
90
93
  output:
91
94
  schema:
@@ -120,6 +123,7 @@ const getWorkflowYAML = (app, version) => {
120
123
  trigger:
121
124
  title: Main Flow Trigger
122
125
  type: trigger
126
+ entity: '{$self.input.data.entity}'
123
127
  job:
124
128
  maps:
125
129
  done: false
@@ -249,6 +253,9 @@ const getWorkflowYAML = (app, version) => {
249
253
  await:
250
254
  type: string
251
255
  description: when set to false, do not await the child flow's completion
256
+ entity:
257
+ type: string
258
+ description: the entity type for the child workflow
252
259
  591:
253
260
  schema:
254
261
  type: object
@@ -367,6 +374,9 @@ const getWorkflowYAML = (app, version) => {
367
374
  description: the arguments to pass to the activity
368
375
  items:
369
376
  type: string
377
+ entity:
378
+ type: string
379
+ description: the entity type for the child workflow
370
380
  maps:
371
381
  arguments: '{worker.output.data.arguments}'
372
382
  workflowDimension: '{worker.output.data.workflowDimension}'
@@ -379,6 +389,7 @@ const getWorkflowYAML = (app, version) => {
379
389
  workflowId: '{worker.output.data.workflowId}'
380
390
  workflowName: '{worker.output.data.workflowName}'
381
391
  workflowTopic: '{worker.output.data.workflowTopic}'
392
+ entity: '{worker.output.data.entity}'
382
393
  backoffCoefficient:
383
394
  '@pipe':
384
395
  - ['{worker.output.data.backoffCoefficient}','{trigger.output.data.backoffCoefficient}']
@@ -992,6 +1003,9 @@ const getWorkflowYAML = (app, version) => {
992
1003
  await:
993
1004
  type: string
994
1005
  description: when set to false, do not await the child flow's completion
1006
+ entity:
1007
+ type: string
1008
+ description: the entity type for the child workflow
995
1009
  591:
996
1010
  schema:
997
1011
  type: object
@@ -1109,6 +1123,9 @@ const getWorkflowYAML = (app, version) => {
1109
1123
  description: the arguments to pass to the activity
1110
1124
  items:
1111
1125
  type: string
1126
+ entity:
1127
+ type: string
1128
+ description: the entity type for the child workflow
1112
1129
  maps:
1113
1130
  arguments: '{signaler_worker.output.data.arguments}'
1114
1131
  workflowDimension: '{signaler_worker.output.data.workflowDimension}'
@@ -1121,6 +1138,7 @@ const getWorkflowYAML = (app, version) => {
1121
1138
  workflowId: '{signaler_worker.output.data.workflowId}'
1122
1139
  workflowName: '{signaler_worker.output.data.workflowName}'
1123
1140
  workflowTopic: '{signaler_worker.output.data.workflowTopic}'
1141
+ entity: '{signaler_worker.output.data.entity}'
1124
1142
  backoffCoefficient:
1125
1143
  '@pipe':
1126
1144
  - ['{signaler_worker.output.data.backoffCoefficient}','{trigger.output.data.backoffCoefficient}']
@@ -1875,6 +1893,9 @@ const getWorkflowYAML = (app, version) => {
1875
1893
  description: the arguments to pass to the activity
1876
1894
  items:
1877
1895
  type: string
1896
+ entity:
1897
+ type: string
1898
+ description: the entity type for the child workflow
1878
1899
  maps:
1879
1900
  arguments:
1880
1901
  '@pipe':
@@ -1946,6 +1967,11 @@ const getWorkflowYAML = (app, version) => {
1946
1967
  - ['{collator_trigger.output.data.items}', '{collator_cycle_hook.output.data.cur_index}']
1947
1968
  - ['{@array.get}', maximumInterval]
1948
1969
  - ['{@object.get}']
1970
+ entity:
1971
+ '@pipe':
1972
+ - ['{collator_trigger.output.data.items}', '{collator_cycle_hook.output.data.cur_index}']
1973
+ - ['{@array.get}', entity]
1974
+ - ['{@object.get}']
1949
1975
  output:
1950
1976
  schema:
1951
1977
  type: object
@@ -431,6 +431,7 @@ class WorkerService {
431
431
  maximumAttempts: err.maximumAttempts || enums_1.HMSH_MEMFLOW_MAX_ATTEMPTS,
432
432
  maximumInterval: err.maximumInterval || (0, utils_1.s)(enums_1.HMSH_MEMFLOW_MAX_INTERVAL),
433
433
  originJobId: err.originJobId,
434
+ entity: err.entity,
434
435
  parentWorkflowId: err.parentWorkflowId,
435
436
  expire: err.expire,
436
437
  persistent: err.persistent,
@@ -22,7 +22,7 @@ function getChildInterruptPayload(context, options, execIndex) {
22
22
  }
23
23
  const parentWorkflowId = workflowId;
24
24
  const taskQueueName = options.taskQueue ?? options.entity;
25
- const workflowName = options.entity ?? options.workflowName;
25
+ const workflowName = options.taskQueue ? options.workflowName : (options.entity ?? options.workflowName);
26
26
  const workflowTopic = `${taskQueueName}-${workflowName}`;
27
27
  return {
28
28
  arguments: [...(options.args || [])],
@@ -32,6 +32,7 @@ function getChildInterruptPayload(context, options, execIndex) {
32
32
  maximumAttempts: options?.config?.maximumAttempts ?? common_1.HMSH_MEMFLOW_MAX_ATTEMPTS,
33
33
  maximumInterval: (0, common_1.s)(options?.config?.maximumInterval ?? common_1.HMSH_MEMFLOW_MAX_INTERVAL),
34
34
  originJobId: originJobId ?? workflowId,
35
+ entity: options.entity,
35
36
  expire: options.expire ?? expire,
36
37
  persistent: options.persistent,
37
38
  signalIn: options.signalIn,
@@ -9,6 +9,7 @@ interface BaseActivity {
9
9
  statusThreshold?: number;
10
10
  statusThresholdType?: 'stop' | 'throw' | 'stall';
11
11
  input?: Record<string, any>;
12
+ entity?: string;
12
13
  output?: Record<string, any>;
13
14
  settings?: Record<string, any>;
14
15
  job?: Record<string, any>;
@@ -73,6 +74,7 @@ interface TriggerActivityStats {
73
74
  interface TriggerActivity extends BaseActivity {
74
75
  type: 'trigger';
75
76
  stats?: TriggerActivityStats;
77
+ entity?: string;
76
78
  }
77
79
  interface AwaitActivity extends BaseActivity {
78
80
  type: 'await';
@@ -6,6 +6,7 @@ export type MemFlowChildErrorType = {
6
6
  expire?: number;
7
7
  persistent?: boolean;
8
8
  signalIn?: boolean;
9
+ entity?: string;
9
10
  maximumAttempts?: number;
10
11
  maximumInterval?: number;
11
12
  originJobId: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "Permanent-Memory Workflows & AI Agents",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",