@loopstack/meeting-notes-example-workflow 0.20.7 → 0.21.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
@@ -87,17 +87,17 @@ Add action buttons to documents that trigger transitions. These are defined in t
87
87
  # meeting-notes-document.yaml
88
88
  type: document
89
89
  ui:
90
- form:
91
- properties:
92
- text:
93
- title: Text
94
- widget: textarea
95
- actions:
96
- - type: button
97
- widget: button
98
- transition: user_response
90
+ widgets:
91
+ - widget: form
99
92
  options:
100
- label: 'Optimize Notes'
93
+ properties:
94
+ text:
95
+ title: Text
96
+ widget: textarea
97
+ actions:
98
+ - type: button
99
+ transition: user_response
100
+ label: 'Optimize Notes'
101
101
  ```
102
102
 
103
103
  When clicked, the button triggers the `user_response` transition with the current document content.
@@ -135,7 +135,7 @@ export const MeetingNotesDocumentSchema = z.object({
135
135
  });
136
136
 
137
137
  @Document({
138
- configFile: __dirname + '/meeting-notes-document.yaml',
138
+ uiConfig: __dirname + '/meeting-notes-document.yaml',
139
139
  })
140
140
  export class MeetingNotesDocument implements DocumentInterface {
141
141
  @Input({
@@ -167,35 +167,35 @@ Configure the document UI with ordering, collapsible arrays, and confirm button:
167
167
  # optimized-notes-document.yaml
168
168
  type: document
169
169
  ui:
170
- form:
171
- order:
172
- - date
173
- - summary
174
- - participants
175
- - decisions
176
- - actionItems
177
- properties:
178
- date:
179
- title: Date
180
- summary:
181
- title: Summary
182
- widget: textarea
183
- participants:
184
- title: Participants
185
- collapsed: true
186
- items:
187
- title: Participant
188
- actionItems:
189
- title: Action Items
190
- collapsed: true
191
- items:
192
- title: Action Item
193
- actions:
194
- - type: button
195
- widget: button
196
- transition: confirm
170
+ widgets:
171
+ - widget: form
197
172
  options:
198
- label: 'Confirm'
173
+ order:
174
+ - date
175
+ - summary
176
+ - participants
177
+ - decisions
178
+ - actionItems
179
+ properties:
180
+ date:
181
+ title: Date
182
+ summary:
183
+ title: Summary
184
+ widget: textarea
185
+ participants:
186
+ title: Participants
187
+ collapsed: true
188
+ items:
189
+ title: Participant
190
+ actionItems:
191
+ title: Action Items
192
+ collapsed: true
193
+ items:
194
+ title: Action Item
195
+ actions:
196
+ - type: button
197
+ transition: confirm
198
+ label: 'Confirm'
199
199
  ```
200
200
 
201
201
  #### 7. AI Document Generation
@@ -1,11 +1,8 @@
1
1
  import { z } from 'zod';
2
- import { DocumentInterface } from '@loopstack/common';
3
2
  export declare const MeetingNotesDocumentSchema: z.ZodObject<{
4
3
  text: z.ZodString;
5
4
  }, z.core.$strip>;
6
- export declare class MeetingNotesDocument implements DocumentInterface {
7
- content: {
8
- text: string;
9
- };
5
+ export declare class MeetingNotesDocument {
6
+ text: string;
10
7
  }
11
8
  //# sourceMappingURL=meeting-notes-document.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"meeting-notes-document.d.ts","sourceRoot":"","sources":["../../src/documents/meeting-notes-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAY,iBAAiB,EAAS,MAAM,mBAAmB,CAAC;AAEvE,eAAO,MAAM,0BAA0B;;iBAErC,CAAC;AAEH,qBAGa,oBAAqB,YAAW,iBAAiB;IAI5D,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH"}
1
+ {"version":3,"file":"meeting-notes-document.d.ts","sourceRoot":"","sources":["../../src/documents/meeting-notes-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,0BAA0B;;iBAErC,CAAC;AAEH,qBAIa,oBAAoB;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd"}
@@ -5,9 +5,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
7
  };
8
- var __metadata = (this && this.__metadata) || function (k, v) {
9
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
- };
11
8
  Object.defineProperty(exports, "__esModule", { value: true });
12
9
  exports.MeetingNotesDocument = exports.MeetingNotesDocumentSchema = void 0;
13
10
  const zod_1 = require("zod");
@@ -16,18 +13,13 @@ exports.MeetingNotesDocumentSchema = zod_1.z.object({
16
13
  text: zod_1.z.string(),
17
14
  });
18
15
  let MeetingNotesDocument = class MeetingNotesDocument {
19
- content;
16
+ text;
20
17
  };
21
18
  exports.MeetingNotesDocument = MeetingNotesDocument;
22
- __decorate([
23
- (0, common_1.Input)({
24
- schema: exports.MeetingNotesDocumentSchema,
25
- }),
26
- __metadata("design:type", Object)
27
- ], MeetingNotesDocument.prototype, "content", void 0);
28
19
  exports.MeetingNotesDocument = MeetingNotesDocument = __decorate([
29
20
  (0, common_1.Document)({
30
- configFile: __dirname + '/meeting-notes-document.yaml',
21
+ schema: exports.MeetingNotesDocumentSchema,
22
+ uiConfig: __dirname + '/meeting-notes-document.yaml',
31
23
  })
32
24
  ], MeetingNotesDocument);
33
25
  //# sourceMappingURL=meeting-notes-document.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"meeting-notes-document.js","sourceRoot":"","sources":["../../src/documents/meeting-notes-document.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6BAAwB;AACxB,8CAAuE;AAE1D,QAAA,0BAA0B,GAAG,OAAC,CAAC,MAAM,CAAC;IACjD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAC;AAKI,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAI/B,OAAO,CAEL;CACH,CAAA;AAPY,oDAAoB;AAI/B;IAHC,IAAA,cAAK,EAAC;QACL,MAAM,EAAE,kCAA0B;KACnC,CAAC;;qDAGA;+BANS,oBAAoB;IAHhC,IAAA,iBAAQ,EAAC;QACR,UAAU,EAAE,SAAS,GAAG,8BAA8B;KACvD,CAAC;GACW,oBAAoB,CAOhC"}
1
+ {"version":3,"file":"meeting-notes-document.js","sourceRoot":"","sources":["../../src/documents/meeting-notes-document.ts"],"names":[],"mappings":";;;;;;;;;AAAA,6BAAwB;AACxB,8CAA6C;AAEhC,QAAA,0BAA0B,GAAG,OAAC,CAAC,MAAM,CAAC;IACjD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAC;AAMI,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAC/B,IAAI,CAAS;CACd,CAAA;AAFY,oDAAoB;+BAApB,oBAAoB;IAJhC,IAAA,iBAAQ,EAAC;QACR,MAAM,EAAE,kCAA0B;QAClC,QAAQ,EAAE,SAAS,GAAG,8BAA8B;KACrD,CAAC;GACW,oBAAoB,CAEhC"}
@@ -1,13 +1,13 @@
1
1
  type: document
2
2
  ui:
3
- form:
4
- properties:
5
- text:
6
- title: Text
7
- widget: textarea
8
- actions:
9
- - type: button
10
- widget: button
3
+ widgets:
4
+ - widget: form
11
5
  options:
12
- transition: user_response
13
- label: 'Optimize Notes'
6
+ properties:
7
+ text:
8
+ title: Text
9
+ widget: textarea
10
+ actions:
11
+ - type: button
12
+ transition: userResponse
13
+ label: 'Optimize Notes'
@@ -1,5 +1,4 @@
1
1
  import { z } from 'zod';
2
- import { DocumentInterface } from '@loopstack/common';
3
2
  export declare const OptimizedMeetingNotesDocumentSchema: z.ZodObject<{
4
3
  date: z.ZodString;
5
4
  summary: z.ZodString;
@@ -7,7 +6,11 @@ export declare const OptimizedMeetingNotesDocumentSchema: z.ZodObject<{
7
6
  decisions: z.ZodArray<z.ZodString>;
8
7
  actionItems: z.ZodArray<z.ZodString>;
9
8
  }, z.core.$strip>;
10
- export declare class OptimizedNotesDocument implements DocumentInterface {
11
- content: z.infer<typeof OptimizedMeetingNotesDocumentSchema>;
9
+ export declare class OptimizedNotesDocument {
10
+ date: string;
11
+ summary: string;
12
+ participants: string[];
13
+ decisions: string[];
14
+ actionItems: string[];
12
15
  }
13
16
  //# sourceMappingURL=optimized-notes-document.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"optimized-notes-document.d.ts","sourceRoot":"","sources":["../../src/documents/optimized-notes-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAY,iBAAiB,EAAS,MAAM,mBAAmB,CAAC;AAEvE,eAAO,MAAM,mCAAmC;;;;;;iBAM9C,CAAC;AAEH,qBAGa,sBAAuB,YAAW,iBAAiB;IAI9D,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC;CAC9D"}
1
+ {"version":3,"file":"optimized-notes-document.d.ts","sourceRoot":"","sources":["../../src/documents/optimized-notes-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,mCAAmC;;;;;;iBAM9C,CAAC;AAEH,qBAIa,sBAAsB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB"}
@@ -5,9 +5,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
7
  };
8
- var __metadata = (this && this.__metadata) || function (k, v) {
9
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
- };
11
8
  Object.defineProperty(exports, "__esModule", { value: true });
12
9
  exports.OptimizedNotesDocument = exports.OptimizedMeetingNotesDocumentSchema = void 0;
13
10
  const zod_1 = require("zod");
@@ -20,18 +17,17 @@ exports.OptimizedMeetingNotesDocumentSchema = zod_1.z.object({
20
17
  actionItems: zod_1.z.array(zod_1.z.string()),
21
18
  });
22
19
  let OptimizedNotesDocument = class OptimizedNotesDocument {
23
- content;
20
+ date;
21
+ summary;
22
+ participants;
23
+ decisions;
24
+ actionItems;
24
25
  };
25
26
  exports.OptimizedNotesDocument = OptimizedNotesDocument;
26
- __decorate([
27
- (0, common_1.Input)({
28
- schema: exports.OptimizedMeetingNotesDocumentSchema,
29
- }),
30
- __metadata("design:type", Object)
31
- ], OptimizedNotesDocument.prototype, "content", void 0);
32
27
  exports.OptimizedNotesDocument = OptimizedNotesDocument = __decorate([
33
28
  (0, common_1.Document)({
34
- configFile: __dirname + '/optimized-notes-document.yaml',
29
+ schema: exports.OptimizedMeetingNotesDocumentSchema,
30
+ uiConfig: __dirname + '/optimized-notes-document.yaml',
35
31
  })
36
32
  ], OptimizedNotesDocument);
37
33
  //# sourceMappingURL=optimized-notes-document.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"optimized-notes-document.js","sourceRoot":"","sources":["../../src/documents/optimized-notes-document.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6BAAwB;AACxB,8CAAuE;AAE1D,QAAA,mCAAmC,GAAG,OAAC,CAAC,MAAM,CAAC;IAC1D,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE;IACnB,YAAY,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC;IACjC,SAAS,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC;IAC9B,WAAW,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC;CACjC,CAAC,CAAC;AAKI,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IAIjC,OAAO,CAAsD;CAC9D,CAAA;AALY,wDAAsB;AAIjC;IAHC,IAAA,cAAK,EAAC;QACL,MAAM,EAAE,2CAAmC;KAC5C,CAAC;;uDAC2D;iCAJlD,sBAAsB;IAHlC,IAAA,iBAAQ,EAAC;QACR,UAAU,EAAE,SAAS,GAAG,gCAAgC;KACzD,CAAC;GACW,sBAAsB,CAKlC"}
1
+ {"version":3,"file":"optimized-notes-document.js","sourceRoot":"","sources":["../../src/documents/optimized-notes-document.ts"],"names":[],"mappings":";;;;;;;;;AAAA,6BAAwB;AACxB,8CAA6C;AAEhC,QAAA,mCAAmC,GAAG,OAAC,CAAC,MAAM,CAAC;IAC1D,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE;IACnB,YAAY,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC;IACjC,SAAS,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC;IAC9B,WAAW,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC;CACjC,CAAC,CAAC;AAMI,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IACjC,IAAI,CAAS;IACb,OAAO,CAAS;IAChB,YAAY,CAAW;IACvB,SAAS,CAAW;IACpB,WAAW,CAAW;CACvB,CAAA;AANY,wDAAsB;iCAAtB,sBAAsB;IAJlC,IAAA,iBAAQ,EAAC;QACR,MAAM,EAAE,2CAAmC;QAC3C,QAAQ,EAAE,SAAS,GAAG,gCAAgC;KACvD,CAAC;GACW,sBAAsB,CAMlC"}
@@ -1,36 +1,36 @@
1
1
  type: document
2
2
  ui:
3
- form:
4
- order:
5
- - date
6
- - summary
7
- - participants
8
- - decisions
9
- - actionItems
10
- properties:
11
- date:
12
- title: Date
13
- summary:
14
- title: Summary
15
- widget: textarea
16
- participants:
17
- title: Participants
18
- collapsed: true
19
- items:
20
- title: Participant
21
- decisions:
22
- title: Decisions
23
- collapsed: true
24
- items:
25
- title: Decision
26
- actionItems:
27
- title: Action Items
28
- collapsed: true
29
- items:
30
- title: Action Item
31
- actions:
32
- - type: button
33
- widget: button
3
+ widgets:
4
+ - widget: form
34
5
  options:
35
- transition: confirm
36
- label: 'Confirm'
6
+ order:
7
+ - date
8
+ - summary
9
+ - participants
10
+ - decisions
11
+ - actionItems
12
+ properties:
13
+ date:
14
+ title: Date
15
+ summary:
16
+ title: Summary
17
+ widget: textarea
18
+ participants:
19
+ title: Participants
20
+ collapsed: true
21
+ items:
22
+ title: Participant
23
+ decisions:
24
+ title: Decisions
25
+ collapsed: true
26
+ items:
27
+ title: Decision
28
+ actionItems:
29
+ title: Action Items
30
+ collapsed: true
31
+ items:
32
+ title: Action Item
33
+ actions:
34
+ - type: button
35
+ transition: confirm
36
+ label: 'Confirm'
@@ -0,0 +1,10 @@
1
+ title: 'Human-in-the-loop Demo (Meeting Notes Optimizer)'
2
+
3
+ description: 'A demo workflow to demonstrate how to use AI to structure meeting notes.'
4
+
5
+ ui:
6
+ form:
7
+ properties:
8
+ inputText:
9
+ title: 'Text'
10
+ widget: 'textarea'
@@ -1,20 +1,19 @@
1
1
  import { z } from 'zod';
2
2
  import { ClaudeGenerateDocument } from '@loopstack/claude-module';
3
- import { CreateDocument } from '@loopstack/core';
4
- import { MeetingNotesDocument, MeetingNotesDocumentSchema } from './documents/meeting-notes-document';
5
- import { OptimizedMeetingNotesDocumentSchema, OptimizedNotesDocument } from './documents/optimized-notes-document';
6
- export declare class MeetingNotesWorkflow {
3
+ import { BaseWorkflow } from '@loopstack/common';
4
+ import { MeetingNotesDocumentSchema } from './documents/meeting-notes-document';
5
+ import { OptimizedMeetingNotesDocumentSchema } from './documents/optimized-notes-document';
6
+ export declare class MeetingNotesWorkflow extends BaseWorkflow<{
7
+ inputText: string;
8
+ }> {
7
9
  claudeGenerateDocument: ClaudeGenerateDocument;
8
- createDocument: CreateDocument;
9
- meetingNotesDocument: MeetingNotesDocument;
10
- optimizedNotesDocument: OptimizedNotesDocument;
11
- args: {
10
+ meetingNotes?: z.infer<typeof MeetingNotesDocumentSchema>;
11
+ optimizedNotes?: z.infer<typeof OptimizedMeetingNotesDocumentSchema>;
12
+ createForm(args: {
12
13
  inputText: string;
13
- };
14
- state: {
15
- meetingNotes?: z.infer<typeof MeetingNotesDocumentSchema>;
16
- optimizedNotes?: z.infer<typeof OptimizedMeetingNotesDocumentSchema>;
17
- };
18
- runtime: any;
14
+ }): Promise<void>;
15
+ userResponse(payload: z.infer<typeof MeetingNotesDocumentSchema>): Promise<void>;
16
+ optimizeNotes(): Promise<void>;
17
+ confirm(payload: z.infer<typeof OptimizedMeetingNotesDocumentSchema>): Promise<void>;
19
18
  }
20
19
  //# sourceMappingURL=meeting-notes.workflow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"meeting-notes.workflow.d.ts","sourceRoot":"","sources":["../src/meeting-notes.workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACtG,OAAO,EAAE,mCAAmC,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAEnH,qBAGa,oBAAoB;IACjB,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,cAAc,EAAE,cAAc,CAAC;IAC3B,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,sBAAsB,EAAE,sBAAsB,CAAC;IAWjE,IAAI,EAAE;QACJ,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAQF,KAAK,EAAE;QACL,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;QAC1D,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC;KACtE,CAAC;IAGF,OAAO,EAAE,GAAG,CAAC;CACd"}
1
+ {"version":3,"file":"meeting-notes.workflow.d.ts","sourceRoot":"","sources":["../src/meeting-notes.workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAoD,MAAM,mBAAmB,CAAC;AACnG,OAAO,EAAwB,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACtG,OAAO,EAAE,mCAAmC,EAA0B,MAAM,sCAAsC,CAAC;AAEnH,qBAUa,oBAAqB,SAAQ,YAAY,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;IAC7D,sBAAsB,EAAE,sBAAsB,CAAC;IAE7D,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;IAC1D,cAAc,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC;IAG/D,UAAU,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;IAWtC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC;IAMhE,aAAa;IAgBb,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC;CAI3E"}
@@ -13,17 +13,39 @@ exports.MeetingNotesWorkflow = void 0;
13
13
  const zod_1 = require("zod");
14
14
  const claude_module_1 = require("@loopstack/claude-module");
15
15
  const common_1 = require("@loopstack/common");
16
- const core_1 = require("@loopstack/core");
17
16
  const meeting_notes_document_1 = require("./documents/meeting-notes-document");
18
17
  const optimized_notes_document_1 = require("./documents/optimized-notes-document");
19
- let MeetingNotesWorkflow = class MeetingNotesWorkflow {
18
+ let MeetingNotesWorkflow = class MeetingNotesWorkflow extends common_1.BaseWorkflow {
20
19
  claudeGenerateDocument;
21
- createDocument;
22
- meetingNotesDocument;
23
- optimizedNotesDocument;
24
- args;
25
- state;
26
- runtime;
20
+ meetingNotes;
21
+ optimizedNotes;
22
+ async createForm(args) {
23
+ await this.repository.save(meeting_notes_document_1.MeetingNotesDocument, {
24
+ text: `Unstructured Notes:\n\n${args.inputText}`,
25
+ }, { id: 'input' });
26
+ }
27
+ async userResponse(payload) {
28
+ const result = await this.repository.save(meeting_notes_document_1.MeetingNotesDocument, payload, { id: 'input' });
29
+ this.meetingNotes = result.content;
30
+ }
31
+ async optimizeNotes() {
32
+ await this.claudeGenerateDocument.call({
33
+ claude: { model: 'claude-sonnet-4-6' },
34
+ response: {
35
+ id: 'final',
36
+ document: optimized_notes_document_1.OptimizedNotesDocument,
37
+ },
38
+ prompt: `Extract all information from the provided meeting notes into the structured document.
39
+
40
+ <Meeting Notes>
41
+ ${this.meetingNotes?.text}
42
+ </Meeting Notes>`,
43
+ });
44
+ }
45
+ async confirm(payload) {
46
+ const result = await this.repository.save(optimized_notes_document_1.OptimizedNotesDocument, payload, { id: 'final' });
47
+ this.optimizedNotes = result.content;
48
+ }
27
49
  };
28
50
  exports.MeetingNotesWorkflow = MeetingNotesWorkflow;
29
51
  __decorate([
@@ -31,43 +53,37 @@ __decorate([
31
53
  __metadata("design:type", claude_module_1.ClaudeGenerateDocument)
32
54
  ], MeetingNotesWorkflow.prototype, "claudeGenerateDocument", void 0);
33
55
  __decorate([
34
- (0, common_1.InjectTool)(),
35
- __metadata("design:type", core_1.CreateDocument)
36
- ], MeetingNotesWorkflow.prototype, "createDocument", void 0);
56
+ (0, common_1.Initial)({ to: 'waiting_for_response' }),
57
+ __metadata("design:type", Function),
58
+ __metadata("design:paramtypes", [Object]),
59
+ __metadata("design:returntype", Promise)
60
+ ], MeetingNotesWorkflow.prototype, "createForm", null);
37
61
  __decorate([
38
- (0, common_1.InjectDocument)(),
39
- __metadata("design:type", meeting_notes_document_1.MeetingNotesDocument)
40
- ], MeetingNotesWorkflow.prototype, "meetingNotesDocument", void 0);
62
+ (0, common_1.Transition)({ from: 'waiting_for_response', to: 'response_received', wait: true, schema: meeting_notes_document_1.MeetingNotesDocumentSchema }),
63
+ __metadata("design:type", Function),
64
+ __metadata("design:paramtypes", [Object]),
65
+ __metadata("design:returntype", Promise)
66
+ ], MeetingNotesWorkflow.prototype, "userResponse", null);
41
67
  __decorate([
42
- (0, common_1.InjectDocument)(),
43
- __metadata("design:type", optimized_notes_document_1.OptimizedNotesDocument)
44
- ], MeetingNotesWorkflow.prototype, "optimizedNotesDocument", void 0);
68
+ (0, common_1.Transition)({ from: 'response_received', to: 'notes_optimized' }),
69
+ __metadata("design:type", Function),
70
+ __metadata("design:paramtypes", []),
71
+ __metadata("design:returntype", Promise)
72
+ ], MeetingNotesWorkflow.prototype, "optimizeNotes", null);
45
73
  __decorate([
46
- (0, common_1.Input)({
74
+ (0, common_1.Final)({ from: 'notes_optimized', wait: true, schema: optimized_notes_document_1.OptimizedMeetingNotesDocumentSchema }),
75
+ __metadata("design:type", Function),
76
+ __metadata("design:paramtypes", [Object]),
77
+ __metadata("design:returntype", Promise)
78
+ ], MeetingNotesWorkflow.prototype, "confirm", null);
79
+ exports.MeetingNotesWorkflow = MeetingNotesWorkflow = __decorate([
80
+ (0, common_1.Workflow)({
81
+ uiConfig: __dirname + '/meeting-notes.ui.yaml',
47
82
  schema: zod_1.z.object({
48
83
  inputText: zod_1.z
49
84
  .string()
50
85
  .default('- meeting 1.1.2025\n- budget: need 2 cut costs sarah said\n- hire new person?? --> marketing\n- vendor pricing - follow up needed by anna'),
51
86
  }),
52
- }),
53
- __metadata("design:type", Object)
54
- ], MeetingNotesWorkflow.prototype, "args", void 0);
55
- __decorate([
56
- (0, common_1.State)({
57
- schema: zod_1.z.object({
58
- meetingNotes: meeting_notes_document_1.MeetingNotesDocumentSchema.optional(),
59
- optimizedNotes: optimized_notes_document_1.OptimizedMeetingNotesDocumentSchema.optional(),
60
- }),
61
- }),
62
- __metadata("design:type", Object)
63
- ], MeetingNotesWorkflow.prototype, "state", void 0);
64
- __decorate([
65
- (0, common_1.Runtime)(),
66
- __metadata("design:type", Object)
67
- ], MeetingNotesWorkflow.prototype, "runtime", void 0);
68
- exports.MeetingNotesWorkflow = MeetingNotesWorkflow = __decorate([
69
- (0, common_1.Workflow)({
70
- configFile: __dirname + '/meeting-notes.workflow.yaml',
71
87
  })
72
88
  ], MeetingNotesWorkflow);
73
89
  //# sourceMappingURL=meeting-notes.workflow.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"meeting-notes.workflow.js","sourceRoot":"","sources":["../src/meeting-notes.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6BAAwB;AACxB,4DAAkE;AAClE,8CAAgG;AAChG,0CAAiD;AACjD,+EAAsG;AACtG,mFAAmH;AAK5G,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IACjB,sBAAsB,CAAyB;IAC/C,cAAc,CAAiB;IAC3B,oBAAoB,CAAuB;IAC3C,sBAAsB,CAAyB;IAWjE,IAAI,CAEF;IAQF,KAAK,CAGH;IAGF,OAAO,CAAM;CACd,CAAA;AAhCY,oDAAoB;AACjB;IAAb,IAAA,mBAAU,GAAE;8BAAyB,sCAAsB;oEAAC;AAC/C;IAAb,IAAA,mBAAU,GAAE;8BAAiB,qBAAc;4DAAC;AAC3B;IAAjB,IAAA,uBAAc,GAAE;8BAAuB,6CAAoB;kEAAC;AAC3C;IAAjB,IAAA,uBAAc,GAAE;8BAAyB,iDAAsB;oEAAC;AAWjE;IATC,IAAA,cAAK,EAAC;QACL,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC;YACf,SAAS,EAAE,OAAC;iBACT,MAAM,EAAE;iBACR,OAAO,CACN,2IAA2I,CAC5I;SACJ,CAAC;KACH,CAAC;;kDAGA;AAQF;IANC,IAAA,cAAK,EAAC;QACL,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC;YACf,YAAY,EAAE,mDAA0B,CAAC,QAAQ,EAAE;YACnD,cAAc,EAAE,8DAAmC,CAAC,QAAQ,EAAE;SAC/D,CAAC;KACH,CAAC;;mDAIA;AAGF;IADC,IAAA,gBAAO,GAAE;;qDACG;+BA/BF,oBAAoB;IAHhC,IAAA,iBAAQ,EAAC;QACR,UAAU,EAAE,SAAS,GAAG,8BAA8B;KACvD,CAAC;GACW,oBAAoB,CAgChC"}
1
+ {"version":3,"file":"meeting-notes.workflow.js","sourceRoot":"","sources":["../src/meeting-notes.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6BAAwB;AACxB,4DAAkE;AAClE,8CAAmG;AACnG,+EAAsG;AACtG,mFAAmH;AAY5G,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,qBAAmC;IAC7D,sBAAsB,CAAyB;IAE7D,YAAY,CAA8C;IAC1D,cAAc,CAAuD;IAG/D,AAAN,KAAK,CAAC,UAAU,CAAC,IAA2B;QAC1C,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CACxB,6CAAoB,EACpB;YACE,IAAI,EAAE,0BAA0B,IAAI,CAAC,SAAS,EAAE;SACjD,EACD,EAAE,EAAE,EAAE,OAAO,EAAE,CAChB,CAAC;IACJ,CAAC;IAGK,AAAN,KAAK,CAAC,YAAY,CAAC,OAAmD;QACpE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,6CAAoB,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1F,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,OAAqD,CAAC;IACnF,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa;QACjB,MAAM,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC;YACrC,MAAM,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE;YACtC,QAAQ,EAAE;gBACR,EAAE,EAAE,OAAO;gBACX,QAAQ,EAAE,iDAAsB;aACjC;YACD,MAAM,EAAE;;;EAGZ,IAAI,CAAC,YAAY,EAAE,IAAI;iBACR;SACZ,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,OAAO,CAAC,OAA4D;QACxE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,iDAAsB,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QAC5F,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,OAA8D,CAAC;IAC9F,CAAC;CACF,CAAA;AA5CY,oDAAoB;AACjB;IAAb,IAAA,mBAAU,GAAE;8BAAyB,sCAAsB;oEAAC;AAMvD;IADL,IAAA,gBAAO,EAAC,EAAE,EAAE,EAAE,sBAAsB,EAAE,CAAC;;;;sDASvC;AAGK;IADL,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,EAAE,EAAE,mBAAmB,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,mDAA0B,EAAE,CAAC;;;;wDAIrH;AAGK;IADL,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC;;;;yDAchE;AAGK;IADL,IAAA,cAAK,EAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,8DAAmC,EAAE,CAAC;;;;mDAI3F;+BA3CU,oBAAoB;IAVhC,IAAA,iBAAQ,EAAC;QACR,QAAQ,EAAE,SAAS,GAAG,wBAAwB;QAC9C,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC;YACf,SAAS,EAAE,OAAC;iBACT,MAAM,EAAE;iBACR,OAAO,CACN,2IAA2I,CAC5I;SACJ,CAAC;KACH,CAAC;GACW,oBAAoB,CA4ChC"}
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "summary",
10
10
  "workflow"
11
11
  ],
12
- "version": "0.20.7",
12
+ "version": "0.21.0",
13
13
  "license": "Apache-2.0",
14
14
  "author": {
15
15
  "name": "Jakob Klippel",
@@ -30,9 +30,9 @@
30
30
  "watch": "nest build --watch"
31
31
  },
32
32
  "dependencies": {
33
- "@loopstack/claude-module": "^0.21.1",
34
- "@loopstack/common": "^0.24.0",
35
- "@loopstack/core": "^0.24.0",
33
+ "@loopstack/claude-module": "^0.22.0",
34
+ "@loopstack/common": "^0.25.0",
35
+ "@loopstack/core": "^0.25.0",
36
36
  "@nestjs/common": "^11.1.14",
37
37
  "zod": "^4.3.6"
38
38
  },
@@ -1,8 +1,8 @@
1
1
  import { TestingModule } from '@nestjs/testing';
2
2
  import { ClaudeGenerateDocument, ClaudeModule } from '@loopstack/claude-module';
3
- import { RunContext, generateObjectFingerprint, getBlockTools } from '@loopstack/common';
4
- import { CreateDocument, LoopCoreModule, WorkflowProcessorService } from '@loopstack/core';
5
- import { ToolMock, createWorkflowTest } from '@loopstack/testing';
3
+ import { RunContext, WorkflowEntity, getBlockTools } from '@loopstack/common';
4
+ import { LoopCoreModule, WorkflowProcessorService } from '@loopstack/core';
5
+ import { ToolMock, createStatelessContext, createWorkflowTest } from '@loopstack/testing';
6
6
  import { MeetingNotesDocument } from '../documents/meeting-notes-document';
7
7
  import { OptimizedNotesDocument } from '../documents/optimized-notes-document';
8
8
  import { MeetingNotesWorkflow } from '../meeting-notes.workflow';
@@ -12,15 +12,7 @@ describe('MeetingNotesWorkflow', () => {
12
12
  let workflow: MeetingNotesWorkflow;
13
13
  let processor: WorkflowProcessorService;
14
14
 
15
- let mockCreateDocument: ToolMock;
16
-
17
- const mockInitialNotes = {
18
- text: `
19
- - meeting 1.1.2025
20
- - budget: need 2 cut costs sarah said
21
- - hire new person?? --> marketing
22
- - vendor pricing - follow up needed by anna`,
23
- };
15
+ let mockClaudeGenerateDocument: ToolMock;
24
16
 
25
17
  beforeEach(async () => {
26
18
  module = await createWorkflowTest()
@@ -28,14 +20,13 @@ describe('MeetingNotesWorkflow', () => {
28
20
  .withImports(LoopCoreModule, ClaudeModule)
29
21
  .withProvider(MeetingNotesDocument)
30
22
  .withProvider(OptimizedNotesDocument)
31
- .withToolOverride(CreateDocument)
32
23
  .withToolOverride(ClaudeGenerateDocument)
33
24
  .compile();
34
25
 
35
26
  workflow = module.get(MeetingNotesWorkflow);
36
27
  processor = module.get(WorkflowProcessorService);
37
28
 
38
- mockCreateDocument = module.get(CreateDocument);
29
+ mockClaudeGenerateDocument = module.get(ClaudeGenerateDocument);
39
30
  });
40
31
 
41
32
  afterEach(async () => {
@@ -45,151 +36,103 @@ describe('MeetingNotesWorkflow', () => {
45
36
  describe('initialization', () => {
46
37
  it('should be defined with correct tools', () => {
47
38
  expect(workflow).toBeDefined();
48
- expect(getBlockTools(workflow)).toContain('createDocument');
49
39
  expect(getBlockTools(workflow)).toContain('claudeGenerateDocument');
50
40
  });
51
41
  });
52
42
 
53
43
  describe('initial step', () => {
54
- const context = {} as RunContext;
55
-
56
44
  it('should execute initial step and stop at waiting_for_response', async () => {
57
- mockCreateDocument.execute.mockResolvedValue({
58
- data: { content: mockInitialNotes },
59
- });
45
+ const context = createStatelessContext();
60
46
 
61
47
  const result = await processor.process(workflow, {}, context);
62
48
 
63
- // Should execute without errors and stop at waiting_for_response (manual step)
64
49
  expect(result.hasError).toBe(false);
65
50
  expect(result.stop).toBe(true);
51
+ expect(result.place).toBe('waiting_for_response');
66
52
 
67
- // Should call CreateDocument once for the initial form
68
- expect(mockCreateDocument.execute).toHaveBeenCalledTimes(1);
69
- expect(mockCreateDocument.execute).toHaveBeenCalledWith(
53
+ expect(result.documents).toHaveLength(1);
54
+ expect(result.documents[0]).toEqual(
70
55
  expect.objectContaining({
71
- id: 'input',
72
- update: {
73
- content: {
74
- text: expect.stringContaining('1.1.2025'),
75
- },
76
- },
56
+ className: 'MeetingNotesDocument',
57
+ content: expect.objectContaining({
58
+ text: expect.stringContaining('1.1.2025'),
59
+ }),
77
60
  }),
78
- expect.anything(),
79
- expect.anything(),
80
- expect.anything(),
81
61
  );
82
-
83
- // // Verify history contains expected places
84
- // const history = result.state.getHistory();
85
- // const places = history.map((h) => h.metadata?.place);
86
- // expect(places).toContain('waiting_for_response');
87
62
  });
88
- });
89
63
 
90
- describe('user response step', () => {
91
- it('should process user response and generate optimized notes', async () => {
92
- const mockUserEditedNotes = {
93
- text: `Meeting Notes - January 1, 2025
94
- - Budget discussion: need to cut costs (Sarah's input)
95
- - Hiring: new person needed for marketing
96
- - Vendor pricing: follow up needed by Anna`,
97
- };
64
+ it('should use custom input text when provided', async () => {
65
+ const context = createStatelessContext();
98
66
 
99
- const mockOptimizedNotes = {
100
- date: '2025-01-01',
101
- summary: 'Budget and hiring discussion',
102
- participants: ['Sarah', 'Anna'],
103
- decisions: ['Cut costs', 'Hire for marketing'],
104
- actionItems: ['Follow up on vendor pricing'],
105
- };
67
+ const result = await processor.process(workflow, { inputText: 'Custom meeting notes here' }, context);
106
68
 
107
- const args = { inputText: mockInitialNotes.text };
108
-
109
- // Create module with existing workflow state
110
- const moduleWithState = await createWorkflowTest()
111
- .forWorkflow(MeetingNotesWorkflow)
112
- .withImports(LoopCoreModule, ClaudeModule)
113
- .withProvider(MeetingNotesDocument)
114
- .withProvider(OptimizedNotesDocument)
115
- .withToolOverride(CreateDocument)
116
- .withToolOverride(ClaudeGenerateDocument)
117
- .withExistingWorkflow({
118
- id: '123',
119
- place: 'waiting_for_response',
120
- hashRecord: {
121
- options: generateObjectFingerprint(args), // previously run with same arguments
122
- },
123
- })
124
- .compile();
69
+ expect(result.hasError).toBe(false);
70
+ expect(result.stop).toBe(true);
125
71
 
126
- const workflowWithState = moduleWithState.get(MeetingNotesWorkflow);
127
- const processorWithState = moduleWithState.get(WorkflowProcessorService);
72
+ expect(result.documents[0]).toEqual(
73
+ expect.objectContaining({
74
+ content: expect.objectContaining({
75
+ text: expect.stringContaining('Custom meeting notes here'),
76
+ }),
77
+ }),
78
+ );
79
+ });
80
+ });
128
81
 
129
- const mockCreateDocumentWithState: ToolMock = moduleWithState.get(CreateDocument);
130
- const mockClaudeGenerateDocumentWithState: ToolMock = moduleWithState.get(ClaudeGenerateDocument);
82
+ describe('resume from waiting_for_response', () => {
83
+ it('should process user response and call LLM to optimize notes', async () => {
84
+ const workflowId = '00000000-0000-0000-0000-000000000001';
131
85
 
132
- mockCreateDocumentWithState.execute.mockResolvedValue({
133
- data: { content: mockUserEditedNotes },
134
- });
135
- mockClaudeGenerateDocumentWithState.execute.mockResolvedValue({
136
- data: { content: mockOptimizedNotes },
137
- });
86
+ mockClaudeGenerateDocument.call.mockResolvedValue({ data: undefined });
138
87
 
139
- // Context with user payload for manual transition
140
- const contextWithPayload = {
88
+ const context = {
89
+ workflowEntity: {
90
+ id: workflowId,
91
+ place: 'waiting_for_response',
92
+ documents: [],
93
+ } as Partial<WorkflowEntity>,
141
94
  payload: {
142
95
  transition: {
143
- id: 'user_response',
144
- workflowId: '123',
145
- payload: mockUserEditedNotes,
96
+ id: 'userResponse',
97
+ workflowId,
98
+ payload: { text: 'Cleaned up meeting notes from user' },
146
99
  },
147
100
  },
148
- } as RunContext;
101
+ } as unknown as RunContext;
149
102
 
150
- const result = await processorWithState.process(workflowWithState, args, contextWithPayload);
103
+ const result = await processor.process(workflow, {}, context);
151
104
 
152
- // Should execute and stop at notes_optimized (next manual step)
153
105
  expect(result.hasError).toBe(false);
154
106
  expect(result.stop).toBe(true);
155
-
156
- // Should call CreateDocument once for user response
157
- expect(mockCreateDocumentWithState.execute).toHaveBeenCalledTimes(1);
158
- expect(mockCreateDocumentWithState.execute).toHaveBeenCalledWith(
159
- expect.objectContaining({
160
- id: 'input',
161
- }),
162
- expect.anything(),
163
- expect.anything(),
164
- expect.anything(),
107
+ expect(result.place).toBe('notes_optimized');
108
+
109
+ // User response should have been saved as document
110
+ expect(result.documents).toEqual(
111
+ expect.arrayContaining([
112
+ expect.objectContaining({
113
+ className: 'MeetingNotesDocument',
114
+ }),
115
+ ]),
165
116
  );
166
117
 
167
- // Should call ClaudeGenerateDocument once
168
- expect(mockClaudeGenerateDocumentWithState.execute).toHaveBeenCalledTimes(1);
169
- expect(mockClaudeGenerateDocumentWithState.execute).toHaveBeenCalledWith(
118
+ // LLM should have been called to optimize notes
119
+ expect(mockClaudeGenerateDocument.call).toHaveBeenCalledTimes(1);
120
+ expect(mockClaudeGenerateDocument.call).toHaveBeenCalledWith(
170
121
  expect.objectContaining({
171
- claude: {
172
- model: 'claude-sonnet-4-6',
173
- },
122
+ claude: { model: 'claude-sonnet-4-6' },
123
+ response: expect.objectContaining({ id: 'final' }),
124
+ prompt: expect.stringContaining('meeting notes'),
174
125
  }),
175
- expect.anything(),
176
- expect.anything(),
177
- expect.anything(),
126
+ undefined,
178
127
  );
179
-
180
- // // Verify history contains expected places
181
- // const history = result.state.getHistory();
182
- // const places = history.map((h) => h.metadata?.place);
183
- // expect(places).toContain('response_received');
184
- // expect(places).toContain('notes_optimized');
185
-
186
- await moduleWithState.close();
187
128
  });
188
129
  });
189
130
 
190
- describe('confirm step', () => {
131
+ describe('resume from notes_optimized', () => {
191
132
  it('should complete workflow when user confirms optimized notes', async () => {
192
- const mockFinalNotes = {
133
+ const workflowId = '00000000-0000-0000-0000-000000000002';
134
+
135
+ const optimizedPayload = {
193
136
  date: '2025-01-01',
194
137
  summary: 'Budget discussion with updates',
195
138
  participants: ['Sarah', 'Anna', 'Bob'],
@@ -197,60 +140,38 @@ describe('MeetingNotesWorkflow', () => {
197
140
  actionItems: ['Follow up on vendor pricing by Friday'],
198
141
  };
199
142
 
200
- const args = { inputText: 'any text' };
201
-
202
- // Create module with existing workflow state after AI optimization
203
- const moduleWithState = await createWorkflowTest()
204
- .forWorkflow(MeetingNotesWorkflow)
205
- .withImports(LoopCoreModule, ClaudeModule)
206
- .withProvider(MeetingNotesDocument)
207
- .withProvider(OptimizedNotesDocument)
208
- .withToolOverride(CreateDocument)
209
- .withToolOverride(ClaudeGenerateDocument)
210
- .withExistingWorkflow({
211
- id: '123',
143
+ const context = {
144
+ workflowEntity: {
145
+ id: workflowId,
212
146
  place: 'notes_optimized',
213
- hashRecord: {
214
- options: generateObjectFingerprint(args), // previously run with same arguments
215
- },
216
- })
217
- .compile();
218
-
219
- const workflowWithState = moduleWithState.get(MeetingNotesWorkflow);
220
- const processorWithState = moduleWithState.get(WorkflowProcessorService);
221
-
222
- const mockCreateDocumentWithState: ToolMock = moduleWithState.get(CreateDocument);
223
-
224
- mockCreateDocumentWithState.execute.mockResolvedValue({
225
- data: { content: mockFinalNotes },
226
- });
227
-
228
- // Context with user confirmation for manual transition
229
- const contextWithPayload = {
147
+ documents: [],
148
+ } as Partial<WorkflowEntity>,
230
149
  payload: {
231
150
  transition: {
232
151
  id: 'confirm',
233
- workflowId: '123',
234
- payload: mockFinalNotes,
152
+ workflowId,
153
+ payload: optimizedPayload,
235
154
  },
236
155
  },
237
- } as RunContext;
156
+ } as unknown as RunContext;
238
157
 
239
- const result = await processorWithState.process(workflowWithState, args, contextWithPayload);
158
+ const result = await processor.process(workflow, {}, context);
240
159
 
241
- // Should complete and reach end state
242
160
  expect(result.hasError).toBe(false);
243
161
  expect(result.stop).toBe(false);
162
+ expect(result.place).toBe('end');
163
+
164
+ // Confirmed payload should have been saved as OptimizedNotesDocument
165
+ expect(result.documents).toEqual(
166
+ expect.arrayContaining([
167
+ expect.objectContaining({
168
+ className: 'OptimizedNotesDocument',
169
+ }),
170
+ ]),
171
+ );
244
172
 
245
- // Should call CreateDocument once for final confirmation
246
- expect(mockCreateDocumentWithState.execute).toHaveBeenCalledTimes(1);
247
-
248
- // // Verify history contains expected places including end
249
- // const history = result.state.getHistory();
250
- // const places = history.map((h) => h.metadata?.place);
251
- // expect(places).toContain('end');
252
-
253
- await moduleWithState.close();
173
+ // No additional LLM calls during confirmation
174
+ expect(mockClaudeGenerateDocument.call).not.toHaveBeenCalled();
254
175
  });
255
176
  });
256
177
  });
@@ -1,18 +1,14 @@
1
1
  import { z } from 'zod';
2
- import { Document, DocumentInterface, Input } from '@loopstack/common';
2
+ import { Document } from '@loopstack/common';
3
3
 
4
4
  export const MeetingNotesDocumentSchema = z.object({
5
5
  text: z.string(),
6
6
  });
7
7
 
8
8
  @Document({
9
- configFile: __dirname + '/meeting-notes-document.yaml',
9
+ schema: MeetingNotesDocumentSchema,
10
+ uiConfig: __dirname + '/meeting-notes-document.yaml',
10
11
  })
11
- export class MeetingNotesDocument implements DocumentInterface {
12
- @Input({
13
- schema: MeetingNotesDocumentSchema,
14
- })
15
- content: {
16
- text: string;
17
- };
12
+ export class MeetingNotesDocument {
13
+ text: string;
18
14
  }
@@ -1,13 +1,13 @@
1
1
  type: document
2
2
  ui:
3
- form:
4
- properties:
5
- text:
6
- title: Text
7
- widget: textarea
8
- actions:
9
- - type: button
10
- widget: button
3
+ widgets:
4
+ - widget: form
11
5
  options:
12
- transition: user_response
13
- label: 'Optimize Notes'
6
+ properties:
7
+ text:
8
+ title: Text
9
+ widget: textarea
10
+ actions:
11
+ - type: button
12
+ transition: userResponse
13
+ label: 'Optimize Notes'
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { Document, DocumentInterface, Input } from '@loopstack/common';
2
+ import { Document } from '@loopstack/common';
3
3
 
4
4
  export const OptimizedMeetingNotesDocumentSchema = z.object({
5
5
  date: z.string(),
@@ -10,11 +10,13 @@ export const OptimizedMeetingNotesDocumentSchema = z.object({
10
10
  });
11
11
 
12
12
  @Document({
13
- configFile: __dirname + '/optimized-notes-document.yaml',
13
+ schema: OptimizedMeetingNotesDocumentSchema,
14
+ uiConfig: __dirname + '/optimized-notes-document.yaml',
14
15
  })
15
- export class OptimizedNotesDocument implements DocumentInterface {
16
- @Input({
17
- schema: OptimizedMeetingNotesDocumentSchema,
18
- })
19
- content: z.infer<typeof OptimizedMeetingNotesDocumentSchema>;
16
+ export class OptimizedNotesDocument {
17
+ date: string;
18
+ summary: string;
19
+ participants: string[];
20
+ decisions: string[];
21
+ actionItems: string[];
20
22
  }
@@ -1,36 +1,36 @@
1
1
  type: document
2
2
  ui:
3
- form:
4
- order:
5
- - date
6
- - summary
7
- - participants
8
- - decisions
9
- - actionItems
10
- properties:
11
- date:
12
- title: Date
13
- summary:
14
- title: Summary
15
- widget: textarea
16
- participants:
17
- title: Participants
18
- collapsed: true
19
- items:
20
- title: Participant
21
- decisions:
22
- title: Decisions
23
- collapsed: true
24
- items:
25
- title: Decision
26
- actionItems:
27
- title: Action Items
28
- collapsed: true
29
- items:
30
- title: Action Item
31
- actions:
32
- - type: button
33
- widget: button
3
+ widgets:
4
+ - widget: form
34
5
  options:
35
- transition: confirm
36
- label: 'Confirm'
6
+ order:
7
+ - date
8
+ - summary
9
+ - participants
10
+ - decisions
11
+ - actionItems
12
+ properties:
13
+ date:
14
+ title: Date
15
+ summary:
16
+ title: Summary
17
+ widget: textarea
18
+ participants:
19
+ title: Participants
20
+ collapsed: true
21
+ items:
22
+ title: Participant
23
+ decisions:
24
+ title: Decisions
25
+ collapsed: true
26
+ items:
27
+ title: Decision
28
+ actionItems:
29
+ title: Action Items
30
+ collapsed: true
31
+ items:
32
+ title: Action Item
33
+ actions:
34
+ - type: button
35
+ transition: confirm
36
+ label: 'Confirm'
@@ -0,0 +1,10 @@
1
+ title: 'Human-in-the-loop Demo (Meeting Notes Optimizer)'
2
+
3
+ description: 'A demo workflow to demonstrate how to use AI to structure meeting notes.'
4
+
5
+ ui:
6
+ form:
7
+ properties:
8
+ inputText:
9
+ title: 'Text'
10
+ widget: 'textarea'
@@ -1,43 +1,61 @@
1
1
  import { z } from 'zod';
2
2
  import { ClaudeGenerateDocument } from '@loopstack/claude-module';
3
- import { InjectDocument, InjectTool, Input, Runtime, State, Workflow } from '@loopstack/common';
4
- import { CreateDocument } from '@loopstack/core';
3
+ import { BaseWorkflow, Final, Initial, InjectTool, Transition, Workflow } from '@loopstack/common';
5
4
  import { MeetingNotesDocument, MeetingNotesDocumentSchema } from './documents/meeting-notes-document';
6
5
  import { OptimizedMeetingNotesDocumentSchema, OptimizedNotesDocument } from './documents/optimized-notes-document';
7
6
 
8
7
  @Workflow({
9
- configFile: __dirname + '/meeting-notes.workflow.yaml',
8
+ uiConfig: __dirname + '/meeting-notes.ui.yaml',
9
+ schema: z.object({
10
+ inputText: z
11
+ .string()
12
+ .default(
13
+ '- meeting 1.1.2025\n- budget: need 2 cut costs sarah said\n- hire new person?? --> marketing\n- vendor pricing - follow up needed by anna',
14
+ ),
15
+ }),
10
16
  })
11
- export class MeetingNotesWorkflow {
17
+ export class MeetingNotesWorkflow extends BaseWorkflow<{ inputText: string }> {
12
18
  @InjectTool() claudeGenerateDocument: ClaudeGenerateDocument;
13
- @InjectTool() createDocument: CreateDocument;
14
- @InjectDocument() meetingNotesDocument: MeetingNotesDocument;
15
- @InjectDocument() optimizedNotesDocument: OptimizedNotesDocument;
16
19
 
17
- @Input({
18
- schema: z.object({
19
- inputText: z
20
- .string()
21
- .default(
22
- '- meeting 1.1.2025\n- budget: need 2 cut costs sarah said\n- hire new person?? --> marketing\n- vendor pricing - follow up needed by anna',
23
- ),
24
- }),
25
- })
26
- args: {
27
- inputText: string;
28
- };
20
+ meetingNotes?: z.infer<typeof MeetingNotesDocumentSchema>;
21
+ optimizedNotes?: z.infer<typeof OptimizedMeetingNotesDocumentSchema>;
29
22
 
30
- @State({
31
- schema: z.object({
32
- meetingNotes: MeetingNotesDocumentSchema.optional(),
33
- optimizedNotes: OptimizedMeetingNotesDocumentSchema.optional(),
34
- }),
35
- })
36
- state: {
37
- meetingNotes?: z.infer<typeof MeetingNotesDocumentSchema>;
38
- optimizedNotes?: z.infer<typeof OptimizedMeetingNotesDocumentSchema>;
39
- };
23
+ @Initial({ to: 'waiting_for_response' })
24
+ async createForm(args: { inputText: string }) {
25
+ await this.repository.save(
26
+ MeetingNotesDocument,
27
+ {
28
+ text: `Unstructured Notes:\n\n${args.inputText}`,
29
+ },
30
+ { id: 'input' },
31
+ );
32
+ }
40
33
 
41
- @Runtime()
42
- runtime: any;
34
+ @Transition({ from: 'waiting_for_response', to: 'response_received', wait: true, schema: MeetingNotesDocumentSchema })
35
+ async userResponse(payload: z.infer<typeof MeetingNotesDocumentSchema>) {
36
+ const result = await this.repository.save(MeetingNotesDocument, payload, { id: 'input' });
37
+ this.meetingNotes = result.content as z.infer<typeof MeetingNotesDocumentSchema>;
38
+ }
39
+
40
+ @Transition({ from: 'response_received', to: 'notes_optimized' })
41
+ async optimizeNotes() {
42
+ await this.claudeGenerateDocument.call({
43
+ claude: { model: 'claude-sonnet-4-6' },
44
+ response: {
45
+ id: 'final',
46
+ document: OptimizedNotesDocument,
47
+ },
48
+ prompt: `Extract all information from the provided meeting notes into the structured document.
49
+
50
+ <Meeting Notes>
51
+ ${this.meetingNotes?.text}
52
+ </Meeting Notes>`,
53
+ });
54
+ }
55
+
56
+ @Final({ from: 'notes_optimized', wait: true, schema: OptimizedMeetingNotesDocumentSchema })
57
+ async confirm(payload: z.infer<typeof OptimizedMeetingNotesDocumentSchema>) {
58
+ const result = await this.repository.save(OptimizedNotesDocument, payload, { id: 'final' });
59
+ this.optimizedNotes = result.content as z.infer<typeof OptimizedMeetingNotesDocumentSchema>;
60
+ }
43
61
  }
@@ -1,76 +0,0 @@
1
- title: 'Human-in-the-loop Demo (Meeting Notes Optimizer)'
2
-
3
- description: 'A demo workflow to demonstrate how to use AI to structure meeting notes.'
4
-
5
- ui:
6
- form:
7
- properties:
8
- inputText:
9
- title: 'Text'
10
- widget: 'textarea'
11
-
12
- transitions:
13
- - id: create_form
14
- from: start
15
- to: waiting_for_response
16
- call:
17
- - id: form
18
- tool: createDocument
19
- args:
20
- id: input
21
- document: meetingNotesDocument
22
- update:
23
- content:
24
- text: |
25
- Unstructured Notes:
26
-
27
- {{ args.inputText }}
28
-
29
- - id: user_response
30
- from: waiting_for_response
31
- to: response_received
32
- trigger: manual
33
- call:
34
- - id: create_response
35
- tool: createDocument
36
- args:
37
- id: input
38
- document: meetingNotesDocument
39
- update:
40
- content: ${{ runtime.transition.payload }}
41
- assign:
42
- meetingNotes: ${{ result.data.content }}
43
-
44
- - id: optimize_notes
45
- from: response_received
46
- to: notes_optimized
47
- call:
48
- - id: prompt
49
- tool: claudeGenerateDocument
50
- args:
51
- claude:
52
- model: claude-sonnet-4-6
53
- response:
54
- id: final
55
- document: optimizedNotesDocument
56
- prompt: |
57
- Extract all information from the provided meeting notes into the structured document.
58
-
59
- <Meeting Notes>
60
- {{ state.meetingNotes.text }}
61
- </Meeting Notes>
62
-
63
- - id: confirm
64
- from: notes_optimized
65
- to: end
66
- trigger: manual
67
- call:
68
- - id: create_response
69
- tool: createDocument
70
- args:
71
- id: final
72
- document: optimizedNotesDocument
73
- update:
74
- content: ${{ runtime.transition.payload }}
75
- assign:
76
- optimizedNotes: ${{ result.data.content }}
@@ -1,76 +0,0 @@
1
- title: 'Human-in-the-loop Demo (Meeting Notes Optimizer)'
2
-
3
- description: 'A demo workflow to demonstrate how to use AI to structure meeting notes.'
4
-
5
- ui:
6
- form:
7
- properties:
8
- inputText:
9
- title: 'Text'
10
- widget: 'textarea'
11
-
12
- transitions:
13
- - id: create_form
14
- from: start
15
- to: waiting_for_response
16
- call:
17
- - id: form
18
- tool: createDocument
19
- args:
20
- id: input
21
- document: meetingNotesDocument
22
- update:
23
- content:
24
- text: |
25
- Unstructured Notes:
26
-
27
- {{ args.inputText }}
28
-
29
- - id: user_response
30
- from: waiting_for_response
31
- to: response_received
32
- trigger: manual
33
- call:
34
- - id: create_response
35
- tool: createDocument
36
- args:
37
- id: input
38
- document: meetingNotesDocument
39
- update:
40
- content: ${{ runtime.transition.payload }}
41
- assign:
42
- meetingNotes: ${{ result.data.content }}
43
-
44
- - id: optimize_notes
45
- from: response_received
46
- to: notes_optimized
47
- call:
48
- - id: prompt
49
- tool: claudeGenerateDocument
50
- args:
51
- claude:
52
- model: claude-sonnet-4-6
53
- response:
54
- id: final
55
- document: optimizedNotesDocument
56
- prompt: |
57
- Extract all information from the provided meeting notes into the structured document.
58
-
59
- <Meeting Notes>
60
- {{ state.meetingNotes.text }}
61
- </Meeting Notes>
62
-
63
- - id: confirm
64
- from: notes_optimized
65
- to: end
66
- trigger: manual
67
- call:
68
- - id: create_response
69
- tool: createDocument
70
- args:
71
- id: final
72
- document: optimizedNotesDocument
73
- update:
74
- content: ${{ runtime.transition.payload }}
75
- assign:
76
- optimizedNotes: ${{ result.data.content }}