@ddse/acm-examples 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.
Files changed (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/bin/acm-demo.ts +495 -0
  4. package/data/coaching/agents.json +16 -0
  5. package/data/coaching/transcripts.json +37 -0
  6. package/data/documents.json +72 -0
  7. package/data/entitlement/customers.json +38 -0
  8. package/data/entitlement/policies.json +38 -0
  9. package/data/incidents/incidents.json +30 -0
  10. package/data/incidents/routing_rules.json +36 -0
  11. package/data/invoices/invoices.json +38 -0
  12. package/data/invoices/purchase-orders.json +38 -0
  13. package/data/issues.json +99 -0
  14. package/data/knowledge/docs/kb-001.md +7 -0
  15. package/data/knowledge/docs/kb-002.md +7 -0
  16. package/data/knowledge/docs/kb-003.md +9 -0
  17. package/data/knowledge/index.json +25 -0
  18. package/data/orders.json +106 -0
  19. package/dist/bin/acm-demo.d.ts +3 -0
  20. package/dist/bin/acm-demo.d.ts.map +1 -0
  21. package/dist/bin/acm-demo.js +392 -0
  22. package/dist/bin/acm-demo.js.map +1 -0
  23. package/dist/src/context/directives.d.ts +3 -0
  24. package/dist/src/context/directives.d.ts.map +1 -0
  25. package/dist/src/context/directives.js +325 -0
  26. package/dist/src/context/directives.js.map +1 -0
  27. package/dist/src/context/index.d.ts +2 -0
  28. package/dist/src/context/index.d.ts.map +1 -0
  29. package/dist/src/context/index.js +2 -0
  30. package/dist/src/context/index.js.map +1 -0
  31. package/dist/src/data/coaching.d.ts +19 -0
  32. package/dist/src/data/coaching.d.ts.map +1 -0
  33. package/dist/src/data/coaching.js +22 -0
  34. package/dist/src/data/coaching.js.map +1 -0
  35. package/dist/src/data/entitlement.d.ts +25 -0
  36. package/dist/src/data/entitlement.d.ts.map +1 -0
  37. package/dist/src/data/entitlement.js +26 -0
  38. package/dist/src/data/entitlement.js.map +1 -0
  39. package/dist/src/data/incidents.d.ts +23 -0
  40. package/dist/src/data/incidents.d.ts.map +1 -0
  41. package/dist/src/data/incidents.js +37 -0
  42. package/dist/src/data/incidents.js.map +1 -0
  43. package/dist/src/data/invoices.d.ts +34 -0
  44. package/dist/src/data/invoices.d.ts.map +1 -0
  45. package/dist/src/data/invoices.js +49 -0
  46. package/dist/src/data/invoices.js.map +1 -0
  47. package/dist/src/data/knowledge.d.ts +11 -0
  48. package/dist/src/data/knowledge.d.ts.map +1 -0
  49. package/dist/src/data/knowledge.js +57 -0
  50. package/dist/src/data/knowledge.js.map +1 -0
  51. package/dist/src/data/loader.d.ts +4 -0
  52. package/dist/src/data/loader.d.ts.map +1 -0
  53. package/dist/src/data/loader.js +69 -0
  54. package/dist/src/data/loader.js.map +1 -0
  55. package/dist/src/examples/scenarios.d.ts +23 -0
  56. package/dist/src/examples/scenarios.d.ts.map +1 -0
  57. package/dist/src/examples/scenarios.js +609 -0
  58. package/dist/src/examples/scenarios.js.map +1 -0
  59. package/dist/src/goals/index.d.ts +8 -0
  60. package/dist/src/goals/index.d.ts.map +1 -0
  61. package/dist/src/goals/index.js +12 -0
  62. package/dist/src/goals/index.js.map +1 -0
  63. package/dist/src/policy.d.ts +5 -0
  64. package/dist/src/policy.d.ts.map +1 -0
  65. package/dist/src/policy.js +24 -0
  66. package/dist/src/policy.js.map +1 -0
  67. package/dist/src/registries.d.ts +18 -0
  68. package/dist/src/registries.d.ts.map +1 -0
  69. package/dist/src/registries.js +38 -0
  70. package/dist/src/registries.js.map +1 -0
  71. package/dist/src/renderer.d.ts +9 -0
  72. package/dist/src/renderer.d.ts.map +1 -0
  73. package/dist/src/renderer.js +76 -0
  74. package/dist/src/renderer.js.map +1 -0
  75. package/dist/src/search/bm25.d.ts +68 -0
  76. package/dist/src/search/bm25.d.ts.map +1 -0
  77. package/dist/src/search/bm25.js +131 -0
  78. package/dist/src/search/bm25.js.map +1 -0
  79. package/dist/src/search/index.d.ts +2 -0
  80. package/dist/src/search/index.d.ts.map +1 -0
  81. package/dist/src/search/index.js +3 -0
  82. package/dist/src/search/index.js.map +1 -0
  83. package/dist/src/tasks/coaching.d.ts +30 -0
  84. package/dist/src/tasks/coaching.d.ts.map +1 -0
  85. package/dist/src/tasks/coaching.js +143 -0
  86. package/dist/src/tasks/coaching.js.map +1 -0
  87. package/dist/src/tasks/entitlement.d.ts +29 -0
  88. package/dist/src/tasks/entitlement.d.ts.map +1 -0
  89. package/dist/src/tasks/entitlement.js +135 -0
  90. package/dist/src/tasks/entitlement.js.map +1 -0
  91. package/dist/src/tasks/incidents.d.ts +42 -0
  92. package/dist/src/tasks/incidents.d.ts.map +1 -0
  93. package/dist/src/tasks/incidents.js +189 -0
  94. package/dist/src/tasks/incidents.js.map +1 -0
  95. package/dist/src/tasks/index.d.ts +7 -0
  96. package/dist/src/tasks/index.d.ts.map +1 -0
  97. package/dist/src/tasks/index.js +7 -0
  98. package/dist/src/tasks/index.js.map +1 -0
  99. package/dist/src/tasks/invoices.d.ts +40 -0
  100. package/dist/src/tasks/invoices.d.ts.map +1 -0
  101. package/dist/src/tasks/invoices.js +180 -0
  102. package/dist/src/tasks/invoices.js.map +1 -0
  103. package/dist/src/tasks/knowledge.d.ts +23 -0
  104. package/dist/src/tasks/knowledge.d.ts.map +1 -0
  105. package/dist/src/tasks/knowledge.js +115 -0
  106. package/dist/src/tasks/knowledge.js.map +1 -0
  107. package/dist/src/tasks/legacy.d.ts +50 -0
  108. package/dist/src/tasks/legacy.d.ts.map +1 -0
  109. package/dist/src/tasks/legacy.js +85 -0
  110. package/dist/src/tasks/legacy.js.map +1 -0
  111. package/dist/src/tools/coaching/index.d.ts +49 -0
  112. package/dist/src/tools/coaching/index.d.ts.map +1 -0
  113. package/dist/src/tools/coaching/index.js +119 -0
  114. package/dist/src/tools/coaching/index.js.map +1 -0
  115. package/dist/src/tools/entitlement/index.d.ts +52 -0
  116. package/dist/src/tools/entitlement/index.d.ts.map +1 -0
  117. package/dist/src/tools/entitlement/index.js +120 -0
  118. package/dist/src/tools/entitlement/index.js.map +1 -0
  119. package/dist/src/tools/incidents/index.d.ts +55 -0
  120. package/dist/src/tools/incidents/index.d.ts.map +1 -0
  121. package/dist/src/tools/incidents/index.js +109 -0
  122. package/dist/src/tools/incidents/index.js.map +1 -0
  123. package/dist/src/tools/index.d.ts +90 -0
  124. package/dist/src/tools/index.d.ts.map +1 -0
  125. package/dist/src/tools/index.js +109 -0
  126. package/dist/src/tools/index.js.map +1 -0
  127. package/dist/src/tools/invoices/index.d.ts +56 -0
  128. package/dist/src/tools/invoices/index.d.ts.map +1 -0
  129. package/dist/src/tools/invoices/index.js +85 -0
  130. package/dist/src/tools/invoices/index.js.map +1 -0
  131. package/dist/src/tools/knowledge/index.d.ts +52 -0
  132. package/dist/src/tools/knowledge/index.d.ts.map +1 -0
  133. package/dist/src/tools/knowledge/index.js +120 -0
  134. package/dist/src/tools/knowledge/index.js.map +1 -0
  135. package/dist/tests/bm25.test.d.ts +2 -0
  136. package/dist/tests/bm25.test.d.ts.map +1 -0
  137. package/dist/tests/bm25.test.js +98 -0
  138. package/dist/tests/bm25.test.js.map +1 -0
  139. package/dist/tests/integration.test.d.ts +2 -0
  140. package/dist/tests/integration.test.d.ts.map +1 -0
  141. package/dist/tests/integration.test.js +126 -0
  142. package/dist/tests/integration.test.js.map +1 -0
  143. package/dist/tests/plan-hydration.test.d.ts +2 -0
  144. package/dist/tests/plan-hydration.test.d.ts.map +1 -0
  145. package/dist/tests/plan-hydration.test.js +28 -0
  146. package/dist/tests/plan-hydration.test.js.map +1 -0
  147. package/dist/tsconfig.tsbuildinfo +1 -0
  148. package/docs/examples-architecture.md +144 -0
  149. package/docs/successrun.md +1022 -0
  150. package/package.json +33 -0
  151. package/src/context/directives.ts +366 -0
  152. package/src/context/index.ts +1 -0
  153. package/src/data/coaching.ts +50 -0
  154. package/src/data/entitlement.ts +60 -0
  155. package/src/data/incidents.ts +78 -0
  156. package/src/data/invoices.ts +103 -0
  157. package/src/data/knowledge.ts +77 -0
  158. package/src/data/loader.ts +80 -0
  159. package/src/examples/scenarios.ts +724 -0
  160. package/src/goals/index.ts +18 -0
  161. package/src/policy.ts +30 -0
  162. package/src/registries.ts +48 -0
  163. package/src/renderer.ts +82 -0
  164. package/src/search/bm25.ts +173 -0
  165. package/src/search/index.ts +2 -0
  166. package/src/tasks/coaching.ts +217 -0
  167. package/src/tasks/entitlement.ts +197 -0
  168. package/src/tasks/incidents.ts +277 -0
  169. package/src/tasks/index.ts +6 -0
  170. package/src/tasks/invoices.ts +269 -0
  171. package/src/tasks/knowledge.ts +169 -0
  172. package/src/tasks/legacy.ts +112 -0
  173. package/src/tools/coaching/index.ts +197 -0
  174. package/src/tools/entitlement/index.ts +199 -0
  175. package/src/tools/incidents/index.ts +185 -0
  176. package/src/tools/index.ts +192 -0
  177. package/src/tools/invoices/index.ts +165 -0
  178. package/src/tools/knowledge/index.ts +203 -0
  179. package/tests/bm25.test.ts +129 -0
  180. package/tests/integration.test.ts +163 -0
  181. package/tests/plan-hydration.test.ts +33 -0
  182. package/tsconfig.json +18 -0
@@ -0,0 +1,269 @@
1
+ import { Task, type RunContext } from '@ddse/acm-sdk';
2
+ import type { InvoiceRecord, PurchaseOrderRecord } from '../data/invoices.js';
3
+ import {
4
+ type FetchInvoiceInput,
5
+ type FetchInvoiceOutput,
6
+ type FetchPurchaseOrderInput,
7
+ type FetchPurchaseOrderOutput,
8
+ type CompareLineItemsInput,
9
+ type CompareLineItemsOutput,
10
+ type RecordFindingsInput,
11
+ type RecordFindingsOutput,
12
+ } from '../tools/invoices/index.js';
13
+
14
+ export class FetchInvoiceTask extends Task<FetchInvoiceInput, FetchInvoiceOutput> {
15
+ constructor() {
16
+ super('task-invoice-fetch', 'invoice.fetch');
17
+ }
18
+
19
+ idemKey(_ctx: RunContext, input: FetchInvoiceInput): string | undefined {
20
+ return input?.invoiceId ? `invoice:${input.invoiceId}` : undefined;
21
+ }
22
+
23
+ policyInput(_ctx: RunContext, input: FetchInvoiceInput): Record<string, unknown> {
24
+ return {
25
+ action: 'fetch_invoice',
26
+ invoiceId: input.invoiceId,
27
+ };
28
+ }
29
+
30
+ verification(): string[] {
31
+ return ['output.invoice !== undefined'];
32
+ }
33
+
34
+ async execute(
35
+ ctx: RunContext,
36
+ input: FetchInvoiceInput
37
+ ): Promise<FetchInvoiceOutput> {
38
+ const tool = ctx.getTool('fetch_invoice');
39
+ if (!tool) {
40
+ throw new Error('fetch_invoice tool is not registered');
41
+ }
42
+
43
+ const result = (await tool.call(input)) as FetchInvoiceOutput;
44
+ ctx.stream?.emit('task', {
45
+ taskId: this.id,
46
+ stage: 'invoice_loaded',
47
+ invoiceId: input.invoiceId,
48
+ });
49
+ return result;
50
+ }
51
+ }
52
+
53
+ export class FetchPurchaseOrderTask extends Task<
54
+ FetchPurchaseOrderInput,
55
+ FetchPurchaseOrderOutput
56
+ > {
57
+ constructor() {
58
+ super('task-invoice-fetch-po', 'invoice.fetch_purchase_order');
59
+ }
60
+
61
+ idemKey(_ctx: RunContext, input: FetchPurchaseOrderInput): string | undefined {
62
+ return input?.purchaseOrderId ? `purchase-order:${input.purchaseOrderId}` : undefined;
63
+ }
64
+
65
+ policyInput(_ctx: RunContext, input: FetchPurchaseOrderInput): Record<string, unknown> {
66
+ return {
67
+ action: 'fetch_purchase_order',
68
+ purchaseOrderId: input.purchaseOrderId,
69
+ };
70
+ }
71
+
72
+ verification(): string[] {
73
+ return ['output.purchaseOrder !== undefined'];
74
+ }
75
+
76
+ async execute(
77
+ ctx: RunContext,
78
+ input: FetchPurchaseOrderInput
79
+ ): Promise<FetchPurchaseOrderOutput> {
80
+ const tool = ctx.getTool('fetch_purchase_order');
81
+ if (!tool) {
82
+ throw new Error('fetch_purchase_order tool is not registered');
83
+ }
84
+
85
+ const result = (await tool.call(input)) as FetchPurchaseOrderOutput;
86
+ ctx.stream?.emit('task', {
87
+ taskId: this.id,
88
+ stage: 'purchase_order_loaded',
89
+ purchaseOrderId: input.purchaseOrderId,
90
+ });
91
+ return result;
92
+ }
93
+ }
94
+
95
+ export interface CompareLineItemsTaskInput extends Partial<CompareLineItemsInput> {
96
+ invoiceId?: string;
97
+ purchaseOrderId?: string;
98
+ }
99
+
100
+ export class CompareLineItemsTask extends Task<
101
+ CompareLineItemsTaskInput,
102
+ CompareLineItemsOutput
103
+ > {
104
+ constructor() {
105
+ super('task-invoice-compare-lines', 'invoice.compare_line_items');
106
+ }
107
+
108
+ policyInput(_ctx: RunContext, input: CompareLineItemsTaskInput): Record<string, unknown> {
109
+ const invoice = this.resolveInvoice(_ctx, input);
110
+ const purchaseOrder = this.resolvePurchaseOrder(_ctx, input);
111
+ return {
112
+ action: 'compare_line_items',
113
+ invoiceId: invoice?.id ?? input.invoiceId,
114
+ purchaseOrderId: purchaseOrder?.id ?? input.purchaseOrderId,
115
+ };
116
+ }
117
+
118
+ verification(): string[] {
119
+ return ['Array.isArray(output.discrepancies)', 'typeof output.variance === "number"'];
120
+ }
121
+
122
+ async execute(
123
+ ctx: RunContext,
124
+ input: CompareLineItemsTaskInput
125
+ ): Promise<CompareLineItemsOutput> {
126
+ const tool = ctx.getTool('compare_line_items');
127
+ if (!tool) {
128
+ throw new Error('compare_line_items tool is not registered');
129
+ }
130
+
131
+ const invoice = this.resolveInvoice(ctx, input);
132
+ const purchaseOrder = this.resolvePurchaseOrder(ctx, input);
133
+
134
+ if (!invoice || !purchaseOrder) {
135
+ throw new Error('invoice and purchaseOrder are required');
136
+ }
137
+
138
+ const result = (await tool.call({ invoice, purchaseOrder })) as CompareLineItemsOutput;
139
+ ctx.stream?.emit('task', {
140
+ taskId: this.id,
141
+ stage: 'invoice_line_items_compared',
142
+ invoiceId: invoice.id,
143
+ purchaseOrderId: purchaseOrder.id,
144
+ discrepancies: result.discrepancies.length,
145
+ variance: result.variance,
146
+ });
147
+ return result;
148
+ }
149
+
150
+ private resolveInvoice(
151
+ ctx: RunContext,
152
+ input: CompareLineItemsTaskInput
153
+ ): InvoiceRecord | undefined {
154
+ if (input?.invoice) {
155
+ return input.invoice;
156
+ }
157
+
158
+ const fetchOutput = ctx.outputs?.['task-invoice-fetch'] as FetchInvoiceOutput | undefined;
159
+ const invoice = fetchOutput?.invoice;
160
+ if (!invoice) {
161
+ return undefined;
162
+ }
163
+
164
+ if (!input?.invoiceId || invoice.id === input.invoiceId) {
165
+ return invoice;
166
+ }
167
+
168
+ return undefined;
169
+ }
170
+
171
+ private resolvePurchaseOrder(
172
+ ctx: RunContext,
173
+ input: CompareLineItemsTaskInput
174
+ ): PurchaseOrderRecord | undefined {
175
+ if (input?.purchaseOrder) {
176
+ return input.purchaseOrder;
177
+ }
178
+
179
+ const fetchOutput = ctx.outputs?.['task-invoice-fetch-po'] as FetchPurchaseOrderOutput | undefined;
180
+ const purchaseOrder = fetchOutput?.purchaseOrder;
181
+ if (!purchaseOrder) {
182
+ return undefined;
183
+ }
184
+
185
+ if (!input?.purchaseOrderId || purchaseOrder.id === input.purchaseOrderId) {
186
+ return purchaseOrder;
187
+ }
188
+
189
+ return undefined;
190
+ }
191
+ }
192
+
193
+ export interface RecordFindingsTaskInput extends Partial<RecordFindingsInput> {
194
+ invoiceId?: string;
195
+ purchaseOrderId?: string;
196
+ }
197
+
198
+ export class RecordFindingsTask extends Task<
199
+ RecordFindingsTaskInput,
200
+ RecordFindingsOutput
201
+ > {
202
+ constructor() {
203
+ super('task-invoice-record-findings', 'invoice.record_findings');
204
+ }
205
+
206
+ policyInput(_ctx: RunContext, input: RecordFindingsTaskInput): Record<string, unknown> {
207
+ const resolved = this.resolveInput(_ctx, input);
208
+ return {
209
+ action: 'record_findings',
210
+ invoiceId: resolved.invoice.id,
211
+ purchaseOrderId: resolved.purchaseOrder.id,
212
+ discrepancyCount: resolved.discrepancies.length,
213
+ };
214
+ }
215
+
216
+ verification(): string[] {
217
+ return ['typeof output.reportId === "string"', 'Array.isArray(output.nextSteps)'];
218
+ }
219
+
220
+ async execute(
221
+ ctx: RunContext,
222
+ input: RecordFindingsTaskInput
223
+ ): Promise<RecordFindingsOutput> {
224
+ const tool = ctx.getTool('record_findings');
225
+ if (!tool) {
226
+ throw new Error('record_findings tool is not registered');
227
+ }
228
+
229
+ const resolved = this.resolveInput(ctx, input);
230
+
231
+ const result = (await tool.call(resolved)) as RecordFindingsOutput;
232
+ ctx.stream?.emit('task', {
233
+ taskId: this.id,
234
+ stage: 'invoice_findings_recorded',
235
+ invoiceId: resolved.invoice.id,
236
+ purchaseOrderId: resolved.purchaseOrder.id,
237
+ status: result.status,
238
+ });
239
+ return result;
240
+ }
241
+
242
+ private resolveInput(
243
+ ctx: RunContext,
244
+ input: RecordFindingsTaskInput
245
+ ): RecordFindingsInput {
246
+ const invoice = input.invoice ?? (ctx.outputs?.['task-invoice-fetch'] as FetchInvoiceOutput | undefined)?.invoice;
247
+ const purchaseOrder =
248
+ input.purchaseOrder ?? (ctx.outputs?.['task-invoice-fetch-po'] as FetchPurchaseOrderOutput | undefined)?.purchaseOrder;
249
+ const compareOutput = ctx.outputs?.['task-invoice-compare-lines'] as CompareLineItemsOutput | undefined;
250
+
251
+ if (!invoice) {
252
+ throw new Error('invoice is required for recording findings');
253
+ }
254
+
255
+ if (!purchaseOrder) {
256
+ throw new Error('purchaseOrder is required for recording findings');
257
+ }
258
+
259
+ const discrepancies = input.discrepancies ?? compareOutput?.discrepancies ?? [];
260
+ const variance = input.variance ?? compareOutput?.variance ?? 0;
261
+
262
+ return {
263
+ invoice,
264
+ purchaseOrder,
265
+ discrepancies,
266
+ variance,
267
+ };
268
+ }
269
+ }
@@ -0,0 +1,169 @@
1
+ import { Task, type RunContext } from '@ddse/acm-sdk';
2
+ import {
3
+ type KnowledgeSearchInput,
4
+ type KnowledgeSearchOutput,
5
+ type SummarizeSnippetInput,
6
+ type SummarizeSnippetOutput,
7
+ type SuggestFollowupsInput,
8
+ type SuggestFollowupsOutput,
9
+ } from '../tools/knowledge/index.js';
10
+
11
+ export class SearchKnowledgeTask extends Task<
12
+ KnowledgeSearchInput,
13
+ KnowledgeSearchOutput
14
+ > {
15
+ constructor() {
16
+ super('task-knowledge-search', 'knowledge.search');
17
+ }
18
+
19
+ idemKey(_ctx: RunContext, input: KnowledgeSearchInput): string | undefined {
20
+ const query = input?.query?.trim();
21
+ return query ? `knowledge:search:${query.toLowerCase()}` : undefined;
22
+ }
23
+
24
+ policyInput(_ctx: RunContext, input: KnowledgeSearchInput): Record<string, unknown> {
25
+ return {
26
+ action: 'search_knowledge',
27
+ query: input.query,
28
+ limit: input.limit,
29
+ };
30
+ }
31
+
32
+ verification(): string[] {
33
+ return ['Array.isArray(output.hits)', 'output.hits.length >= 0'];
34
+ }
35
+
36
+ async execute(
37
+ ctx: RunContext,
38
+ input: KnowledgeSearchInput
39
+ ): Promise<KnowledgeSearchOutput> {
40
+ const tool = ctx.getTool('search_knowledge');
41
+ if (!tool) {
42
+ throw new Error('search_knowledge tool is not registered');
43
+ }
44
+
45
+ const result = (await tool.call(input)) as KnowledgeSearchOutput;
46
+ ctx.stream?.emit('task', {
47
+ taskId: this.id,
48
+ stage: 'knowledge_hits_found',
49
+ hits: result.hits?.map(hit => ({ docId: hit.id, score: hit.score })),
50
+ });
51
+ return result;
52
+ }
53
+ }
54
+
55
+ export class SummarizeSnippetTask extends Task<
56
+ SummarizeSnippetInput,
57
+ SummarizeSnippetOutput
58
+ > {
59
+ constructor() {
60
+ super('task-knowledge-summarize', 'knowledge.summarize');
61
+ }
62
+
63
+ policyInput(_ctx: RunContext, input: SummarizeSnippetInput): Record<string, unknown> {
64
+ return {
65
+ action: 'summarize_snippet',
66
+ docId: input.docId,
67
+ maxSentences: input.maxSentences,
68
+ focus: input.focus,
69
+ };
70
+ }
71
+
72
+ verification(): string[] {
73
+ return ['typeof output.summary === "string"', 'Array.isArray(output.highlights)'];
74
+ }
75
+
76
+ async execute(
77
+ ctx: RunContext,
78
+ input: SummarizeSnippetInput
79
+ ): Promise<SummarizeSnippetOutput> {
80
+ const tool = ctx.getTool('summarize_snippet');
81
+ if (!tool) {
82
+ throw new Error('summarize_snippet tool is not registered');
83
+ }
84
+
85
+ const result = (await tool.call(input)) as SummarizeSnippetOutput;
86
+ ctx.stream?.emit('task', {
87
+ taskId: this.id,
88
+ stage: 'knowledge_snippet_summarized',
89
+ docId: input.docId,
90
+ title: result.title,
91
+ });
92
+ return result;
93
+ }
94
+ }
95
+
96
+ export class SuggestFollowupsTask extends Task<
97
+ SuggestFollowupsInput,
98
+ SuggestFollowupsOutput
99
+ > {
100
+ constructor() {
101
+ super('task-knowledge-followups', 'knowledge.followups');
102
+ }
103
+
104
+ private resolveDocId(
105
+ ctx: RunContext,
106
+ input: SuggestFollowupsInput
107
+ ): string | undefined {
108
+ const direct = input?.docId;
109
+ if (typeof direct === 'string' && direct.trim()) {
110
+ return direct.trim();
111
+ }
112
+
113
+ const summary = ctx.outputs?.['task-knowledge-summarize'] as
114
+ | SummarizeSnippetOutput
115
+ | undefined;
116
+ const summaryDocId = summary?.docId;
117
+ if (typeof summaryDocId === 'string' && summaryDocId.trim()) {
118
+ return summaryDocId.trim();
119
+ }
120
+
121
+ const search = ctx.outputs?.['task-knowledge-search'] as
122
+ | KnowledgeSearchOutput
123
+ | undefined;
124
+ const firstHitId = search?.hits?.[0]?.id;
125
+ if (typeof firstHitId === 'string' && firstHitId.trim()) {
126
+ return firstHitId.trim();
127
+ }
128
+
129
+ return undefined;
130
+ }
131
+
132
+ policyInput(_ctx: RunContext, input: SuggestFollowupsInput): Record<string, unknown> {
133
+ const docId = this.resolveDocId(_ctx, input);
134
+
135
+ return {
136
+ action: 'suggest_followups',
137
+ docId,
138
+ context: input.context,
139
+ };
140
+ }
141
+
142
+ verification(): string[] {
143
+ return ['Array.isArray(output.suggestions)', 'output.suggestions.length >= 0'];
144
+ }
145
+
146
+ async execute(
147
+ ctx: RunContext,
148
+ input: SuggestFollowupsInput
149
+ ): Promise<SuggestFollowupsOutput> {
150
+ const tool = ctx.getTool('suggest_followups');
151
+ if (!tool) {
152
+ throw new Error('suggest_followups tool is not registered');
153
+ }
154
+
155
+ const docId = this.resolveDocId(ctx, input);
156
+ if (!docId) {
157
+ throw new Error('docId is required');
158
+ }
159
+
160
+ const result = (await tool.call({ ...input, docId })) as SuggestFollowupsOutput;
161
+ ctx.stream?.emit('task', {
162
+ taskId: this.id,
163
+ stage: 'knowledge_followups_suggested',
164
+ docId,
165
+ suggestionCount: result.suggestions.length,
166
+ });
167
+ return result;
168
+ }
169
+ }
@@ -0,0 +1,112 @@
1
+ import { Task, type RunContext } from '@ddse/acm-sdk';
2
+
3
+ export class SearchTask extends Task<{ query: string }, { results: string[] }> {
4
+ constructor() {
5
+ super('search-task', 'search');
6
+ }
7
+
8
+ async execute(ctx: RunContext, input: { query: string }): Promise<{ results: string[] }> {
9
+ const tool = ctx.getTool('search');
10
+ if (!tool) {
11
+ throw new Error('Search tool not found');
12
+ }
13
+
14
+ const result = await tool.call(input);
15
+ ctx.stream?.emit('task', { taskId: this.id, step: 'search_complete', results: result });
16
+ return result;
17
+ }
18
+ }
19
+
20
+ export class EnrichAndActTask extends Task<
21
+ { searchQuery: string; orderId: string },
22
+ { action: string; details: any }
23
+ > {
24
+ constructor() {
25
+ super('enrich-and-act-task', 'enrich_and_act');
26
+ }
27
+
28
+ async execute(
29
+ ctx: RunContext,
30
+ input: { searchQuery: string; orderId: string }
31
+ ): Promise<{ action: string; details: any }> {
32
+ const searchTool = ctx.getTool('search');
33
+ if (!searchTool) throw new Error('Search tool not found');
34
+
35
+ const searchResult = await searchTool.call({ query: input.searchQuery });
36
+ ctx.stream?.emit('task', { taskId: this.id, step: 'search_done' });
37
+
38
+ const extractTool = ctx.getTool('extract_entities');
39
+ if (!extractTool) throw new Error('Extract entities tool not found');
40
+
41
+ const entities = await extractTool.call({ text: JSON.stringify(searchResult) });
42
+ ctx.stream?.emit('task', { taskId: this.id, step: 'entities_extracted' });
43
+
44
+ const riskTool = ctx.getTool('assess_risk');
45
+ if (!riskTool) throw new Error('Risk assessment tool not found');
46
+
47
+ const risk = await riskTool.call({ context: { entities, orderId: input.orderId } });
48
+ ctx.stream?.emit('task', { taskId: this.id, step: 'risk_assessed', risk });
49
+
50
+ return {
51
+ action: risk.riskTier === 'HIGH' ? 'ESCALATE' : 'PROCEED',
52
+ details: { searchResult, entities, risk },
53
+ };
54
+ }
55
+
56
+ policyInput(ctx: RunContext, input: any): Record<string, unknown> {
57
+ return {
58
+ orderId: input.orderId,
59
+ action: 'enrich_and_assess',
60
+ };
61
+ }
62
+
63
+ verification(): string[] {
64
+ return ['output.action !== undefined', 'output.details !== undefined'];
65
+ }
66
+ }
67
+
68
+ export class RefundFlowTask extends Task<
69
+ { orderId: string; amount: number },
70
+ { transactionId: string; notified: boolean }
71
+ > {
72
+ constructor() {
73
+ super('refund-flow-task', 'refund_flow');
74
+ }
75
+
76
+ async execute(
77
+ ctx: RunContext,
78
+ input: { orderId: string; amount: number }
79
+ ): Promise<{ transactionId: string; notified: boolean }> {
80
+ const refundTool = ctx.getTool('create_refund_txn');
81
+ if (!refundTool) throw new Error('Refund tool not found');
82
+
83
+ const txn = await refundTool.call(input);
84
+ ctx.stream?.emit('task', { taskId: this.id, step: 'refund_created', txn });
85
+
86
+ const notifyTool = ctx.getTool('notify_supervisor_legacy');
87
+ if (!notifyTool) throw new Error('Notify tool not found');
88
+
89
+ const notification = await notifyTool.call({
90
+ message: `Refund ${txn.transactionId} created for order ${input.orderId}`,
91
+ channel: 'email',
92
+ });
93
+ ctx.stream?.emit('task', { taskId: this.id, step: 'notification_sent' });
94
+
95
+ return {
96
+ transactionId: txn.transactionId,
97
+ notified: notification.sent,
98
+ };
99
+ }
100
+
101
+ policyInput(ctx: RunContext, input: any): Record<string, unknown> {
102
+ return {
103
+ orderId: input.orderId,
104
+ amount: input.amount,
105
+ action: 'create_refund',
106
+ };
107
+ }
108
+
109
+ verification(): string[] {
110
+ return ['output.transactionId !== undefined', 'output.notified === true'];
111
+ }
112
+ }