@assemble-dev/sdk 0.0.1

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/dist/index.js ADDED
@@ -0,0 +1,3877 @@
1
+ // src/agents/agent-definition.ts
2
+ function agent(config) {
3
+ return Object.freeze({
4
+ ...config,
5
+ maxSteps: config.maxSteps ?? 1
6
+ });
7
+ }
8
+
9
+ // src/agents/agent-runner.ts
10
+ import { AppErrorCode as AppErrorCode3 } from "@assemble-dev/shared-types/errors";
11
+ import { AppError as AppError3 } from "@assemble-dev/shared-utils/errors";
12
+
13
+ // src/agents/agent-messages.ts
14
+ function serializeAgentInputToText(input) {
15
+ return typeof input === "string" ? input : JSON.stringify(input);
16
+ }
17
+ async function buildAgentChatMessages(definition, input, threadMemory) {
18
+ const priorThreadMessages = threadMemory === void 0 ? [] : await threadMemory.listThreadMessages();
19
+ return [
20
+ { role: "system", content: definition.instructions },
21
+ ...priorThreadMessages.map(mapRunMessageToChatMessage),
22
+ { role: "user", content: serializeAgentInputToText(input) }
23
+ ];
24
+ }
25
+ function mapRunMessageToChatMessage(message) {
26
+ if (message.toolName === void 0) {
27
+ return { role: message.role, content: message.content };
28
+ }
29
+ return {
30
+ role: message.role,
31
+ content: message.content,
32
+ toolName: message.toolName
33
+ };
34
+ }
35
+ async function appendAgentExchangeToThreadMemory(threadMemory, agentName, input, assistantText, now) {
36
+ if (threadMemory === void 0) {
37
+ return;
38
+ }
39
+ await threadMemory.appendThreadMessage({
40
+ role: "user",
41
+ content: serializeAgentInputToText(input),
42
+ timestamp: now().toISOString()
43
+ });
44
+ await threadMemory.appendThreadMessage({
45
+ role: "assistant",
46
+ content: assistantText,
47
+ agentName,
48
+ timestamp: now().toISOString()
49
+ });
50
+ }
51
+
52
+ // src/agents/agent-output-generation.ts
53
+ import { AppErrorCode as AppErrorCode2 } from "@assemble-dev/shared-types/errors";
54
+ import { AppError as AppError2 } from "@assemble-dev/shared-utils/errors";
55
+ import { z } from "zod";
56
+
57
+ // src/agents/agent-model-timeout.ts
58
+ import { AppErrorCode } from "@assemble-dev/shared-types/errors";
59
+ import { AppError } from "@assemble-dev/shared-utils/errors";
60
+ var agentTimeoutErrorCode = "AGENT_TIMEOUT";
61
+ async function raceModelCallAgainstTimeout(modelCall, timeoutMs, agentName) {
62
+ if (timeoutMs === void 0) {
63
+ return await modelCall;
64
+ }
65
+ return await new Promise((resolve, reject) => {
66
+ const timeoutHandle = setTimeout(() => {
67
+ reject(buildAgentTimeoutError(agentName, timeoutMs));
68
+ }, timeoutMs);
69
+ void modelCall.then(
70
+ (modelResult) => {
71
+ clearTimeout(timeoutHandle);
72
+ resolve(modelResult);
73
+ },
74
+ (modelError) => {
75
+ clearTimeout(timeoutHandle);
76
+ reject(modelError);
77
+ }
78
+ );
79
+ });
80
+ }
81
+ function buildAgentTimeoutError(agentName, timeoutMs) {
82
+ return new AppError({
83
+ code: AppErrorCode.InternalServerError,
84
+ message: `Agent "${agentName}" timed out after ${timeoutMs}ms`,
85
+ statusCode: 504,
86
+ details: {
87
+ errorCode: agentTimeoutErrorCode,
88
+ timeoutMs
89
+ }
90
+ });
91
+ }
92
+
93
+ // src/agents/agent-output-generation.ts
94
+ async function generateAgentOutput(definition, input, threadMemory) {
95
+ const messages = await buildAgentChatMessages(definition, input, threadMemory);
96
+ const outputSchema = definition.outputSchema;
97
+ if (outputSchema === void 0) {
98
+ const completion = await raceModelCallAgainstTimeout(
99
+ definition.model.generateChatCompletion({ messages }),
100
+ definition.timeoutMs,
101
+ definition.name
102
+ );
103
+ return {
104
+ output: completion.content,
105
+ rawText: completion.content,
106
+ tokenUsage: completion.tokenUsage
107
+ };
108
+ }
109
+ return await generateStructuredAgentOutput(definition, outputSchema, messages);
110
+ }
111
+ async function generateStructuredAgentOutput(definition, outputSchema, messages) {
112
+ try {
113
+ return await requestStructuredAgentOutput(definition, outputSchema, messages);
114
+ } catch (error) {
115
+ if (!(error instanceof AppError2) || error.code !== AppErrorCode2.ValidationFailed) {
116
+ throw error;
117
+ }
118
+ return await retryStructuredAgentOutput(definition, outputSchema, messages, error);
119
+ }
120
+ }
121
+ async function retryStructuredAgentOutput(definition, outputSchema, messages, validationError) {
122
+ const retryMessages = [
123
+ ...messages,
124
+ buildSchemaCorrectionMessage(validationError.message)
125
+ ];
126
+ try {
127
+ return await requestStructuredAgentOutput(definition, outputSchema, retryMessages);
128
+ } catch (retryError) {
129
+ if (!(retryError instanceof AppError2) || retryError.code !== AppErrorCode2.ValidationFailed) {
130
+ throw retryError;
131
+ }
132
+ throw new AppError2({
133
+ code: AppErrorCode2.ValidationFailed,
134
+ message: `Agent "${definition.name}" structured output failed schema validation after one retry: ${retryError.message}`,
135
+ statusCode: 422,
136
+ cause: retryError
137
+ });
138
+ }
139
+ }
140
+ async function requestStructuredAgentOutput(definition, outputSchema, messages) {
141
+ try {
142
+ const structuredResult = await raceModelCallAgainstTimeout(
143
+ definition.model.generateStructuredOutput({
144
+ messages,
145
+ schema: outputSchema
146
+ }),
147
+ definition.timeoutMs,
148
+ definition.name
149
+ );
150
+ return {
151
+ output: structuredResult.value,
152
+ rawText: JSON.stringify(structuredResult.value),
153
+ tokenUsage: structuredResult.tokenUsage
154
+ };
155
+ } catch (error) {
156
+ if (error instanceof z.ZodError) {
157
+ throw new AppError2({
158
+ code: AppErrorCode2.ValidationFailed,
159
+ message: `Structured output failed schema validation: ${error.message}`,
160
+ statusCode: 422
161
+ });
162
+ }
163
+ throw error;
164
+ }
165
+ }
166
+ function buildSchemaCorrectionMessage(validationErrorMessage) {
167
+ return {
168
+ role: "user",
169
+ content: `The previous response failed schema validation: ${validationErrorMessage}. Respond again with JSON that matches the required schema exactly.`
170
+ };
171
+ }
172
+
173
+ // src/agents/agent-trace-events.ts
174
+ import { JsonValueSchema } from "@assemble-dev/shared-types/json";
175
+ import { generateTraceEventId } from "@assemble-dev/shared-utils/ids";
176
+ function buildAgentEventBase(eventContext, parentEventId) {
177
+ const runState = eventContext.traceBus.getRunState();
178
+ return {
179
+ id: generateTraceEventId(),
180
+ runId: runState.runId,
181
+ workflowName: runState.workflowName,
182
+ timestamp: eventContext.now().toISOString(),
183
+ parentEventId,
184
+ redacted: false
185
+ };
186
+ }
187
+ function emitAgentStartedEvent(eventContext, input, parentEventId) {
188
+ return eventContext.traceBus.emitTraceEvent({
189
+ ...buildAgentEventBase(eventContext, parentEventId),
190
+ type: "agent.started",
191
+ agentName: eventContext.agentName,
192
+ input
193
+ });
194
+ }
195
+ function emitAgentCompletedEvent(eventContext, startedEventId, output, latencyMs) {
196
+ eventContext.traceBus.emitTraceEvent({
197
+ ...buildAgentEventBase(eventContext, startedEventId),
198
+ type: "agent.completed",
199
+ agentName: eventContext.agentName,
200
+ output: JsonValueSchema.parse(output),
201
+ latencyMs
202
+ });
203
+ }
204
+ function emitAgentFailedEvent(eventContext, parentEventId, error, latencyMs) {
205
+ eventContext.traceBus.emitTraceEvent({
206
+ ...buildAgentEventBase(eventContext, parentEventId),
207
+ type: "agent.failed",
208
+ agentName: eventContext.agentName,
209
+ error: {
210
+ message: error.message,
211
+ code: resolveAgentTraceErrorCode(error)
212
+ },
213
+ latencyMs
214
+ });
215
+ }
216
+ function resolveAgentTraceErrorCode(error) {
217
+ const detailsErrorCode = error.details?.errorCode;
218
+ return typeof detailsErrorCode === "string" ? detailsErrorCode : error.code;
219
+ }
220
+
221
+ // src/agents/agent-runner.ts
222
+ async function runAgent(runInput) {
223
+ const now = runInput.now ?? (() => /* @__PURE__ */ new Date());
224
+ const eventContext = {
225
+ traceBus: runInput.traceBus,
226
+ agentName: runInput.definition.name,
227
+ now
228
+ };
229
+ const startedAtMs = now().getTime();
230
+ const validatedInput = validateAgentInput(runInput, eventContext, startedAtMs);
231
+ const startedEvent = emitAgentStartedEvent(
232
+ eventContext,
233
+ validatedInput,
234
+ runInput.parentEventId ?? null
235
+ );
236
+ invokeAgentStartHook(runInput.definition, validatedInput);
237
+ try {
238
+ return await completeAgentRun(
239
+ runInput,
240
+ eventContext,
241
+ validatedInput,
242
+ startedEvent.id,
243
+ startedAtMs
244
+ );
245
+ } catch (error) {
246
+ const appError = mapCaughtErrorToAppError(
247
+ error instanceof Error ? error : new Error(String(error))
248
+ );
249
+ emitAgentFailedEvent(
250
+ eventContext,
251
+ startedEvent.id,
252
+ appError,
253
+ now().getTime() - startedAtMs
254
+ );
255
+ invokeAgentErrorHook(runInput.definition, appError);
256
+ throw appError;
257
+ }
258
+ }
259
+ async function completeAgentRun(runInput, eventContext, validatedInput, startedEventId, startedAtMs) {
260
+ const generation = await generateAgentOutput(
261
+ runInput.definition,
262
+ validatedInput,
263
+ runInput.threadMemory
264
+ );
265
+ await appendAgentExchangeToThreadMemory(
266
+ runInput.threadMemory,
267
+ runInput.definition.name,
268
+ validatedInput,
269
+ generation.rawText,
270
+ eventContext.now
271
+ );
272
+ const latencyMs = eventContext.now().getTime() - startedAtMs;
273
+ emitAgentCompletedEvent(eventContext, startedEventId, generation.output, latencyMs);
274
+ invokeAgentCompleteHook(runInput.definition, generation.output);
275
+ return {
276
+ output: generation.output,
277
+ rawText: generation.rawText,
278
+ tokenUsage: generation.tokenUsage,
279
+ latencyMs,
280
+ agentCallEventId: startedEventId
281
+ };
282
+ }
283
+ function validateAgentInput(runInput, eventContext, startedAtMs) {
284
+ const inputSchema = runInput.definition.inputSchema;
285
+ if (inputSchema === void 0) {
286
+ return runInput.input;
287
+ }
288
+ const parsedInput = inputSchema.safeParse(runInput.input);
289
+ if (parsedInput.success) {
290
+ return parsedInput.data;
291
+ }
292
+ const validationError = new AppError3({
293
+ code: AppErrorCode3.ValidationFailed,
294
+ message: `Agent "${runInput.definition.name}" input failed schema validation: ${parsedInput.error.message}`,
295
+ statusCode: 422
296
+ });
297
+ emitAgentFailedEvent(
298
+ eventContext,
299
+ runInput.parentEventId ?? null,
300
+ validationError,
301
+ eventContext.now().getTime() - startedAtMs
302
+ );
303
+ invokeAgentErrorHook(runInput.definition, validationError);
304
+ throw validationError;
305
+ }
306
+ function mapCaughtErrorToAppError(error) {
307
+ if (error instanceof AppError3) {
308
+ return error;
309
+ }
310
+ return new AppError3({
311
+ code: AppErrorCode3.InternalServerError,
312
+ message: error.message,
313
+ statusCode: 500,
314
+ cause: error
315
+ });
316
+ }
317
+ function invokeAgentStartHook(definition, input) {
318
+ invokeAgentHookSafely(
319
+ () => definition.onAgentStart?.({ agentName: definition.name, input })
320
+ );
321
+ }
322
+ function invokeAgentCompleteHook(definition, output) {
323
+ invokeAgentHookSafely(
324
+ () => definition.onAgentComplete?.({ agentName: definition.name, output })
325
+ );
326
+ }
327
+ function invokeAgentErrorHook(definition, error) {
328
+ invokeAgentHookSafely(
329
+ () => definition.onAgentError?.({ agentName: definition.name, error })
330
+ );
331
+ }
332
+ function invokeAgentHookSafely(invokeHook) {
333
+ try {
334
+ invokeHook();
335
+ } catch {
336
+ return;
337
+ }
338
+ }
339
+
340
+ // src/tools/tool-definition.ts
341
+ import { z as z2 } from "zod";
342
+ var ToolRetryConfigSchema = z2.object({
343
+ maxAttempts: z2.number().int().min(1)
344
+ });
345
+ var ToolDefinitionOptionsSchema = z2.object({
346
+ name: z2.string().min(1),
347
+ description: z2.string().min(1),
348
+ allowedAgentNames: z2.array(z2.string().min(1)).optional(),
349
+ requiresApproval: z2.boolean().optional(),
350
+ retry: ToolRetryConfigSchema.optional(),
351
+ timeoutMs: z2.number().int().positive().optional(),
352
+ sensitiveFieldNames: z2.array(z2.string().min(1)).optional(),
353
+ dryRun: z2.boolean().optional()
354
+ });
355
+ var defaultToolRetryConfig = Object.freeze({
356
+ maxAttempts: 1
357
+ });
358
+ function tool(config) {
359
+ const parsedOptions = ToolDefinitionOptionsSchema.parse({
360
+ name: config.name,
361
+ description: config.description,
362
+ allowedAgentNames: config.allowedAgentNames,
363
+ requiresApproval: config.requiresApproval,
364
+ retry: config.retry,
365
+ timeoutMs: config.timeoutMs,
366
+ sensitiveFieldNames: config.sensitiveFieldNames,
367
+ dryRun: config.dryRun
368
+ });
369
+ return Object.freeze({
370
+ name: parsedOptions.name,
371
+ description: parsedOptions.description,
372
+ inputSchema: config.inputSchema,
373
+ outputSchema: config.outputSchema ?? null,
374
+ execute: config.execute,
375
+ allowedAgentNames: parsedOptions.allowedAgentNames ?? null,
376
+ requiresApproval: parsedOptions.requiresApproval ?? false,
377
+ retry: parsedOptions.retry ?? defaultToolRetryConfig,
378
+ timeoutMs: parsedOptions.timeoutMs ?? null,
379
+ sensitiveFieldNames: parsedOptions.sensitiveFieldNames ?? [],
380
+ dryRun: parsedOptions.dryRun ?? false
381
+ });
382
+ }
383
+
384
+ // src/tools/tool-broker.ts
385
+ import { AppErrorCode as AppErrorCode4 } from "@assemble-dev/shared-types/errors";
386
+ import { AppError as AppError4 } from "@assemble-dev/shared-utils/errors";
387
+ import { generateApprovalId, generateTraceEventId as generateTraceEventId3 } from "@assemble-dev/shared-utils";
388
+ import {
389
+ resolveRedactionMode
390
+ } from "@assemble-dev/shared-utils/redaction";
391
+
392
+ // src/policies/policy-engine.ts
393
+ import {
394
+ PolicyDecisionSchema
395
+ } from "@assemble-dev/shared-types/runs";
396
+
397
+ // src/policies/tool-policy.ts
398
+ var toolPolicyEffectNames = [
399
+ "block",
400
+ "requireApproval",
401
+ "redact",
402
+ "logOnly"
403
+ ];
404
+
405
+ // src/policies/policy-engine.ts
406
+ var defaultPolicyName = "default-policy";
407
+ var policyActionPrecedence = [
408
+ "block",
409
+ "requireApproval",
410
+ "redact",
411
+ "logOnly",
412
+ "allow"
413
+ ];
414
+ function evaluateToolPolicies(input) {
415
+ const applicablePolicies = input.policies.filter(
416
+ (policy2) => doesPolicyApplyToTool(policy2, input.context.toolName)
417
+ );
418
+ const [firstPolicy, ...remainingPolicies] = applicablePolicies;
419
+ if (firstPolicy === void 0) {
420
+ return buildDefaultAllowEvaluation(input.context, input.timestamp);
421
+ }
422
+ const firstDecision = evaluateSingleToolPolicy(
423
+ firstPolicy,
424
+ input.context,
425
+ input.timestamp
426
+ );
427
+ const remainingDecisions = remainingPolicies.map(
428
+ (policy2) => evaluateSingleToolPolicy(policy2, input.context, input.timestamp)
429
+ );
430
+ const strongestDecision = resolveStrongestPolicyDecision(
431
+ firstDecision,
432
+ remainingDecisions
433
+ );
434
+ return {
435
+ action: strongestDecision.action,
436
+ reason: strongestDecision.reason,
437
+ policyName: strongestDecision.policyName,
438
+ decisions: [firstDecision, ...remainingDecisions]
439
+ };
440
+ }
441
+ function doesPolicyApplyToTool(policy2, toolName) {
442
+ if (policy2.appliesToToolNames === void 0) {
443
+ return true;
444
+ }
445
+ return policy2.appliesToToolNames.includes(toolName);
446
+ }
447
+ function evaluateSingleToolPolicy(policy2, context, timestamp) {
448
+ for (const effectName of toolPolicyEffectNames) {
449
+ const triggeredOutcome = executeToolPolicyRule(policy2, effectName, context);
450
+ if (triggeredOutcome !== null) {
451
+ return buildPolicyDecision({
452
+ policyName: policy2.name,
453
+ action: triggeredOutcome.action,
454
+ reason: triggeredOutcome.reason,
455
+ context,
456
+ timestamp
457
+ });
458
+ }
459
+ }
460
+ return buildPolicyDecision({
461
+ policyName: policy2.name,
462
+ action: "allow",
463
+ reason: "no rules matched",
464
+ context,
465
+ timestamp
466
+ });
467
+ }
468
+ function executeToolPolicyRule(policy2, effectName, context) {
469
+ const evaluateRule = policy2[effectName];
470
+ if (evaluateRule === void 0) {
471
+ return null;
472
+ }
473
+ try {
474
+ const ruleResult = evaluateRule(context);
475
+ if (ruleResult === false) {
476
+ return null;
477
+ }
478
+ if (ruleResult === true) {
479
+ return {
480
+ action: effectName,
481
+ reason: buildGeneratedEffectReason(effectName, policy2.name)
482
+ };
483
+ }
484
+ return {
485
+ action: effectName,
486
+ reason: ruleResult
487
+ };
488
+ } catch (caughtError) {
489
+ const errorMessage = caughtError instanceof Error ? caughtError.message : String(caughtError);
490
+ return {
491
+ action: "block",
492
+ reason: `blocked by policy ${policy2.name}: ${effectName} rule threw: ${errorMessage}`
493
+ };
494
+ }
495
+ }
496
+ function buildGeneratedEffectReason(effectName, policyName) {
497
+ const reasonsByEffectName = {
498
+ block: `blocked by policy ${policyName}`,
499
+ requireApproval: `approval required by policy ${policyName}`,
500
+ redact: `redacted by policy ${policyName}`,
501
+ logOnly: `logged by policy ${policyName}`
502
+ };
503
+ return reasonsByEffectName[effectName];
504
+ }
505
+ function buildPolicyDecision(input) {
506
+ return PolicyDecisionSchema.parse({
507
+ policyName: input.policyName,
508
+ action: input.action,
509
+ agentName: input.context.agentName,
510
+ toolName: input.context.toolName,
511
+ reason: input.reason,
512
+ timestamp: input.timestamp
513
+ });
514
+ }
515
+ function buildDefaultAllowEvaluation(context, timestamp) {
516
+ const defaultDecision = buildPolicyDecision({
517
+ policyName: defaultPolicyName,
518
+ action: "allow",
519
+ reason: `no policies matched tool ${context.toolName}`,
520
+ context,
521
+ timestamp
522
+ });
523
+ return {
524
+ action: defaultDecision.action,
525
+ reason: defaultDecision.reason,
526
+ policyName: defaultDecision.policyName,
527
+ decisions: [defaultDecision]
528
+ };
529
+ }
530
+ function resolveStrongestPolicyDecision(firstDecision, remainingDecisions) {
531
+ let strongestDecision = firstDecision;
532
+ for (const decision of remainingDecisions) {
533
+ const decisionRank = policyActionPrecedence.indexOf(decision.action);
534
+ const strongestRank = policyActionPrecedence.indexOf(
535
+ strongestDecision.action
536
+ );
537
+ if (decisionRank < strongestRank) {
538
+ strongestDecision = decision;
539
+ }
540
+ }
541
+ return strongestDecision;
542
+ }
543
+
544
+ // src/policies/policy-evaluated-event.ts
545
+ import {
546
+ PolicyEvaluatedEventSchema
547
+ } from "@assemble-dev/shared-types/trace-events";
548
+ function buildPolicyEvaluatedEvent(input) {
549
+ return PolicyEvaluatedEventSchema.parse({
550
+ type: "policy.evaluated",
551
+ id: input.id,
552
+ runId: input.runId,
553
+ workflowName: input.workflowName,
554
+ timestamp: input.timestamp,
555
+ parentEventId: input.parentEventId,
556
+ redacted: input.redacted,
557
+ policyName: input.policyName,
558
+ action: input.action,
559
+ reason: input.reason,
560
+ toolName: input.toolName,
561
+ agentName: input.agentName
562
+ });
563
+ }
564
+ function buildPolicyEvaluatedEvents(input) {
565
+ return input.decisionEvents.map(
566
+ (decisionEvent) => buildPolicyEvaluatedEvent({
567
+ id: decisionEvent.id,
568
+ runId: input.runId,
569
+ workflowName: input.workflowName,
570
+ timestamp: input.timestamp,
571
+ parentEventId: input.parentEventId,
572
+ redacted: input.redacted,
573
+ policyName: decisionEvent.decision.policyName,
574
+ action: decisionEvent.decision.action,
575
+ reason: decisionEvent.decision.reason,
576
+ toolName: input.toolName,
577
+ agentName: input.agentName
578
+ })
579
+ );
580
+ }
581
+
582
+ // src/tools/tool-execution.ts
583
+ function buildToolValidationFailureMessage(target, toolName, issues) {
584
+ const issueSummary = issues.map((issue) => issue.message).join("; ");
585
+ return `${target} validation failed for tool ${toolName}: ${issueSummary}`;
586
+ }
587
+ async function runToolExecutionAttempts(toolDefinition, input) {
588
+ let lastError = {
589
+ code: "EXECUTION_FAILED",
590
+ message: `tool ${toolDefinition.name} failed before executing`
591
+ };
592
+ for (let attempt = 1; attempt <= toolDefinition.retry.maxAttempts; attempt += 1) {
593
+ try {
594
+ return { output: await executeToolAttemptWithTimeout(toolDefinition, input) };
595
+ } catch (caughtError) {
596
+ lastError = {
597
+ code: "EXECUTION_FAILED",
598
+ message: caughtError instanceof Error ? caughtError.message : String(caughtError)
599
+ };
600
+ }
601
+ }
602
+ return { error: lastError };
603
+ }
604
+ function validateToolExecutionOutput(toolDefinition, output) {
605
+ if (toolDefinition.outputSchema === null) {
606
+ return { output };
607
+ }
608
+ const parsedOutput = toolDefinition.outputSchema.safeParse(output);
609
+ if (parsedOutput.success) {
610
+ return { output: parsedOutput.data };
611
+ }
612
+ return {
613
+ error: {
614
+ code: "VALIDATION_FAILED",
615
+ message: buildToolValidationFailureMessage(
616
+ "output",
617
+ toolDefinition.name,
618
+ parsedOutput.error.issues
619
+ )
620
+ }
621
+ };
622
+ }
623
+ async function executeToolAttemptWithTimeout(toolDefinition, input) {
624
+ const timeoutMs = toolDefinition.timeoutMs;
625
+ if (timeoutMs === null) {
626
+ return await toolDefinition.execute(input);
627
+ }
628
+ return await new Promise((resolveAttempt, rejectAttempt) => {
629
+ const timeoutHandle = setTimeout(() => {
630
+ rejectAttempt(
631
+ new Error(
632
+ `tool ${toolDefinition.name} timed out after ${timeoutMs}ms`
633
+ )
634
+ );
635
+ }, timeoutMs);
636
+ const settleWithOutput = (output) => {
637
+ clearTimeout(timeoutHandle);
638
+ resolveAttempt(output);
639
+ };
640
+ const settleWithError = (executionError) => {
641
+ clearTimeout(timeoutHandle);
642
+ rejectAttempt(executionError);
643
+ };
644
+ void toolDefinition.execute(input).then(settleWithOutput, settleWithError);
645
+ });
646
+ }
647
+
648
+ // src/tools/tool-trace-events.ts
649
+ import { generateTraceEventId as generateTraceEventId2 } from "@assemble-dev/shared-utils";
650
+ import {
651
+ redactTracePayload
652
+ } from "@assemble-dev/shared-utils/redaction";
653
+ function buildToolEventBase(context, parentEventId, redacted) {
654
+ return {
655
+ id: generateTraceEventId2(),
656
+ runId: context.runId,
657
+ workflowName: context.workflowName,
658
+ timestamp: context.now().toISOString(),
659
+ parentEventId,
660
+ redacted
661
+ };
662
+ }
663
+ function emitToolRequestedEvent(context, input, mode) {
664
+ const redaction = redactTracePayload(input, {
665
+ mode,
666
+ sensitiveFieldNames: context.sensitiveFieldNames
667
+ });
668
+ const emittedEvent = context.traceBus.emitTraceEvent({
669
+ ...buildToolEventBase(context, context.parentEventId, redaction.redacted),
670
+ type: "tool.requested",
671
+ toolName: context.toolName,
672
+ agentName: context.agentName,
673
+ input: redaction.payload
674
+ });
675
+ return emittedEvent.id;
676
+ }
677
+ function emitToolBlockedEvent(context, requestEventId, policyName, reason) {
678
+ context.traceBus.emitTraceEvent({
679
+ ...buildToolEventBase(context, requestEventId, false),
680
+ type: "tool.blocked",
681
+ toolName: context.toolName,
682
+ agentName: context.agentName,
683
+ policyName,
684
+ reason
685
+ });
686
+ }
687
+ function emitToolCompletedEvent(context, requestEventId, output, latencyMs) {
688
+ const redaction = redactTracePayload(output, {
689
+ mode: context.redactionMode,
690
+ sensitiveFieldNames: context.sensitiveFieldNames
691
+ });
692
+ context.traceBus.emitTraceEvent({
693
+ ...buildToolEventBase(context, requestEventId, redaction.redacted),
694
+ type: "tool.completed",
695
+ toolName: context.toolName,
696
+ agentName: context.agentName,
697
+ output: redaction.payload,
698
+ latencyMs
699
+ });
700
+ }
701
+ function emitToolFailedEvent(context, requestEventId, error, latencyMs) {
702
+ const redaction = redactTracePayload(error.message, {
703
+ mode: context.redactionMode,
704
+ sensitiveFieldNames: context.sensitiveFieldNames
705
+ });
706
+ const message = typeof redaction.payload === "string" ? redaction.payload : JSON.stringify(redaction.payload);
707
+ context.traceBus.emitTraceEvent({
708
+ ...buildToolEventBase(context, requestEventId, redaction.redacted),
709
+ type: "tool.failed",
710
+ toolName: context.toolName,
711
+ agentName: context.agentName,
712
+ error: { message, code: error.code },
713
+ latencyMs
714
+ });
715
+ }
716
+ function emitToolApprovedEvent(context, toolRequestEventId, approvalId) {
717
+ const emittedEvent = context.traceBus.emitTraceEvent({
718
+ ...buildToolEventBase(context, toolRequestEventId, false),
719
+ type: "tool.approved",
720
+ toolName: context.toolName,
721
+ agentName: context.agentName,
722
+ approvalId
723
+ });
724
+ return emittedEvent.id;
725
+ }
726
+ function emitApprovalRequiredEvent(context, toolRequestEventId, approvalId, reason) {
727
+ context.traceBus.emitTraceEvent({
728
+ ...buildToolEventBase(context, toolRequestEventId, false),
729
+ type: "approval.required",
730
+ approvalId,
731
+ toolName: context.toolName,
732
+ agentName: context.agentName,
733
+ reason
734
+ });
735
+ }
736
+
737
+ // src/tools/tool-execution-outcome.ts
738
+ async function executeToolAndEmitOutcome(context, toolDefinition, input, requestEventId, mockExecution) {
739
+ const startedAtMs = context.now().getTime();
740
+ if (toolDefinition.dryRun || mockExecution !== void 0) {
741
+ return completeToolCallWithoutExecution(
742
+ context,
743
+ toolDefinition,
744
+ input,
745
+ requestEventId,
746
+ mockExecution,
747
+ startedAtMs
748
+ );
749
+ }
750
+ const executionResult = await runToolExecutionAttempts(toolDefinition, input);
751
+ if ("error" in executionResult) {
752
+ return failToolCallAfterRequest(
753
+ context,
754
+ requestEventId,
755
+ executionResult.error,
756
+ calculateElapsedMs(context, startedAtMs)
757
+ );
758
+ }
759
+ const validatedOutput = validateToolExecutionOutput(
760
+ toolDefinition,
761
+ executionResult.output
762
+ );
763
+ if ("error" in validatedOutput) {
764
+ return failToolCallAfterRequest(
765
+ context,
766
+ requestEventId,
767
+ validatedOutput.error,
768
+ calculateElapsedMs(context, startedAtMs)
769
+ );
770
+ }
771
+ const latencyMs = calculateElapsedMs(context, startedAtMs);
772
+ emitToolCompletedEvent(context, requestEventId, validatedOutput.output, latencyMs);
773
+ return {
774
+ status: "completed",
775
+ output: validatedOutput.output,
776
+ toolRequestEventId: requestEventId,
777
+ latencyMs
778
+ };
779
+ }
780
+ function completeToolCallWithoutExecution(context, toolDefinition, input, requestEventId, mockExecution, startedAtMs) {
781
+ const output = mockExecution !== void 0 ? mockExecution(input) : { dryRun: true, toolName: toolDefinition.name };
782
+ const latencyMs = calculateElapsedMs(context, startedAtMs);
783
+ emitToolCompletedEvent(context, requestEventId, output, latencyMs);
784
+ return {
785
+ status: "completed",
786
+ output,
787
+ toolRequestEventId: requestEventId,
788
+ latencyMs
789
+ };
790
+ }
791
+ function failToolCallAfterRequest(context, requestEventId, error, latencyMs) {
792
+ emitToolFailedEvent(context, requestEventId, error, latencyMs);
793
+ return { status: "failed", error };
794
+ }
795
+ function calculateElapsedMs(context, startedAtMs) {
796
+ return Math.max(0, context.now().getTime() - startedAtMs);
797
+ }
798
+
799
+ // src/tools/tool-broker.ts
800
+ var agentAllowlistPolicyName = "agent-allowlist";
801
+ var staticApprovalReason = "tool requires approval";
802
+ async function executeToolCall(callInput) {
803
+ const context = buildToolBrokerContext(callInput);
804
+ const parsedInput = callInput.tool.inputSchema.safeParse(callInput.input);
805
+ if (!parsedInput.success) {
806
+ return failToolCallForInvalidInput(
807
+ context,
808
+ callInput.input,
809
+ parsedInput.error.issues
810
+ );
811
+ }
812
+ if (callInput.grantedApproval !== void 0) {
813
+ return await executeGrantedApprovalToolCall(
814
+ context,
815
+ callInput,
816
+ parsedInput.data,
817
+ callInput.grantedApproval
818
+ );
819
+ }
820
+ if (!isAgentAllowedToUseTool(callInput.tool, callInput.agentName)) {
821
+ return blockToolCallForDisallowedAgent(context, parsedInput.data);
822
+ }
823
+ const evaluation = evaluatePoliciesForToolCall(
824
+ context,
825
+ callInput.policies,
826
+ parsedInput.data
827
+ );
828
+ if (evaluation !== null && evaluation.action === "block") {
829
+ return blockToolCallByPolicy(context, parsedInput.data, evaluation);
830
+ }
831
+ const requestEventId = emitToolRequestedEvent(
832
+ context,
833
+ parsedInput.data,
834
+ resolveInputRedactionMode(context, evaluation)
835
+ );
836
+ const approvalReason = resolveApprovalReason(callInput.tool, evaluation);
837
+ if (approvalReason !== null) {
838
+ return requireApprovalForToolCall(context, requestEventId, approvalReason);
839
+ }
840
+ return await executeToolAndEmitOutcome(
841
+ context,
842
+ callInput.tool,
843
+ parsedInput.data,
844
+ requestEventId,
845
+ callInput.mockExecution
846
+ );
847
+ }
848
+ function buildToolBrokerContext(callInput) {
849
+ const runState = callInput.traceBus.getRunState();
850
+ return {
851
+ toolName: callInput.tool.name,
852
+ agentName: callInput.agentName,
853
+ runId: runState.runId,
854
+ workflowName: runState.workflowName,
855
+ parentEventId: callInput.parentEventId ?? null,
856
+ traceBus: callInput.traceBus,
857
+ now: callInput.now ?? (() => /* @__PURE__ */ new Date()),
858
+ redactionMode: resolveRedactionMode(callInput.redactionMode),
859
+ sensitiveFieldNames: callInput.tool.sensitiveFieldNames
860
+ };
861
+ }
862
+ function isAgentAllowedToUseTool(toolDefinition, agentName) {
863
+ if (toolDefinition.allowedAgentNames === null) {
864
+ return true;
865
+ }
866
+ return toolDefinition.allowedAgentNames.includes(agentName);
867
+ }
868
+ function resolveInputRedactionMode(context, evaluation) {
869
+ if (evaluation !== null && evaluation.action === "redact") {
870
+ return "redacted";
871
+ }
872
+ return context.redactionMode;
873
+ }
874
+ function resolveApprovalReason(toolDefinition, evaluation) {
875
+ if (evaluation !== null && evaluation.action === "requireApproval") {
876
+ return evaluation.reason;
877
+ }
878
+ if (toolDefinition.requiresApproval) {
879
+ return staticApprovalReason;
880
+ }
881
+ return null;
882
+ }
883
+ function evaluatePoliciesForToolCall(context, policies, input) {
884
+ if (policies === void 0) {
885
+ return null;
886
+ }
887
+ const timestamp = context.now().toISOString();
888
+ const evaluation = evaluateToolPolicies({
889
+ policies,
890
+ context: {
891
+ toolName: context.toolName,
892
+ agentName: context.agentName,
893
+ input
894
+ },
895
+ timestamp
896
+ });
897
+ const policyEvents = buildPolicyEvaluatedEvents({
898
+ runId: context.runId,
899
+ workflowName: context.workflowName,
900
+ timestamp,
901
+ parentEventId: context.parentEventId,
902
+ redacted: false,
903
+ toolName: context.toolName,
904
+ agentName: context.agentName,
905
+ decisionEvents: evaluation.decisions.map((decision) => ({
906
+ id: generateTraceEventId3(),
907
+ decision
908
+ }))
909
+ });
910
+ for (const policyEvent of policyEvents) {
911
+ context.traceBus.emitTraceEvent(policyEvent);
912
+ }
913
+ return evaluation;
914
+ }
915
+ function failToolCallForInvalidInput(context, rawInput, issues) {
916
+ const requestEventId = emitToolRequestedEvent(
917
+ context,
918
+ rawInput,
919
+ context.redactionMode
920
+ );
921
+ const error = {
922
+ code: "VALIDATION_FAILED",
923
+ message: buildToolValidationFailureMessage(
924
+ "input",
925
+ context.toolName,
926
+ issues
927
+ )
928
+ };
929
+ emitToolFailedEvent(context, requestEventId, error, 0);
930
+ return { status: "failed", error };
931
+ }
932
+ function blockToolCallForDisallowedAgent(context, input) {
933
+ const reason = `agent ${context.agentName} is not allowed to use tool ${context.toolName}`;
934
+ const requestEventId = emitToolRequestedEvent(
935
+ context,
936
+ input,
937
+ context.redactionMode
938
+ );
939
+ emitToolBlockedEvent(context, requestEventId, agentAllowlistPolicyName, reason);
940
+ return { status: "blocked", reason };
941
+ }
942
+ function blockToolCallByPolicy(context, input, evaluation) {
943
+ const requestEventId = emitToolRequestedEvent(
944
+ context,
945
+ input,
946
+ context.redactionMode
947
+ );
948
+ emitToolBlockedEvent(
949
+ context,
950
+ requestEventId,
951
+ evaluation.policyName,
952
+ evaluation.reason
953
+ );
954
+ return { status: "blocked", reason: evaluation.reason };
955
+ }
956
+ function requireApprovalForToolCall(context, toolRequestEventId, reason) {
957
+ const approvalId = generateApprovalId();
958
+ emitApprovalRequiredEvent(context, toolRequestEventId, approvalId, reason);
959
+ return { status: "awaiting_approval", approvalId, toolRequestEventId };
960
+ }
961
+ function ensureGrantedApprovalIsKnown(context, grantedApproval) {
962
+ const runApproval = context.traceBus.getRunState().approvals.find((approval) => approval.id === grantedApproval.approvalId);
963
+ if (runApproval === void 0 || runApproval.status === "rejected") {
964
+ throw new AppError4({
965
+ code: AppErrorCode4.Forbidden,
966
+ message: `granted approval "${grantedApproval.approvalId}" does not match a grantable approval for run "${context.runId}"; the tool call cannot use this grant`,
967
+ statusCode: 403,
968
+ details: {
969
+ approvalId: grantedApproval.approvalId,
970
+ toolName: context.toolName,
971
+ agentName: context.agentName,
972
+ runId: context.runId
973
+ }
974
+ });
975
+ }
976
+ }
977
+ async function executeGrantedApprovalToolCall(context, callInput, input, grantedApproval) {
978
+ ensureGrantedApprovalIsKnown(context, grantedApproval);
979
+ if (!isAgentAllowedToUseTool(callInput.tool, callInput.agentName)) {
980
+ return blockToolCallForDisallowedAgent(context, input);
981
+ }
982
+ const evaluation = evaluatePoliciesForToolCall(
983
+ context,
984
+ callInput.policies,
985
+ input
986
+ );
987
+ if (evaluation !== null && evaluation.action === "block") {
988
+ return blockToolCallByPolicy(context, input, evaluation);
989
+ }
990
+ const approvedEventId = emitToolApprovedEvent(
991
+ context,
992
+ grantedApproval.toolRequestEventId,
993
+ grantedApproval.approvalId
994
+ );
995
+ const requestEventId = grantedApproval.toolRequestEventId ?? approvedEventId;
996
+ return await executeToolAndEmitOutcome(
997
+ context,
998
+ callInput.tool,
999
+ input,
1000
+ requestEventId,
1001
+ callInput.mockExecution
1002
+ );
1003
+ }
1004
+
1005
+ // src/teams/team-definition.ts
1006
+ import { AppErrorCode as AppErrorCode5 } from "@assemble-dev/shared-types/errors";
1007
+ import { AppError as AppError5 } from "@assemble-dev/shared-utils/errors";
1008
+ function team(config) {
1009
+ ensureTeamNameIsPresent(config.name);
1010
+ ensureTeamHasAgents(config.name, config.agents);
1011
+ ensureTeamAgentNamesAreUnique(config.name, config.agents);
1012
+ return Object.freeze({
1013
+ name: config.name,
1014
+ description: config.description ?? "",
1015
+ agents: Object.freeze([...config.agents]),
1016
+ agentNames: Object.freeze(config.agents.map((member) => member.name))
1017
+ });
1018
+ }
1019
+ function ensureTeamNameIsPresent(name) {
1020
+ if (name.trim() !== "") {
1021
+ return;
1022
+ }
1023
+ throw new AppError5({
1024
+ code: AppErrorCode5.BadRequest,
1025
+ message: "team name must not be empty",
1026
+ statusCode: 400
1027
+ });
1028
+ }
1029
+ function ensureTeamHasAgents(teamName, agents) {
1030
+ if (agents.length > 0) {
1031
+ return;
1032
+ }
1033
+ throw new AppError5({
1034
+ code: AppErrorCode5.BadRequest,
1035
+ message: `team "${teamName}" must include at least one agent`,
1036
+ statusCode: 400
1037
+ });
1038
+ }
1039
+ function ensureTeamAgentNamesAreUnique(teamName, agents) {
1040
+ const seenAgentNames = /* @__PURE__ */ new Set();
1041
+ for (const member of agents) {
1042
+ if (seenAgentNames.has(member.name)) {
1043
+ throw new AppError5({
1044
+ code: AppErrorCode5.BadRequest,
1045
+ message: `team "${teamName}" contains duplicate agent name "${member.name}"`,
1046
+ statusCode: 400
1047
+ });
1048
+ }
1049
+ seenAgentNames.add(member.name);
1050
+ }
1051
+ }
1052
+
1053
+ // src/teams/team-presets.ts
1054
+ function buildPipelineTeamWorkflowConfig(input) {
1055
+ return {
1056
+ name: input.name,
1057
+ inputSchema: input.inputSchema,
1058
+ outputSchema: input.outputSchema,
1059
+ agents: [...input.team.agents],
1060
+ topology: { kind: "pipeline" }
1061
+ };
1062
+ }
1063
+ function buildSupervisorTeamWorkflowConfig(input) {
1064
+ return {
1065
+ name: input.name,
1066
+ inputSchema: input.inputSchema,
1067
+ outputSchema: input.outputSchema,
1068
+ agents: [...input.team.agents],
1069
+ topology: {
1070
+ kind: "supervisor",
1071
+ supervisorName: input.supervisorName,
1072
+ route: input.route,
1073
+ maxSteps: input.maxSteps
1074
+ }
1075
+ };
1076
+ }
1077
+ function buildReviewPanelWorkflowConfig(input) {
1078
+ return {
1079
+ name: input.name,
1080
+ inputSchema: input.inputSchema,
1081
+ agents: [...input.team.agents],
1082
+ topology: { kind: "parallel" }
1083
+ };
1084
+ }
1085
+
1086
+ // src/policies/policy-definition.ts
1087
+ import { AppErrorCode as AppErrorCode6 } from "@assemble-dev/shared-types/errors";
1088
+ import { AppError as AppError6 } from "@assemble-dev/shared-utils/errors";
1089
+ function policy(config) {
1090
+ ensurePolicyNameIsPresent(config.name);
1091
+ ensurePolicyDefinesAtLeastOneRule(config);
1092
+ return Object.freeze({ ...config });
1093
+ }
1094
+ function ensurePolicyNameIsPresent(name) {
1095
+ if (name.trim() !== "") {
1096
+ return;
1097
+ }
1098
+ throw new AppError6({
1099
+ code: AppErrorCode6.BadRequest,
1100
+ message: "policy name must not be empty",
1101
+ statusCode: 400
1102
+ });
1103
+ }
1104
+ function ensurePolicyDefinesAtLeastOneRule(config) {
1105
+ const hasRule = toolPolicyEffectNames.some(
1106
+ (effectName) => config[effectName] !== void 0
1107
+ );
1108
+ if (hasRule) {
1109
+ return;
1110
+ }
1111
+ throw new AppError6({
1112
+ code: AppErrorCode6.BadRequest,
1113
+ message: `policy "${config.name}" must define at least one rule (${toolPolicyEffectNames.join(", ")})`,
1114
+ statusCode: 400
1115
+ });
1116
+ }
1117
+
1118
+ // src/workflows/workflow-runtime.ts
1119
+ import { AppErrorCode as AppErrorCode12 } from "@assemble-dev/shared-types/errors";
1120
+ import { generateRunId } from "@assemble-dev/shared-utils/ids";
1121
+ import { AppError as AppError12 } from "@assemble-dev/shared-utils/errors";
1122
+
1123
+ // src/memory/artifact-store.ts
1124
+ import { JsonObjectSchema } from "@assemble-dev/shared-types/json";
1125
+ import { RunArtifactSchema } from "@assemble-dev/shared-types/runs";
1126
+ function buildRunArtifactsMemoryKey(runId) {
1127
+ return `run:${runId}:artifacts`;
1128
+ }
1129
+ function mapRunArtifactToJsonValue(artifact) {
1130
+ const artifactValue = {
1131
+ key: artifact.key,
1132
+ value: artifact.value,
1133
+ createdAt: artifact.createdAt
1134
+ };
1135
+ if (artifact.producedByAgentName !== void 0) {
1136
+ artifactValue.producedByAgentName = artifact.producedByAgentName;
1137
+ }
1138
+ return artifactValue;
1139
+ }
1140
+ async function loadRunArtifactsRecord(provider, memoryKey) {
1141
+ const storedArtifacts = await provider.getMemoryValue(memoryKey);
1142
+ if (storedArtifacts === null) {
1143
+ return {};
1144
+ }
1145
+ return JsonObjectSchema.parse(storedArtifacts);
1146
+ }
1147
+ function createArtifactStore(options) {
1148
+ const memoryKey = buildRunArtifactsMemoryKey(options.runId);
1149
+ return {
1150
+ async saveRunArtifact(artifact) {
1151
+ const artifactsRecord = await loadRunArtifactsRecord(
1152
+ options.provider,
1153
+ memoryKey
1154
+ );
1155
+ artifactsRecord[artifact.key] = mapRunArtifactToJsonValue(artifact);
1156
+ await options.provider.setMemoryValue(memoryKey, artifactsRecord);
1157
+ },
1158
+ async getRunArtifact(key) {
1159
+ const artifactsRecord = await loadRunArtifactsRecord(
1160
+ options.provider,
1161
+ memoryKey
1162
+ );
1163
+ const storedArtifact = artifactsRecord[key];
1164
+ if (storedArtifact === void 0) {
1165
+ return null;
1166
+ }
1167
+ return RunArtifactSchema.parse(storedArtifact);
1168
+ },
1169
+ async listRunArtifacts() {
1170
+ const artifactsRecord = await loadRunArtifactsRecord(
1171
+ options.provider,
1172
+ memoryKey
1173
+ );
1174
+ return Object.values(artifactsRecord).map(
1175
+ (storedArtifact) => RunArtifactSchema.parse(storedArtifact)
1176
+ );
1177
+ }
1178
+ };
1179
+ }
1180
+
1181
+ // src/memory/memory-provider.ts
1182
+ function buildAppendedMemoryValue(existingValue, appendedValue) {
1183
+ if (existingValue === void 0) {
1184
+ return [appendedValue];
1185
+ }
1186
+ if (Array.isArray(existingValue)) {
1187
+ return [...existingValue, appendedValue];
1188
+ }
1189
+ return [existingValue, appendedValue];
1190
+ }
1191
+
1192
+ // src/memory/in-memory-provider.ts
1193
+ function cloneJsonValue(value) {
1194
+ return structuredClone(value);
1195
+ }
1196
+ function createInMemoryMemoryProvider() {
1197
+ const memoryEntries = /* @__PURE__ */ new Map();
1198
+ return {
1199
+ async getMemoryValue(key) {
1200
+ const storedValue = memoryEntries.get(key);
1201
+ if (storedValue === void 0) {
1202
+ return await Promise.resolve(null);
1203
+ }
1204
+ return await Promise.resolve(cloneJsonValue(storedValue));
1205
+ },
1206
+ async setMemoryValue(key, value) {
1207
+ memoryEntries.set(key, cloneJsonValue(value));
1208
+ await Promise.resolve();
1209
+ },
1210
+ async appendMemoryValue(key, value) {
1211
+ memoryEntries.set(
1212
+ key,
1213
+ buildAppendedMemoryValue(memoryEntries.get(key), cloneJsonValue(value))
1214
+ );
1215
+ await Promise.resolve();
1216
+ },
1217
+ async listMemoryKeys() {
1218
+ return await Promise.resolve([...memoryEntries.keys()]);
1219
+ }
1220
+ };
1221
+ }
1222
+
1223
+ // src/memory/memory-config.ts
1224
+ function resolveWorkflowMemoryConfig(config) {
1225
+ if (config !== void 0) {
1226
+ return { provider: config.provider };
1227
+ }
1228
+ return { provider: createInMemoryMemoryProvider() };
1229
+ }
1230
+
1231
+ // src/memory/thread-memory.ts
1232
+ import { RunMessageSchema } from "@assemble-dev/shared-types/runs";
1233
+ function buildThreadMessagesMemoryKey(threadKey) {
1234
+ return `thread:${threadKey}:messages`;
1235
+ }
1236
+ function mapRunMessageToJsonValue(message) {
1237
+ const messageValue = {
1238
+ role: message.role,
1239
+ content: message.content,
1240
+ timestamp: message.timestamp
1241
+ };
1242
+ if (message.agentName !== void 0) {
1243
+ messageValue.agentName = message.agentName;
1244
+ }
1245
+ if (message.toolName !== void 0) {
1246
+ messageValue.toolName = message.toolName;
1247
+ }
1248
+ return messageValue;
1249
+ }
1250
+ function createThreadMemory(options) {
1251
+ const memoryKey = buildThreadMessagesMemoryKey(options.threadKey);
1252
+ return {
1253
+ async appendThreadMessage(message) {
1254
+ await options.provider.appendMemoryValue(
1255
+ memoryKey,
1256
+ mapRunMessageToJsonValue(message)
1257
+ );
1258
+ },
1259
+ async listThreadMessages() {
1260
+ const storedMessages = await options.provider.getMemoryValue(memoryKey);
1261
+ if (storedMessages === null) {
1262
+ return [];
1263
+ }
1264
+ return RunMessageSchema.array().parse(storedMessages);
1265
+ }
1266
+ };
1267
+ }
1268
+
1269
+ // src/tracing/trace-bus.ts
1270
+ import {
1271
+ TraceEventSchema
1272
+ } from "@assemble-dev/shared-types/trace-events";
1273
+
1274
+ // src/tracing/run-state-error-mapper.ts
1275
+ function mapEventError(error, timestamp, agentName, toolName) {
1276
+ return {
1277
+ message: error.message,
1278
+ code: error.code,
1279
+ agentName,
1280
+ toolName,
1281
+ timestamp
1282
+ };
1283
+ }
1284
+
1285
+ // src/tracing/run-state-agent-tool-reducers.ts
1286
+ function applyAgentStarted(state, event) {
1287
+ const agentCall = {
1288
+ id: event.id,
1289
+ agentName: event.agentName,
1290
+ status: "running",
1291
+ input: event.input,
1292
+ output: null,
1293
+ error: null,
1294
+ startedAt: event.timestamp,
1295
+ completedAt: null,
1296
+ latencyMs: null
1297
+ };
1298
+ return { ...state, agentCalls: [...state.agentCalls, agentCall] };
1299
+ }
1300
+ function applyAgentCompleted(state, event) {
1301
+ return updateAgentCall(state, event.parentEventId, event.agentName, {
1302
+ status: "completed",
1303
+ output: event.output,
1304
+ completedAt: event.timestamp,
1305
+ latencyMs: event.latencyMs
1306
+ });
1307
+ }
1308
+ function applyAgentFailed(state, event) {
1309
+ const failedState = updateAgentCall(
1310
+ state,
1311
+ event.parentEventId,
1312
+ event.agentName,
1313
+ {
1314
+ status: "failed",
1315
+ error: mapEventError(event.error, event.timestamp, event.agentName),
1316
+ completedAt: event.timestamp,
1317
+ latencyMs: event.latencyMs
1318
+ }
1319
+ );
1320
+ return {
1321
+ ...failedState,
1322
+ errors: [
1323
+ ...failedState.errors,
1324
+ mapEventError(event.error, event.timestamp, event.agentName)
1325
+ ]
1326
+ };
1327
+ }
1328
+ function applyToolRequested(state, event) {
1329
+ const toolCall = {
1330
+ id: event.id,
1331
+ toolName: event.toolName,
1332
+ agentName: event.agentName,
1333
+ status: "requested",
1334
+ input: event.input,
1335
+ output: null,
1336
+ error: null,
1337
+ requestedAt: event.timestamp,
1338
+ completedAt: null,
1339
+ latencyMs: null
1340
+ };
1341
+ return { ...state, toolCalls: [...state.toolCalls, toolCall] };
1342
+ }
1343
+ function applyToolBlocked(state, event) {
1344
+ return applyToolStatus(state, event.parentEventId, event.toolName, {
1345
+ status: "blocked",
1346
+ completedAt: event.timestamp
1347
+ });
1348
+ }
1349
+ function applyToolCompleted(state, event) {
1350
+ return applyToolStatus(state, event.parentEventId, event.toolName, {
1351
+ status: "completed",
1352
+ output: event.output,
1353
+ completedAt: event.timestamp,
1354
+ latencyMs: event.latencyMs
1355
+ });
1356
+ }
1357
+ function applyToolFailed(state, event) {
1358
+ return applyToolStatus(state, event.parentEventId, event.toolName, {
1359
+ status: "failed",
1360
+ error: mapEventError(event.error, event.timestamp, void 0, event.toolName),
1361
+ completedAt: event.timestamp,
1362
+ latencyMs: event.latencyMs
1363
+ });
1364
+ }
1365
+ function updateAgentCall(state, parentEventId, agentName, update) {
1366
+ const targetIndex = findCallIndexByParentThenPredicate(
1367
+ state.agentCalls,
1368
+ parentEventId,
1369
+ (call) => call.agentName === agentName && call.status === "running"
1370
+ );
1371
+ if (targetIndex === -1) {
1372
+ return state;
1373
+ }
1374
+ const agentCalls = state.agentCalls.map(
1375
+ (call, index) => index === targetIndex ? { ...call, ...update } : call
1376
+ );
1377
+ return { ...state, agentCalls };
1378
+ }
1379
+ function applyToolStatus(state, parentEventId, toolName, update) {
1380
+ const targetIndex = findCallIndexByParentThenPredicate(
1381
+ state.toolCalls,
1382
+ parentEventId,
1383
+ (call) => call.toolName === toolName && (call.status === "requested" || call.status === "approved")
1384
+ );
1385
+ if (targetIndex === -1) {
1386
+ return state;
1387
+ }
1388
+ const toolCalls = state.toolCalls.map(
1389
+ (call, index) => index === targetIndex ? { ...call, ...update } : call
1390
+ );
1391
+ return { ...state, toolCalls };
1392
+ }
1393
+ function findCallIndexByParentThenPredicate(calls, parentEventId, fallbackPredicate) {
1394
+ if (parentEventId !== null) {
1395
+ const parentIndex = findLastIndex(
1396
+ calls,
1397
+ (call) => call.id === parentEventId
1398
+ );
1399
+ if (parentIndex !== -1) {
1400
+ return parentIndex;
1401
+ }
1402
+ }
1403
+ return findLastIndex(calls, fallbackPredicate);
1404
+ }
1405
+ function findLastIndex(items, predicate) {
1406
+ for (let index = items.length - 1; index >= 0; index -= 1) {
1407
+ const item = items[index];
1408
+ if (item !== void 0 && predicate(item)) {
1409
+ return index;
1410
+ }
1411
+ }
1412
+ return -1;
1413
+ }
1414
+
1415
+ // src/tracing/run-state-approval-routing-reducers.ts
1416
+ function applyPolicyEvaluated(state, event) {
1417
+ return {
1418
+ ...state,
1419
+ policyDecisions: [
1420
+ ...state.policyDecisions,
1421
+ {
1422
+ policyName: event.policyName,
1423
+ action: event.action,
1424
+ agentName: event.agentName,
1425
+ toolName: event.toolName,
1426
+ reason: event.reason,
1427
+ timestamp: event.timestamp
1428
+ }
1429
+ ]
1430
+ };
1431
+ }
1432
+ function applyRouteSelected(state, event) {
1433
+ return {
1434
+ ...state,
1435
+ routingDecisions: [
1436
+ ...state.routingDecisions,
1437
+ {
1438
+ supervisorName: event.supervisorName,
1439
+ selectedAgentName: event.selectedAgentName,
1440
+ candidateAgentNames: event.candidateAgentNames,
1441
+ reason: event.reason,
1442
+ timestamp: event.timestamp
1443
+ }
1444
+ ]
1445
+ };
1446
+ }
1447
+ function applyApprovalRequired(state, event) {
1448
+ return {
1449
+ ...state,
1450
+ status: "awaiting_approval",
1451
+ approvals: [
1452
+ ...state.approvals,
1453
+ {
1454
+ id: event.approvalId,
1455
+ status: "pending",
1456
+ toolCallId: event.parentEventId,
1457
+ toolName: event.toolName,
1458
+ agentName: event.agentName,
1459
+ reason: event.reason,
1460
+ requestedAt: event.timestamp,
1461
+ decidedAt: null,
1462
+ decidedBy: null
1463
+ }
1464
+ ]
1465
+ };
1466
+ }
1467
+ function applyApprovalDecision(state, event, status, reason) {
1468
+ const approvals = state.approvals.map(
1469
+ (approval) => approval.id === event.approvalId ? {
1470
+ ...approval,
1471
+ status,
1472
+ reason: reason ?? approval.reason,
1473
+ decidedAt: event.timestamp,
1474
+ decidedBy: event.decidedBy
1475
+ } : approval
1476
+ );
1477
+ const hasPendingApprovals = approvals.some(
1478
+ (approval) => approval.status === "pending"
1479
+ );
1480
+ return {
1481
+ ...state,
1482
+ approvals,
1483
+ status: hasPendingApprovals ? "awaiting_approval" : "running"
1484
+ };
1485
+ }
1486
+
1487
+ // src/tracing/run-state-reducer.ts
1488
+ function createInitialRunState(input) {
1489
+ return {
1490
+ runId: input.runId,
1491
+ workflowName: input.workflowName,
1492
+ status: "pending",
1493
+ input: input.input,
1494
+ messages: [],
1495
+ agentCalls: [],
1496
+ toolCalls: [],
1497
+ policyDecisions: [],
1498
+ routingDecisions: [],
1499
+ approvals: [],
1500
+ artifacts: [],
1501
+ errors: [],
1502
+ metrics: {
1503
+ totalLatencyMs: null,
1504
+ tokenUsage: null,
1505
+ agentCallCount: 0,
1506
+ toolCallCount: 0,
1507
+ policyDecisionCount: 0,
1508
+ approvalCount: 0
1509
+ },
1510
+ finalOutput: null,
1511
+ createdAt: input.createdAt,
1512
+ startedAt: null,
1513
+ completedAt: null
1514
+ };
1515
+ }
1516
+ function applyTraceEventToRunState(state, event) {
1517
+ const nextState = reduceTraceEvent(state, event);
1518
+ return {
1519
+ ...nextState,
1520
+ metrics: {
1521
+ ...nextState.metrics,
1522
+ agentCallCount: nextState.agentCalls.length,
1523
+ toolCallCount: nextState.toolCalls.length,
1524
+ policyDecisionCount: nextState.policyDecisions.length,
1525
+ approvalCount: nextState.approvals.length
1526
+ }
1527
+ };
1528
+ }
1529
+ function reduceTraceEvent(state, event) {
1530
+ switch (event.type) {
1531
+ case "run.started":
1532
+ return applyRunStarted(state, event);
1533
+ case "run.completed":
1534
+ return applyRunCompleted(state, event);
1535
+ case "run.failed":
1536
+ return applyRunFailed(state, event);
1537
+ case "agent.started":
1538
+ return applyAgentStarted(state, event);
1539
+ case "agent.completed":
1540
+ return applyAgentCompleted(state, event);
1541
+ case "agent.failed":
1542
+ return applyAgentFailed(state, event);
1543
+ case "tool.requested":
1544
+ return applyToolRequested(state, event);
1545
+ case "tool.approved":
1546
+ return applyToolStatus(state, event.parentEventId, event.toolName, {
1547
+ status: "approved"
1548
+ });
1549
+ case "tool.blocked":
1550
+ return applyToolBlocked(state, event);
1551
+ case "tool.completed":
1552
+ return applyToolCompleted(state, event);
1553
+ case "tool.failed":
1554
+ return applyToolFailed(state, event);
1555
+ case "policy.evaluated":
1556
+ return applyPolicyEvaluated(state, event);
1557
+ case "route.selected":
1558
+ return applyRouteSelected(state, event);
1559
+ case "approval.required":
1560
+ return applyApprovalRequired(state, event);
1561
+ case "approval.approved":
1562
+ return applyApprovalDecision(state, event, "approved", null);
1563
+ case "approval.rejected":
1564
+ return applyApprovalDecision(state, event, "rejected", event.reason);
1565
+ case "eval.started":
1566
+ case "eval.completed":
1567
+ case "replay.started":
1568
+ case "replay.completed":
1569
+ return state;
1570
+ }
1571
+ }
1572
+ function applyRunStarted(state, event) {
1573
+ return {
1574
+ ...state,
1575
+ status: "running",
1576
+ input: event.input,
1577
+ startedAt: event.timestamp
1578
+ };
1579
+ }
1580
+ function applyRunCompleted(state, event) {
1581
+ return {
1582
+ ...state,
1583
+ status: "completed",
1584
+ finalOutput: event.output,
1585
+ completedAt: event.timestamp,
1586
+ metrics: { ...state.metrics, totalLatencyMs: event.latencyMs }
1587
+ };
1588
+ }
1589
+ function applyRunFailed(state, event) {
1590
+ return {
1591
+ ...state,
1592
+ status: "failed",
1593
+ completedAt: event.timestamp,
1594
+ errors: [...state.errors, mapEventError(event.error, event.timestamp)],
1595
+ metrics: { ...state.metrics, totalLatencyMs: event.latencyMs }
1596
+ };
1597
+ }
1598
+
1599
+ // src/tracing/trace-bus.ts
1600
+ function buildSinkFailure(sinkName, operation, thrownError) {
1601
+ return { sinkName, operation, error: thrownError };
1602
+ }
1603
+ function reportSinkFailure(onSinkError, failure) {
1604
+ if (onSinkError !== void 0) {
1605
+ onSinkError(failure);
1606
+ }
1607
+ }
1608
+ function writeTraceEventToSinkSafely(sink, event, onSinkError) {
1609
+ try {
1610
+ Promise.resolve(sink.writeTraceEvent(event)).catch(
1611
+ (thrownError) => {
1612
+ reportSinkFailure(
1613
+ onSinkError,
1614
+ buildSinkFailure(sink.name, "write", thrownError)
1615
+ );
1616
+ }
1617
+ );
1618
+ } catch (thrownError) {
1619
+ reportSinkFailure(
1620
+ onSinkError,
1621
+ buildSinkFailure(
1622
+ sink.name,
1623
+ "write",
1624
+ thrownError instanceof Error ? thrownError : new Error(String(thrownError))
1625
+ )
1626
+ );
1627
+ }
1628
+ }
1629
+ async function flushSinkSafely(sink, onSinkError) {
1630
+ if (sink.flush === void 0) {
1631
+ return;
1632
+ }
1633
+ try {
1634
+ await sink.flush();
1635
+ } catch (thrownError) {
1636
+ reportSinkFailure(
1637
+ onSinkError,
1638
+ buildSinkFailure(
1639
+ sink.name,
1640
+ "flush",
1641
+ thrownError instanceof Error ? thrownError : new Error(String(thrownError))
1642
+ )
1643
+ );
1644
+ }
1645
+ }
1646
+ function createTraceBus(busInput) {
1647
+ const now = busInput.now ?? (() => /* @__PURE__ */ new Date());
1648
+ const events = [];
1649
+ let runState = busInput.initialRunState ?? createInitialRunState({
1650
+ runId: busInput.runId,
1651
+ workflowName: busInput.workflowName,
1652
+ input: busInput.input,
1653
+ createdAt: now().toISOString()
1654
+ });
1655
+ function emitTraceEvent(event) {
1656
+ const validatedEvent = TraceEventSchema.parse(event);
1657
+ events.push(validatedEvent);
1658
+ runState = applyTraceEventToRunState(runState, validatedEvent);
1659
+ for (const sink of busInput.sinks) {
1660
+ writeTraceEventToSinkSafely(sink, validatedEvent, busInput.onSinkError);
1661
+ }
1662
+ return validatedEvent;
1663
+ }
1664
+ async function flushSinks() {
1665
+ for (const sink of busInput.sinks) {
1666
+ await flushSinkSafely(sink, busInput.onSinkError);
1667
+ }
1668
+ }
1669
+ function closeSinks() {
1670
+ for (const sink of busInput.sinks) {
1671
+ if (sink.close !== void 0) {
1672
+ sink.close();
1673
+ }
1674
+ }
1675
+ }
1676
+ return {
1677
+ emitTraceEvent,
1678
+ getRunState: () => runState,
1679
+ listTraceEvents: () => [...events],
1680
+ flushSinks,
1681
+ closeSinks
1682
+ };
1683
+ }
1684
+
1685
+ // src/workflows/workflow-approval.ts
1686
+ import { AppErrorCode as AppErrorCode9 } from "@assemble-dev/shared-types/errors";
1687
+ import { JsonValueSchema as JsonValueSchema2 } from "@assemble-dev/shared-types/json";
1688
+ import { AppError as AppError9 } from "@assemble-dev/shared-utils/errors";
1689
+ import { z as z3 } from "zod";
1690
+
1691
+ // src/workflows/run-state-store.ts
1692
+ import { mkdirSync, readFileSync, writeFileSync } from "fs";
1693
+ import { join } from "path";
1694
+ import { RunStateSchema } from "@assemble-dev/shared-types/runs";
1695
+ var defaultRunStateDirectory = join(".assemble", "runs");
1696
+ function resolveRunStateFilePath(runId, directory = defaultRunStateDirectory) {
1697
+ return join(directory, `${runId}.json`);
1698
+ }
1699
+ function saveRunStateFile(input) {
1700
+ const directory = input.directory ?? defaultRunStateDirectory;
1701
+ const filePath = resolveRunStateFilePath(input.runState.runId, directory);
1702
+ const validatedRunState = RunStateSchema.parse(input.runState);
1703
+ mkdirSync(directory, { recursive: true });
1704
+ writeFileSync(filePath, JSON.stringify(validatedRunState, null, 2), "utf8");
1705
+ return filePath;
1706
+ }
1707
+ function loadRunStateFile(input) {
1708
+ const filePath = resolveRunStateFilePath(input.runId, input.directory);
1709
+ const rawContents = readFileSync(filePath, "utf8");
1710
+ return RunStateSchema.parse(JSON.parse(rawContents));
1711
+ }
1712
+
1713
+ // src/workflows/workflow-runtime-context.ts
1714
+ async function persistWorkflowRunState(context) {
1715
+ if (!context.persistRunState) {
1716
+ return;
1717
+ }
1718
+ saveRunStateFile({
1719
+ runState: await buildWorkflowRunState(context),
1720
+ directory: context.runStateDirectory
1721
+ });
1722
+ }
1723
+ async function buildWorkflowRunState(context) {
1724
+ return {
1725
+ ...context.traceBus.getRunState(),
1726
+ artifacts: await context.artifactStore.listRunArtifacts()
1727
+ };
1728
+ }
1729
+ function buildWorkflowEventContext(context) {
1730
+ return {
1731
+ traceBus: context.traceBus,
1732
+ runId: context.runId,
1733
+ workflowName: context.definition.name,
1734
+ now: context.now
1735
+ };
1736
+ }
1737
+ function calculateWorkflowElapsedMs(context) {
1738
+ return Math.max(0, context.now().getTime() - context.startedAtMs);
1739
+ }
1740
+
1741
+ // src/workflows/workflow-step-execution.ts
1742
+ import { AppErrorCode as AppErrorCode7 } from "@assemble-dev/shared-types/errors";
1743
+ import { AppError as AppError7 } from "@assemble-dev/shared-utils/errors";
1744
+
1745
+ // src/workflows/workflow-trace-events.ts
1746
+ import { generateTraceEventId as generateTraceEventId4 } from "@assemble-dev/shared-utils/ids";
1747
+ function buildWorkflowEventBase(eventContext, parentEventId) {
1748
+ return {
1749
+ id: generateTraceEventId4(),
1750
+ runId: eventContext.runId,
1751
+ workflowName: eventContext.workflowName,
1752
+ timestamp: eventContext.now().toISOString(),
1753
+ parentEventId,
1754
+ redacted: false
1755
+ };
1756
+ }
1757
+ function emitRunStartedEvent(eventContext, input) {
1758
+ return eventContext.traceBus.emitTraceEvent({
1759
+ ...buildWorkflowEventBase(eventContext, null),
1760
+ type: "run.started",
1761
+ input
1762
+ });
1763
+ }
1764
+ function emitRunCompletedEvent(eventContext, runStartedEventId, output, latencyMs) {
1765
+ eventContext.traceBus.emitTraceEvent({
1766
+ ...buildWorkflowEventBase(eventContext, runStartedEventId),
1767
+ type: "run.completed",
1768
+ output,
1769
+ latencyMs
1770
+ });
1771
+ }
1772
+ function emitRouteSelectedEvent(eventContext, runStartedEventId, routeSelection) {
1773
+ eventContext.traceBus.emitTraceEvent({
1774
+ ...buildWorkflowEventBase(eventContext, runStartedEventId),
1775
+ type: "route.selected",
1776
+ supervisorName: routeSelection.supervisorName,
1777
+ selectedAgentName: routeSelection.selectedAgentName,
1778
+ candidateAgentNames: routeSelection.candidateAgentNames,
1779
+ reason: routeSelection.reason
1780
+ });
1781
+ }
1782
+ function emitApprovalApprovedEvent(eventContext, decision) {
1783
+ eventContext.traceBus.emitTraceEvent({
1784
+ ...buildWorkflowEventBase(eventContext, decision.parentEventId),
1785
+ type: "approval.approved",
1786
+ approvalId: decision.approvalId,
1787
+ decidedBy: decision.decidedBy
1788
+ });
1789
+ }
1790
+ function emitApprovalRejectedEvent(eventContext, decision, reason) {
1791
+ eventContext.traceBus.emitTraceEvent({
1792
+ ...buildWorkflowEventBase(eventContext, decision.parentEventId),
1793
+ type: "approval.rejected",
1794
+ approvalId: decision.approvalId,
1795
+ decidedBy: decision.decidedBy,
1796
+ reason
1797
+ });
1798
+ }
1799
+ function emitRunFailedEvent(eventContext, runStartedEventId, error, latencyMs) {
1800
+ eventContext.traceBus.emitTraceEvent({
1801
+ ...buildWorkflowEventBase(eventContext, runStartedEventId),
1802
+ type: "run.failed",
1803
+ error,
1804
+ latencyMs
1805
+ });
1806
+ }
1807
+
1808
+ // src/workflows/workflow-step-execution.ts
1809
+ async function executeWorkflowStep(context, stepIndex, stepAgent, stepInput) {
1810
+ try {
1811
+ const agentResult = await runAgent({
1812
+ definition: stepAgent,
1813
+ input: stepInput,
1814
+ traceBus: context.traceBus,
1815
+ parentEventId: context.runStartedEventId,
1816
+ threadMemory: context.threadMemory,
1817
+ now: context.now
1818
+ });
1819
+ await saveWorkflowStepArtifact(
1820
+ context,
1821
+ stepIndex,
1822
+ stepAgent.name,
1823
+ agentResult.output
1824
+ );
1825
+ return agentResult.output;
1826
+ } catch (thrownError) {
1827
+ const stepError = mapCaughtWorkflowErrorToAppError(
1828
+ thrownError instanceof Error ? thrownError : new Error(String(thrownError))
1829
+ );
1830
+ return await resolveWorkflowStepFailure(
1831
+ context,
1832
+ stepIndex,
1833
+ stepAgent.name,
1834
+ stepError,
1835
+ stepInput
1836
+ );
1837
+ }
1838
+ }
1839
+ async function resolveWorkflowStepFailure(context, stepIndex, agentName, stepError, previousStepOutput) {
1840
+ if (context.definition.continueOnAgentFailure) {
1841
+ return previousStepOutput;
1842
+ }
1843
+ const failureError = new AppError7({
1844
+ code: stepError.code,
1845
+ message: `step ${stepIndex} (${agentName}) failed: ${stepError.message}`,
1846
+ statusCode: stepError.statusCode,
1847
+ details: { stepIndex, agentName, reason: stepError.message },
1848
+ cause: stepError
1849
+ });
1850
+ await failWorkflowRun(context, failureError);
1851
+ throw failureError;
1852
+ }
1853
+ function shouldStopWorkflowRun(context, stepIndex) {
1854
+ if (context.definition.stopCondition === null) {
1855
+ return false;
1856
+ }
1857
+ return context.definition.stopCondition({
1858
+ state: context.traceBus.getRunState(),
1859
+ stepIndex
1860
+ });
1861
+ }
1862
+ async function saveWorkflowStepArtifact(context, stepIndex, agentName, stepOutput) {
1863
+ await context.artifactStore.saveRunArtifact({
1864
+ key: `step-${stepIndex}:${agentName}`,
1865
+ value: stepOutput,
1866
+ producedByAgentName: agentName,
1867
+ createdAt: context.now().toISOString()
1868
+ });
1869
+ }
1870
+ async function failWorkflowRun(context, failureError) {
1871
+ emitRunFailedEvent(
1872
+ buildWorkflowEventContext(context),
1873
+ context.runStartedEventId,
1874
+ { message: failureError.message, code: failureError.code },
1875
+ calculateWorkflowElapsedMs(context)
1876
+ );
1877
+ await persistWorkflowRunState(context);
1878
+ await context.traceBus.flushSinks();
1879
+ }
1880
+ function mapCaughtWorkflowErrorToAppError(thrownError) {
1881
+ if (thrownError instanceof AppError7) {
1882
+ return thrownError;
1883
+ }
1884
+ return new AppError7({
1885
+ code: AppErrorCode7.InternalServerError,
1886
+ message: thrownError.message,
1887
+ statusCode: 500,
1888
+ cause: thrownError
1889
+ });
1890
+ }
1891
+
1892
+ // src/workflows/workflow-tool-execution.ts
1893
+ import { AppErrorCode as AppErrorCode8 } from "@assemble-dev/shared-types/errors";
1894
+ import { AppError as AppError8 } from "@assemble-dev/shared-utils/errors";
1895
+ async function executeWorkflowTool(toolInput) {
1896
+ const runtimeContext = toolInput.runtimeContext;
1897
+ const workflowTool = findWorkflowToolByName(
1898
+ runtimeContext.definition,
1899
+ toolInput.toolName
1900
+ );
1901
+ const workflowPolicies = runtimeContext.definition.policies;
1902
+ const outcome = await executeToolCall({
1903
+ tool: workflowTool,
1904
+ agentName: toolInput.agentName,
1905
+ input: toolInput.input,
1906
+ policies: workflowPolicies.length > 0 ? [...workflowPolicies] : void 0,
1907
+ traceBus: runtimeContext.traceBus,
1908
+ parentEventId: runtimeContext.parentEventId ?? void 0,
1909
+ now: runtimeContext.now,
1910
+ grantedApproval: toolInput.grantedApproval
1911
+ });
1912
+ if (toolInput.throwOnUnsuccessfulOutcome === false) {
1913
+ return outcome;
1914
+ }
1915
+ return ensureWorkflowToolOutcomeSucceeded(toolInput, outcome);
1916
+ }
1917
+ function findWorkflowToolByName(definition, toolName) {
1918
+ const workflowTool = definition.tools.find(
1919
+ (candidateTool) => candidateTool.name === toolName
1920
+ );
1921
+ if (workflowTool !== void 0) {
1922
+ return workflowTool;
1923
+ }
1924
+ const registeredToolNames = definition.tools.map((registeredTool) => registeredTool.name).join(", ");
1925
+ throw new AppError8({
1926
+ code: AppErrorCode8.NotFound,
1927
+ message: `tool "${toolName}" is not registered on workflow "${definition.name}"; registered tools: [${registeredToolNames}]. Add the tool to the workflow definition before calling it`,
1928
+ statusCode: 404,
1929
+ details: { toolName, workflowName: definition.name }
1930
+ });
1931
+ }
1932
+ function ensureWorkflowToolOutcomeSucceeded(toolInput, outcome) {
1933
+ const stepIndex = toolInput.runtimeContext.stepIndex;
1934
+ if (outcome.status === "blocked") {
1935
+ throw new AppError8({
1936
+ code: AppErrorCode8.Forbidden,
1937
+ message: `tool "${toolInput.toolName}" was blocked at step ${stepIndex} for agent "${toolInput.agentName}": ${outcome.reason}`,
1938
+ statusCode: 403,
1939
+ details: { toolName: toolInput.toolName, stepIndex, reason: outcome.reason }
1940
+ });
1941
+ }
1942
+ if (outcome.status === "failed") {
1943
+ throw new AppError8({
1944
+ code: mapToolCallErrorCodeToAppErrorCode(outcome.error.code),
1945
+ message: `tool "${toolInput.toolName}" failed at step ${stepIndex} for agent "${toolInput.agentName}": ${outcome.error.message}`,
1946
+ statusCode: outcome.error.code === "VALIDATION_FAILED" ? 422 : 500,
1947
+ details: {
1948
+ toolName: toolInput.toolName,
1949
+ stepIndex,
1950
+ reason: outcome.error.message
1951
+ }
1952
+ });
1953
+ }
1954
+ return outcome;
1955
+ }
1956
+ function mapToolCallErrorCodeToAppErrorCode(toolCallErrorCode) {
1957
+ return toolCallErrorCode === "VALIDATION_FAILED" ? AppErrorCode8.ValidationFailed : AppErrorCode8.InternalServerError;
1958
+ }
1959
+
1960
+ // src/workflows/workflow-approval.ts
1961
+ var pendingApprovalArtifactKey = "workflow:pending-approval";
1962
+ var PendingWorkflowApprovalSchema = z3.object({
1963
+ stepIndex: z3.number().int().nonnegative(),
1964
+ agentName: z3.string().min(1),
1965
+ toolName: z3.string().min(1),
1966
+ toolInput: JsonValueSchema2,
1967
+ approvalId: z3.string().min(1),
1968
+ toolRequestEventId: z3.string().nullable(),
1969
+ agentOutput: JsonValueSchema2,
1970
+ runStartedEventId: z3.string().min(1)
1971
+ });
1972
+ function buildBoundToolArtifactKey(stepIndex, agentName, toolName) {
1973
+ return `step-${stepIndex}:${agentName}:tool:${toolName}`;
1974
+ }
1975
+ function buildWorkflowStepContext(context, stepIndex) {
1976
+ return {
1977
+ definition: context.definition,
1978
+ traceBus: context.traceBus,
1979
+ stepIndex,
1980
+ parentEventId: context.runStartedEventId,
1981
+ artifactStore: context.artifactStore,
1982
+ threadMemory: context.threadMemory,
1983
+ now: context.now
1984
+ };
1985
+ }
1986
+ async function applyAgentToolBindingAfterStep(context, stepIndex, agentName, agentOutput) {
1987
+ const binding = findAgentToolBinding(context, agentName);
1988
+ if (binding === null) {
1989
+ return { status: "continue" };
1990
+ }
1991
+ const toolInput = binding.buildToolInput({
1992
+ state: await buildWorkflowRunState(context),
1993
+ stepIndex,
1994
+ agentOutput
1995
+ });
1996
+ const outcome = await executeBoundToolOrFailRun(
1997
+ context,
1998
+ stepIndex,
1999
+ binding,
2000
+ toolInput
2001
+ );
2002
+ if (outcome.status === "awaiting_approval") {
2003
+ return await pauseWorkflowRunForApproval(context, {
2004
+ stepIndex,
2005
+ agentName,
2006
+ toolName: binding.toolName,
2007
+ toolInput,
2008
+ approvalId: outcome.approvalId,
2009
+ toolRequestEventId: outcome.toolRequestEventId,
2010
+ agentOutput,
2011
+ runStartedEventId: context.runStartedEventId
2012
+ });
2013
+ }
2014
+ if (outcome.status === "completed") {
2015
+ await saveBoundToolArtifact(
2016
+ context,
2017
+ stepIndex,
2018
+ agentName,
2019
+ binding.toolName,
2020
+ outcome.output
2021
+ );
2022
+ }
2023
+ return { status: "continue" };
2024
+ }
2025
+ async function executeApprovedWorkflowTool(context, pendingApproval) {
2026
+ const outcome = await executeBoundToolOrFailRun(
2027
+ context,
2028
+ pendingApproval.stepIndex,
2029
+ {
2030
+ agentName: pendingApproval.agentName,
2031
+ toolName: pendingApproval.toolName,
2032
+ buildToolInput: () => pendingApproval.toolInput
2033
+ },
2034
+ pendingApproval.toolInput,
2035
+ {
2036
+ approvalId: pendingApproval.approvalId,
2037
+ toolRequestEventId: pendingApproval.toolRequestEventId
2038
+ }
2039
+ );
2040
+ if (outcome.status === "completed") {
2041
+ await saveBoundToolArtifact(
2042
+ context,
2043
+ pendingApproval.stepIndex,
2044
+ pendingApproval.agentName,
2045
+ pendingApproval.toolName,
2046
+ outcome.output
2047
+ );
2048
+ }
2049
+ }
2050
+ async function executeBoundToolOrFailRun(context, stepIndex, binding, toolInput, grantedApproval) {
2051
+ try {
2052
+ const outcome = await executeWorkflowTool({
2053
+ runtimeContext: buildWorkflowStepContext(context, stepIndex),
2054
+ toolName: binding.toolName,
2055
+ agentName: binding.agentName,
2056
+ input: toolInput,
2057
+ grantedApproval
2058
+ });
2059
+ if (outcome.status === "completed") {
2060
+ return { status: "completed", output: outcome.output };
2061
+ }
2062
+ if (outcome.status === "awaiting_approval") {
2063
+ return outcome;
2064
+ }
2065
+ throw new AppError9({
2066
+ code: AppErrorCode9.InternalServerError,
2067
+ message: `tool "${binding.toolName}" returned unexpected outcome "${outcome.status}" at step ${stepIndex} for agent "${binding.agentName}"`,
2068
+ statusCode: 500,
2069
+ details: { toolName: binding.toolName, stepIndex }
2070
+ });
2071
+ } catch (thrownError) {
2072
+ const bindingError = thrownError instanceof AppError9 ? thrownError : new AppError9({
2073
+ code: AppErrorCode9.InternalServerError,
2074
+ message: `tool "${binding.toolName}" threw at step ${stepIndex} for agent "${binding.agentName}": ${String(thrownError)}`,
2075
+ statusCode: 500,
2076
+ details: { toolName: binding.toolName, stepIndex }
2077
+ });
2078
+ await failWorkflowRun(context, bindingError);
2079
+ throw bindingError;
2080
+ }
2081
+ }
2082
+ async function pauseWorkflowRunForApproval(context, pendingApproval) {
2083
+ await context.artifactStore.saveRunArtifact({
2084
+ key: pendingApprovalArtifactKey,
2085
+ value: PendingWorkflowApprovalSchema.parse(pendingApproval),
2086
+ producedByAgentName: pendingApproval.agentName,
2087
+ createdAt: context.now().toISOString()
2088
+ });
2089
+ await persistWorkflowRunState(context);
2090
+ await context.traceBus.flushSinks();
2091
+ return { status: "paused", approvalId: pendingApproval.approvalId };
2092
+ }
2093
+ async function saveBoundToolArtifact(context, stepIndex, agentName, toolName, toolOutput) {
2094
+ await context.artifactStore.saveRunArtifact({
2095
+ key: buildBoundToolArtifactKey(stepIndex, agentName, toolName),
2096
+ value: toolOutput,
2097
+ producedByAgentName: agentName,
2098
+ createdAt: context.now().toISOString()
2099
+ });
2100
+ }
2101
+ function findAgentToolBinding(context, agentName) {
2102
+ const binding = context.definition.agentToolBindings.find(
2103
+ (candidateBinding) => candidateBinding.agentName === agentName
2104
+ );
2105
+ return binding ?? null;
2106
+ }
2107
+
2108
+ // src/workflows/workflow-parallel-execution.ts
2109
+ import { AppErrorCode as AppErrorCode10 } from "@assemble-dev/shared-types/errors";
2110
+ import { AppError as AppError10 } from "@assemble-dev/shared-utils/errors";
2111
+
2112
+ // src/workflows/workflow-topology.ts
2113
+ import { z as z4 } from "zod";
2114
+ var finishWorkflow = "__finish__";
2115
+ var defaultSupervisorMaxSteps = 10;
2116
+ var defaultParallelMergedArtifactKey = "parallel:merged";
2117
+ var PipelineTopologySchema = z4.object({
2118
+ kind: z4.literal("pipeline")
2119
+ });
2120
+ var SupervisorTopologySchema = z4.object({
2121
+ kind: z4.literal("supervisor"),
2122
+ supervisorName: z4.string().min(1),
2123
+ route: z4.custom(
2124
+ (value) => typeof value === "function"
2125
+ ),
2126
+ maxSteps: z4.number().int().min(1).optional(),
2127
+ stopWhen: z4.custom((value) => typeof value === "function").optional()
2128
+ });
2129
+ var ParallelTopologySchema = z4.object({
2130
+ kind: z4.literal("parallel"),
2131
+ agentNames: z4.array(z4.string().min(1)).min(1).optional(),
2132
+ mergedArtifactKey: z4.string().min(1).optional()
2133
+ });
2134
+ var WorkflowTopologySchema = z4.discriminatedUnion("kind", [
2135
+ PipelineTopologySchema,
2136
+ SupervisorTopologySchema,
2137
+ ParallelTopologySchema
2138
+ ]);
2139
+ var defaultWorkflowTopology = Object.freeze({
2140
+ kind: "pipeline"
2141
+ });
2142
+
2143
+ // src/workflows/workflow-parallel-execution.ts
2144
+ function buildParallelThreadKey(runId, agentName) {
2145
+ return `${runId}:${agentName}`;
2146
+ }
2147
+ async function executeParallelSteps(context, topology, start) {
2148
+ const fanOutAgents = await resolveParallelFanOutAgents(context, topology);
2149
+ const branchContext = {
2150
+ ...context,
2151
+ artifactStore: buildSerializedArtifactStore(context.artifactStore)
2152
+ };
2153
+ const branchPromises = fanOutAgents.map(
2154
+ async (fanOutAgent, fanOutIndex) => await executeParallelBranch(
2155
+ branchContext,
2156
+ fanOutIndex,
2157
+ fanOutAgent,
2158
+ start.input
2159
+ )
2160
+ );
2161
+ const branchResults = await collectSettledParallelBranchResults(
2162
+ branchPromises
2163
+ );
2164
+ await ensureAllParallelBranchesSucceeded(context, branchResults);
2165
+ const mergedOutput = mergeParallelBranchOutputs(branchResults);
2166
+ await saveMergedParallelArtifact(context, topology, mergedOutput);
2167
+ await persistWorkflowRunState(context);
2168
+ return { status: "completed", output: mergedOutput };
2169
+ }
2170
+ function buildSerializedArtifactStore(artifactStore) {
2171
+ let pendingWrite = Promise.resolve();
2172
+ return {
2173
+ async saveRunArtifact(artifact) {
2174
+ pendingWrite = pendingWrite.then(
2175
+ async () => await artifactStore.saveRunArtifact(artifact)
2176
+ );
2177
+ return await pendingWrite;
2178
+ },
2179
+ async getRunArtifact(key) {
2180
+ return await artifactStore.getRunArtifact(key);
2181
+ },
2182
+ async listRunArtifacts() {
2183
+ return await artifactStore.listRunArtifacts();
2184
+ }
2185
+ };
2186
+ }
2187
+ async function collectSettledParallelBranchResults(branchPromises) {
2188
+ const branchResults = [];
2189
+ for (const branchPromise of branchPromises) {
2190
+ branchResults.push(await branchPromise);
2191
+ }
2192
+ return branchResults;
2193
+ }
2194
+ async function resolveParallelFanOutAgents(context, topology) {
2195
+ if (topology.agentNames === void 0) {
2196
+ return [...context.definition.agents];
2197
+ }
2198
+ const fanOutAgents = [];
2199
+ for (const agentName of topology.agentNames) {
2200
+ const fanOutAgent = context.definition.agents.find(
2201
+ (agentDefinition) => agentDefinition.name === agentName
2202
+ );
2203
+ if (fanOutAgent === void 0) {
2204
+ return await failParallelRunForUnknownAgent(context, agentName);
2205
+ }
2206
+ fanOutAgents.push(fanOutAgent);
2207
+ }
2208
+ return fanOutAgents;
2209
+ }
2210
+ async function failParallelRunForUnknownAgent(context, agentName) {
2211
+ const validAgentNames = context.definition.agents.map((agentDefinition) => agentDefinition.name).join(", ");
2212
+ const unknownAgentError = new AppError10({
2213
+ code: AppErrorCode10.BadRequest,
2214
+ message: `workflow "${context.definition.name}" parallel topology selected unknown agent "${agentName}"; valid agent names: ${validAgentNames}`,
2215
+ statusCode: 400,
2216
+ details: {
2217
+ workflowName: context.definition.name,
2218
+ agentName,
2219
+ validAgentNames
2220
+ }
2221
+ });
2222
+ await failWorkflowRun(context, unknownAgentError);
2223
+ throw unknownAgentError;
2224
+ }
2225
+ async function executeParallelBranch(context, fanOutIndex, fanOutAgent, workflowInput) {
2226
+ try {
2227
+ const agentResult = await runAgent({
2228
+ definition: fanOutAgent,
2229
+ input: workflowInput,
2230
+ traceBus: context.traceBus,
2231
+ parentEventId: context.runStartedEventId,
2232
+ threadMemory: createThreadMemory({
2233
+ provider: context.memoryProvider,
2234
+ threadKey: buildParallelThreadKey(context.runId, fanOutAgent.name)
2235
+ }),
2236
+ now: context.now
2237
+ });
2238
+ await saveParallelBranchArtifact(
2239
+ context,
2240
+ fanOutIndex,
2241
+ fanOutAgent.name,
2242
+ agentResult.output
2243
+ );
2244
+ await applyParallelAgentToolBinding(
2245
+ context,
2246
+ fanOutIndex,
2247
+ fanOutAgent.name,
2248
+ agentResult.output
2249
+ );
2250
+ return {
2251
+ status: "completed",
2252
+ agentName: fanOutAgent.name,
2253
+ output: agentResult.output
2254
+ };
2255
+ } catch (thrownError) {
2256
+ const branchError = thrownError instanceof Error ? thrownError : new Error(String(thrownError));
2257
+ return {
2258
+ status: "failed",
2259
+ agentName: fanOutAgent.name,
2260
+ error: mapParallelBranchErrorToAppError(branchError)
2261
+ };
2262
+ }
2263
+ }
2264
+ async function applyParallelAgentToolBinding(context, fanOutIndex, agentName, agentOutput) {
2265
+ const bindingOutcome = await applyAgentToolBindingAfterStep(
2266
+ context,
2267
+ fanOutIndex,
2268
+ agentName,
2269
+ agentOutput
2270
+ );
2271
+ if (bindingOutcome.status !== "paused") {
2272
+ return;
2273
+ }
2274
+ throw new AppError10({
2275
+ code: AppErrorCode10.Conflict,
2276
+ message: `workflow "${context.definition.name}" agent "${agentName}" has a tool binding that requires approval, which is not supported inside parallel fan-out; run this agent in a pipeline or supervisor topology, or remove the approval requirement from the bound tool`,
2277
+ statusCode: 409,
2278
+ details: {
2279
+ workflowName: context.definition.name,
2280
+ agentName,
2281
+ fanOutIndex,
2282
+ approvalId: bindingOutcome.approvalId
2283
+ }
2284
+ });
2285
+ }
2286
+ async function ensureAllParallelBranchesSucceeded(context, branchResults) {
2287
+ const failures = branchResults.filter(
2288
+ (branchResult) => branchResult.status === "failed"
2289
+ );
2290
+ const firstFailure = failures[0];
2291
+ if (firstFailure === void 0) {
2292
+ return;
2293
+ }
2294
+ const failedAgentNames = failures.map((failure) => failure.agentName).join(", ");
2295
+ const failureReasons = failures.map((failure) => `${failure.agentName}: ${failure.error.message}`).join("; ");
2296
+ const parallelError = new AppError10({
2297
+ code: firstFailure.error.code,
2298
+ message: `workflow "${context.definition.name}" parallel fan-out failed for agent(s) [${failedAgentNames}]: ${failureReasons}`,
2299
+ statusCode: firstFailure.error.statusCode,
2300
+ details: {
2301
+ workflowName: context.definition.name,
2302
+ failedAgentNames,
2303
+ failedAgentCount: failures.length
2304
+ },
2305
+ cause: firstFailure.error
2306
+ });
2307
+ await failWorkflowRun(context, parallelError);
2308
+ throw parallelError;
2309
+ }
2310
+ function mergeParallelBranchOutputs(branchResults) {
2311
+ const mergedOutput = {};
2312
+ for (const branchResult of branchResults) {
2313
+ if (branchResult.status === "completed") {
2314
+ mergedOutput[branchResult.agentName] = branchResult.output;
2315
+ }
2316
+ }
2317
+ return mergedOutput;
2318
+ }
2319
+ async function saveParallelBranchArtifact(context, fanOutIndex, agentName, branchOutput) {
2320
+ await context.artifactStore.saveRunArtifact({
2321
+ key: `step-${fanOutIndex}:${agentName}`,
2322
+ value: branchOutput,
2323
+ producedByAgentName: agentName,
2324
+ createdAt: context.now().toISOString()
2325
+ });
2326
+ }
2327
+ async function saveMergedParallelArtifact(context, topology, mergedOutput) {
2328
+ await context.artifactStore.saveRunArtifact({
2329
+ key: topology.mergedArtifactKey ?? defaultParallelMergedArtifactKey,
2330
+ value: mergedOutput,
2331
+ createdAt: context.now().toISOString()
2332
+ });
2333
+ }
2334
+ function mapParallelBranchErrorToAppError(branchError) {
2335
+ if (branchError instanceof AppError10) {
2336
+ return branchError;
2337
+ }
2338
+ return new AppError10({
2339
+ code: AppErrorCode10.InternalServerError,
2340
+ message: branchError.message,
2341
+ statusCode: 500,
2342
+ cause: branchError
2343
+ });
2344
+ }
2345
+
2346
+ // src/workflows/workflow-supervisor-execution.ts
2347
+ import { AppErrorCode as AppErrorCode11 } from "@assemble-dev/shared-types/errors";
2348
+ import { AppError as AppError11 } from "@assemble-dev/shared-utils/errors";
2349
+ async function executeSupervisorSteps(context, topology, start) {
2350
+ let loopState = {
2351
+ stepIndex: start.startStepIndex,
2352
+ lastAgentName: start.lastAgentName,
2353
+ lastOutput: start.lastAgentName === null ? null : start.input,
2354
+ currentOutput: start.input
2355
+ };
2356
+ for (; ; ) {
2357
+ const routeContext = buildWorkflowRouteContext(context, loopState);
2358
+ if (topology.stopWhen !== void 0 && topology.stopWhen(routeContext)) {
2359
+ return { status: "completed", output: loopState.currentOutput };
2360
+ }
2361
+ const routeDecision = await resolveSupervisorRouteDecision(
2362
+ context,
2363
+ topology,
2364
+ routeContext
2365
+ );
2366
+ if (routeDecision.agentName === finishWorkflow) {
2367
+ return { status: "completed", output: loopState.currentOutput };
2368
+ }
2369
+ const stepOutput = await executeSupervisorStep(
2370
+ context,
2371
+ topology,
2372
+ loopState,
2373
+ routeDecision
2374
+ );
2375
+ const bindingOutcome = await applyAgentToolBindingAfterStep(
2376
+ context,
2377
+ loopState.stepIndex,
2378
+ routeDecision.agentName,
2379
+ stepOutput
2380
+ );
2381
+ if (bindingOutcome.status === "paused") {
2382
+ return bindingOutcome;
2383
+ }
2384
+ if (shouldStopWorkflowRun(context, loopState.stepIndex)) {
2385
+ return { status: "completed", output: stepOutput };
2386
+ }
2387
+ loopState = {
2388
+ stepIndex: loopState.stepIndex + 1,
2389
+ lastAgentName: routeDecision.agentName,
2390
+ lastOutput: stepOutput,
2391
+ currentOutput: stepOutput
2392
+ };
2393
+ }
2394
+ }
2395
+ async function executeSupervisorStep(context, topology, loopState, routeDecision) {
2396
+ const selectedAgent = await resolveRoutedWorkflowAgent(
2397
+ context,
2398
+ topology,
2399
+ routeDecision,
2400
+ loopState.stepIndex
2401
+ );
2402
+ await ensureSupervisorStepBudget(context, topology, loopState.stepIndex);
2403
+ emitRouteSelectedEvent(
2404
+ buildWorkflowEventContext(context),
2405
+ context.runStartedEventId,
2406
+ {
2407
+ supervisorName: topology.supervisorName,
2408
+ selectedAgentName: selectedAgent.name,
2409
+ candidateAgentNames: listWorkflowAgentNames(context),
2410
+ reason: routeDecision.reason
2411
+ }
2412
+ );
2413
+ const stepOutput = await executeWorkflowStep(
2414
+ context,
2415
+ loopState.stepIndex,
2416
+ selectedAgent,
2417
+ loopState.currentOutput
2418
+ );
2419
+ await persistWorkflowRunState(context);
2420
+ return stepOutput;
2421
+ }
2422
+ async function resolveSupervisorRouteDecision(context, topology, routeContext) {
2423
+ try {
2424
+ const routeTarget = await topology.route(routeContext);
2425
+ return normalizeWorkflowRouteTarget(routeTarget);
2426
+ } catch (thrownError) {
2427
+ const causeError = thrownError instanceof Error ? thrownError : new Error(String(thrownError));
2428
+ return await failSupervisorRun(
2429
+ context,
2430
+ new AppError11({
2431
+ code: AppErrorCode11.InternalServerError,
2432
+ message: `workflow "${context.definition.name}" supervisor "${topology.supervisorName}" route function threw at step ${routeContext.stepIndex}: ${causeError.message}; fix the route function to return an agent name or finishWorkflow`,
2433
+ statusCode: 500,
2434
+ details: {
2435
+ workflowName: context.definition.name,
2436
+ supervisorName: topology.supervisorName,
2437
+ stepIndex: routeContext.stepIndex,
2438
+ reason: causeError.message
2439
+ },
2440
+ cause: causeError
2441
+ })
2442
+ );
2443
+ }
2444
+ }
2445
+ async function resolveRoutedWorkflowAgent(context, topology, routeDecision, stepIndex) {
2446
+ const selectedAgent = context.definition.agents.find(
2447
+ (agentDefinition) => agentDefinition.name === routeDecision.agentName
2448
+ );
2449
+ if (selectedAgent !== void 0) {
2450
+ return selectedAgent;
2451
+ }
2452
+ const validAgentNames = listWorkflowAgentNames(context).join(", ");
2453
+ return await failSupervisorRun(
2454
+ context,
2455
+ new AppError11({
2456
+ code: AppErrorCode11.BadRequest,
2457
+ message: `workflow "${context.definition.name}" supervisor "${topology.supervisorName}" routed to unknown agent "${routeDecision.agentName}" at step ${stepIndex}; valid agent names: ${validAgentNames}`,
2458
+ statusCode: 400,
2459
+ details: {
2460
+ workflowName: context.definition.name,
2461
+ supervisorName: topology.supervisorName,
2462
+ selectedAgentName: routeDecision.agentName,
2463
+ validAgentNames,
2464
+ stepIndex
2465
+ }
2466
+ })
2467
+ );
2468
+ }
2469
+ async function ensureSupervisorStepBudget(context, topology, stepIndex) {
2470
+ const maxSteps = topology.maxSteps ?? defaultSupervisorMaxSteps;
2471
+ if (stepIndex < maxSteps) {
2472
+ return;
2473
+ }
2474
+ await failSupervisorRun(
2475
+ context,
2476
+ new AppError11({
2477
+ code: AppErrorCode11.Conflict,
2478
+ message: `workflow "${context.definition.name}" supervisor "${topology.supervisorName}" exceeded ${maxSteps} steps; return finishWorkflow from the route function, add a stopWhen condition, or increase maxSteps`,
2479
+ statusCode: 409,
2480
+ details: {
2481
+ workflowName: context.definition.name,
2482
+ supervisorName: topology.supervisorName,
2483
+ maxSteps
2484
+ }
2485
+ })
2486
+ );
2487
+ }
2488
+ async function failSupervisorRun(context, supervisorError) {
2489
+ await failWorkflowRun(context, supervisorError);
2490
+ throw supervisorError;
2491
+ }
2492
+ function normalizeWorkflowRouteTarget(routeTarget) {
2493
+ if (typeof routeTarget === "string") {
2494
+ return {
2495
+ agentName: routeTarget,
2496
+ reason: `route function selected ${routeTarget}`
2497
+ };
2498
+ }
2499
+ return routeTarget;
2500
+ }
2501
+ function listWorkflowAgentNames(context) {
2502
+ return context.definition.agents.map(
2503
+ (agentDefinition) => agentDefinition.name
2504
+ );
2505
+ }
2506
+ function buildWorkflowRouteContext(context, loopState) {
2507
+ return {
2508
+ state: context.traceBus.getRunState(),
2509
+ stepIndex: loopState.stepIndex,
2510
+ lastAgentName: loopState.lastAgentName,
2511
+ lastOutput: loopState.lastOutput
2512
+ };
2513
+ }
2514
+
2515
+ // src/workflows/workflow-runtime.ts
2516
+ async function runWorkflow(runInput) {
2517
+ const validatedInput = validateWorkflowInput(
2518
+ runInput.definition,
2519
+ runInput.input
2520
+ );
2521
+ const context = createWorkflowRuntimeContext(
2522
+ runInput.definition,
2523
+ validatedInput,
2524
+ runInput.options
2525
+ );
2526
+ await persistWorkflowRunState(context);
2527
+ return await continueWorkflowRunFromStep(context, {
2528
+ input: validatedInput,
2529
+ startStepIndex: 0,
2530
+ lastAgentName: null
2531
+ });
2532
+ }
2533
+ async function continueWorkflowRunFromStep(context, start) {
2534
+ const outcome = await executeWorkflowTopologySteps(context, start);
2535
+ if (outcome.status === "paused") {
2536
+ return await buildAwaitingApprovalRunResult(context, outcome.approvalId);
2537
+ }
2538
+ return await completeWorkflowRun(context, outcome.output);
2539
+ }
2540
+ async function executeWorkflowTopologySteps(context, start) {
2541
+ const topology = context.definition.topology;
2542
+ if (topology.kind === "supervisor") {
2543
+ return await executeSupervisorSteps(context, topology, start);
2544
+ }
2545
+ if (topology.kind === "parallel") {
2546
+ return await executeParallelSteps(context, topology, start);
2547
+ }
2548
+ return await executePipelineSteps(context, start);
2549
+ }
2550
+ function validateWorkflowInput(definition, input) {
2551
+ const parsedInput = definition.inputSchema.safeParse(input);
2552
+ if (parsedInput.success) {
2553
+ return parsedInput.data;
2554
+ }
2555
+ throw new AppError12({
2556
+ code: AppErrorCode12.ValidationFailed,
2557
+ message: `workflow "${definition.name}" input failed schema validation: ${parsedInput.error.message}`,
2558
+ statusCode: 422
2559
+ });
2560
+ }
2561
+ function createWorkflowRuntimeContext(definition, validatedInput, options) {
2562
+ const now = options?.now ?? (() => /* @__PURE__ */ new Date());
2563
+ const runId = options?.runId ?? generateRunId();
2564
+ const startedAtMs = now().getTime();
2565
+ const traceBus = createTraceBus({
2566
+ runId,
2567
+ workflowName: definition.name,
2568
+ input: validatedInput,
2569
+ sinks: options?.sinks ?? [],
2570
+ now
2571
+ });
2572
+ const memoryConfig = resolveWorkflowMemoryConfig(definition.memory ?? void 0);
2573
+ const runStartedEvent = emitRunStartedEvent(
2574
+ { traceBus, runId, workflowName: definition.name, now },
2575
+ validatedInput
2576
+ );
2577
+ return {
2578
+ definition,
2579
+ traceBus,
2580
+ runId,
2581
+ runStartedEventId: runStartedEvent.id,
2582
+ threadMemory: createThreadMemory({
2583
+ provider: memoryConfig.provider,
2584
+ threadKey: runId
2585
+ }),
2586
+ memoryProvider: memoryConfig.provider,
2587
+ artifactStore: createArtifactStore({
2588
+ provider: memoryConfig.provider,
2589
+ runId
2590
+ }),
2591
+ now,
2592
+ startedAtMs,
2593
+ persistRunState: options?.persistRunState ?? true,
2594
+ runStateDirectory: options?.runStateDirectory ?? defaultRunStateDirectory
2595
+ };
2596
+ }
2597
+ async function executePipelineSteps(context, start) {
2598
+ const stepAgents = context.definition.agents;
2599
+ let currentStepInput = start.input;
2600
+ for (let stepIndex = start.startStepIndex; stepIndex < stepAgents.length; stepIndex += 1) {
2601
+ const stepAgent = stepAgents[stepIndex];
2602
+ if (stepAgent === void 0) {
2603
+ break;
2604
+ }
2605
+ currentStepInput = await executeWorkflowStep(
2606
+ context,
2607
+ stepIndex,
2608
+ stepAgent,
2609
+ currentStepInput
2610
+ );
2611
+ await persistWorkflowRunState(context);
2612
+ const bindingOutcome = await applyAgentToolBindingAfterStep(
2613
+ context,
2614
+ stepIndex,
2615
+ stepAgent.name,
2616
+ currentStepInput
2617
+ );
2618
+ if (bindingOutcome.status === "paused") {
2619
+ return bindingOutcome;
2620
+ }
2621
+ if (stepIndex < stepAgents.length - 1 && shouldStopWorkflowRun(context, stepIndex)) {
2622
+ break;
2623
+ }
2624
+ }
2625
+ return { status: "completed", output: currentStepInput };
2626
+ }
2627
+ async function buildAwaitingApprovalRunResult(context, approvalId) {
2628
+ return {
2629
+ status: "awaiting_approval",
2630
+ runId: context.runId,
2631
+ approvalId,
2632
+ state: await buildWorkflowRunState(context),
2633
+ events: context.traceBus.listTraceEvents()
2634
+ };
2635
+ }
2636
+ async function completeWorkflowRun(context, lastStepOutput) {
2637
+ const finalOutput = await validateWorkflowOutput(context, lastStepOutput);
2638
+ emitRunCompletedEvent(
2639
+ buildWorkflowEventContext(context),
2640
+ context.runStartedEventId,
2641
+ finalOutput,
2642
+ calculateWorkflowElapsedMs(context)
2643
+ );
2644
+ await persistWorkflowRunState(context);
2645
+ await context.traceBus.flushSinks();
2646
+ return {
2647
+ status: "completed",
2648
+ runId: context.runId,
2649
+ output: finalOutput,
2650
+ state: await buildWorkflowRunState(context),
2651
+ events: context.traceBus.listTraceEvents()
2652
+ };
2653
+ }
2654
+ async function validateWorkflowOutput(context, lastStepOutput) {
2655
+ if (context.definition.outputSchema === null) {
2656
+ return lastStepOutput;
2657
+ }
2658
+ const parsedOutput = context.definition.outputSchema.safeParse(lastStepOutput);
2659
+ if (parsedOutput.success) {
2660
+ return parsedOutput.data;
2661
+ }
2662
+ const validationError = new AppError12({
2663
+ code: AppErrorCode12.ValidationFailed,
2664
+ message: `workflow "${context.definition.name}" output failed schema validation: ${parsedOutput.error.message}`,
2665
+ statusCode: 422
2666
+ });
2667
+ await failWorkflowRun(context, validationError);
2668
+ throw validationError;
2669
+ }
2670
+
2671
+ // src/workflows/workflow-definition.ts
2672
+ function workflow(config) {
2673
+ const definition = Object.freeze({
2674
+ name: config.name,
2675
+ inputSchema: config.inputSchema,
2676
+ outputSchema: config.outputSchema ?? null,
2677
+ agents: Object.freeze([...config.agents]),
2678
+ tools: Object.freeze([...config.tools ?? []]),
2679
+ agentToolBindings: Object.freeze([...config.agentToolBindings ?? []]),
2680
+ topology: config.topology ?? defaultWorkflowTopology,
2681
+ policies: Object.freeze([...config.policies ?? []]),
2682
+ memory: config.memory ?? null,
2683
+ stopCondition: config.stopCondition ?? null,
2684
+ continueOnAgentFailure: config.continueOnAgentFailure ?? false,
2685
+ run: async (input, options) => {
2686
+ return await runWorkflow({ definition, input, options });
2687
+ }
2688
+ });
2689
+ return definition;
2690
+ }
2691
+
2692
+ // src/workflows/resume-run.ts
2693
+ import { existsSync } from "fs";
2694
+ import { AppErrorCode as AppErrorCode13 } from "@assemble-dev/shared-types/errors";
2695
+ import { AppError as AppError13 } from "@assemble-dev/shared-utils/errors";
2696
+ async function resumeRun(resumeInput) {
2697
+ const directory = resumeInput.options?.runStateDirectory ?? defaultRunStateDirectory;
2698
+ const persistedState = loadPersistedRunStateForResume(
2699
+ resumeInput.runId,
2700
+ directory
2701
+ );
2702
+ const pendingApproval = findPendingWorkflowApproval(persistedState);
2703
+ const context = await createResumedWorkflowRuntimeContext(
2704
+ resumeInput.definition,
2705
+ persistedState,
2706
+ pendingApproval,
2707
+ resumeInput.options
2708
+ );
2709
+ if (!resumeInput.decision.approved) {
2710
+ return await rejectPendingWorkflowApproval(
2711
+ context,
2712
+ pendingApproval,
2713
+ resumeInput.decision
2714
+ );
2715
+ }
2716
+ return await approvePendingWorkflowApproval(
2717
+ context,
2718
+ pendingApproval,
2719
+ resumeInput.decision
2720
+ );
2721
+ }
2722
+ function loadPersistedRunStateForResume(runId, directory) {
2723
+ const filePath = resolveRunStateFilePath(runId, directory);
2724
+ if (!existsSync(filePath)) {
2725
+ throw new AppError13({
2726
+ code: AppErrorCode13.NotFound,
2727
+ message: `run state file not found at "${filePath}" for run "${runId}"; the run was never persisted or the runStateDirectory is wrong`,
2728
+ statusCode: 404,
2729
+ details: { runId, filePath }
2730
+ });
2731
+ }
2732
+ const persistedState = loadRunStateFile({ runId, directory });
2733
+ if (persistedState.status !== "awaiting_approval") {
2734
+ throw new AppError13({
2735
+ code: AppErrorCode13.Conflict,
2736
+ message: `run "${runId}" has status "${persistedState.status}" and cannot be resumed; only runs with status "awaiting_approval" can be resumed`,
2737
+ statusCode: 409,
2738
+ details: { runId, status: persistedState.status }
2739
+ });
2740
+ }
2741
+ return persistedState;
2742
+ }
2743
+ function findPendingWorkflowApproval(persistedState) {
2744
+ const pendingArtifact = persistedState.artifacts.find(
2745
+ (artifact) => artifact.key === pendingApprovalArtifactKey
2746
+ );
2747
+ if (pendingArtifact === void 0) {
2748
+ throw new AppError13({
2749
+ code: AppErrorCode13.Conflict,
2750
+ message: `run "${persistedState.runId}" is awaiting approval but has no "${pendingApprovalArtifactKey}" artifact; the persisted state is incomplete and the run cannot be resumed`,
2751
+ statusCode: 409,
2752
+ details: { runId: persistedState.runId }
2753
+ });
2754
+ }
2755
+ return PendingWorkflowApprovalSchema.parse(pendingArtifact.value);
2756
+ }
2757
+ async function createResumedWorkflowRuntimeContext(definition, persistedState, pendingApproval, options) {
2758
+ const now = options?.now ?? (() => /* @__PURE__ */ new Date());
2759
+ const traceBus = createTraceBus({
2760
+ runId: persistedState.runId,
2761
+ workflowName: definition.name,
2762
+ input: persistedState.input,
2763
+ sinks: options?.sinks ?? [],
2764
+ now,
2765
+ initialRunState: persistedState
2766
+ });
2767
+ const memoryConfig = resolveWorkflowMemoryConfig(definition.memory ?? void 0);
2768
+ const artifactStore = createArtifactStore({
2769
+ provider: memoryConfig.provider,
2770
+ runId: persistedState.runId
2771
+ });
2772
+ for (const artifact of persistedState.artifacts) {
2773
+ await artifactStore.saveRunArtifact(artifact);
2774
+ }
2775
+ return {
2776
+ definition,
2777
+ traceBus,
2778
+ runId: persistedState.runId,
2779
+ runStartedEventId: pendingApproval.runStartedEventId,
2780
+ threadMemory: createThreadMemory({
2781
+ provider: memoryConfig.provider,
2782
+ threadKey: persistedState.runId
2783
+ }),
2784
+ memoryProvider: memoryConfig.provider,
2785
+ artifactStore,
2786
+ now,
2787
+ startedAtMs: resolveResumedRunStartedAtMs(persistedState, now),
2788
+ persistRunState: options?.persistRunState ?? true,
2789
+ runStateDirectory: options?.runStateDirectory ?? defaultRunStateDirectory
2790
+ };
2791
+ }
2792
+ function resolveResumedRunStartedAtMs(persistedState, now) {
2793
+ if (persistedState.startedAt === null) {
2794
+ return now().getTime();
2795
+ }
2796
+ return new Date(persistedState.startedAt).getTime();
2797
+ }
2798
+ async function rejectPendingWorkflowApproval(context, pendingApproval, decision) {
2799
+ const rejectionReason = decision.reason ?? "approval rejected";
2800
+ emitApprovalRejectedEvent(
2801
+ buildWorkflowEventContext(context),
2802
+ {
2803
+ approvalId: pendingApproval.approvalId,
2804
+ decidedBy: decision.approvedBy,
2805
+ parentEventId: pendingApproval.toolRequestEventId
2806
+ },
2807
+ rejectionReason
2808
+ );
2809
+ await failWorkflowRun(
2810
+ context,
2811
+ new AppError13({
2812
+ code: AppErrorCode13.Forbidden,
2813
+ message: `tool "${pendingApproval.toolName}" at step ${pendingApproval.stepIndex} was rejected by "${decision.approvedBy}": ${rejectionReason}`,
2814
+ statusCode: 403,
2815
+ details: {
2816
+ toolName: pendingApproval.toolName,
2817
+ stepIndex: pendingApproval.stepIndex,
2818
+ decidedBy: decision.approvedBy,
2819
+ reason: rejectionReason
2820
+ }
2821
+ })
2822
+ );
2823
+ await context.traceBus.flushSinks();
2824
+ return {
2825
+ status: "rejected",
2826
+ runId: context.runId,
2827
+ state: await buildWorkflowRunState(context),
2828
+ events: context.traceBus.listTraceEvents()
2829
+ };
2830
+ }
2831
+ async function approvePendingWorkflowApproval(context, pendingApproval, decision) {
2832
+ emitApprovalApprovedEvent(buildWorkflowEventContext(context), {
2833
+ approvalId: pendingApproval.approvalId,
2834
+ decidedBy: decision.approvedBy,
2835
+ parentEventId: pendingApproval.toolRequestEventId
2836
+ });
2837
+ await executeApprovedWorkflowTool(context, pendingApproval);
2838
+ const result = await continueWorkflowRunFromStep(context, {
2839
+ input: pendingApproval.agentOutput,
2840
+ startStepIndex: pendingApproval.stepIndex + 1,
2841
+ lastAgentName: pendingApproval.agentName
2842
+ });
2843
+ await context.traceBus.flushSinks();
2844
+ return result;
2845
+ }
2846
+
2847
+ // src/memory/json-file-provider.ts
2848
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2849
+ import { dirname } from "path";
2850
+ import { JsonObjectSchema as JsonObjectSchema2 } from "@assemble-dev/shared-types/json";
2851
+ function parseMemoryFileContents(rawContents) {
2852
+ try {
2853
+ const parsedContents = JsonObjectSchema2.parse(JSON.parse(rawContents));
2854
+ return new Map(Object.entries(parsedContents));
2855
+ } catch {
2856
+ return /* @__PURE__ */ new Map();
2857
+ }
2858
+ }
2859
+ function loadMemoryEntriesFromFile(filePath) {
2860
+ if (!existsSync2(filePath)) {
2861
+ return /* @__PURE__ */ new Map();
2862
+ }
2863
+ return parseMemoryFileContents(readFileSync2(filePath, "utf8"));
2864
+ }
2865
+ function writeMemoryEntriesToFile(filePath, memoryEntries) {
2866
+ mkdirSync2(dirname(filePath), { recursive: true });
2867
+ writeFileSync2(
2868
+ filePath,
2869
+ JSON.stringify(Object.fromEntries(memoryEntries)),
2870
+ "utf8"
2871
+ );
2872
+ }
2873
+ function createJsonFileMemoryProvider(options) {
2874
+ let memoryEntries = null;
2875
+ const ensureMemoryEntriesLoaded = () => {
2876
+ if (memoryEntries === null) {
2877
+ memoryEntries = loadMemoryEntriesFromFile(options.filePath);
2878
+ }
2879
+ return memoryEntries;
2880
+ };
2881
+ return {
2882
+ async getMemoryValue(key) {
2883
+ return await Promise.resolve(
2884
+ ensureMemoryEntriesLoaded().get(key) ?? null
2885
+ );
2886
+ },
2887
+ async setMemoryValue(key, value) {
2888
+ const loadedEntries = ensureMemoryEntriesLoaded();
2889
+ loadedEntries.set(key, value);
2890
+ writeMemoryEntriesToFile(options.filePath, loadedEntries);
2891
+ await Promise.resolve();
2892
+ },
2893
+ async appendMemoryValue(key, value) {
2894
+ const loadedEntries = ensureMemoryEntriesLoaded();
2895
+ loadedEntries.set(key, buildAppendedMemoryValue(loadedEntries.get(key), value));
2896
+ writeMemoryEntriesToFile(options.filePath, loadedEntries);
2897
+ await Promise.resolve();
2898
+ },
2899
+ async listMemoryKeys() {
2900
+ return await Promise.resolve([...ensureMemoryEntriesLoaded().keys()]);
2901
+ }
2902
+ };
2903
+ }
2904
+
2905
+ // src/tracing/console-sink.ts
2906
+ var ansiCodes = {
2907
+ reset: "\x1B[0m",
2908
+ dim: "\x1B[2m",
2909
+ green: "\x1B[32m",
2910
+ red: "\x1B[31m",
2911
+ yellow: "\x1B[33m",
2912
+ blue: "\x1B[34m",
2913
+ magenta: "\x1B[35m",
2914
+ cyan: "\x1B[36m"
2915
+ };
2916
+ var colorByEventCategory = {
2917
+ run: "magenta",
2918
+ agent: "blue",
2919
+ tool: "cyan",
2920
+ policy: "yellow",
2921
+ route: "yellow",
2922
+ approval: "yellow",
2923
+ eval: "green",
2924
+ replay: "green"
2925
+ };
2926
+ function createConsoleSink(options = {}) {
2927
+ const writeLine = options.writeLine ?? ((line) => process.stdout.write(`${line}
2928
+ `));
2929
+ const useColor = options.useColor ?? true;
2930
+ return {
2931
+ name: "console",
2932
+ writeTraceEvent(event) {
2933
+ writeLine(formatTraceEventLine(event, useColor));
2934
+ }
2935
+ };
2936
+ }
2937
+ function formatTraceEventLine(event, useColor) {
2938
+ const time = event.timestamp.slice(11, 23);
2939
+ const category = event.type.split(".")[0] ?? "run";
2940
+ const detail = formatTraceEventDetail(event);
2941
+ const suffix = detail === "" ? "" : ` ${detail}`;
2942
+ if (!useColor) {
2943
+ return `[${time}] ${event.type}${suffix}`;
2944
+ }
2945
+ const color = ansiCodes[colorByEventCategory[category] ?? "blue"];
2946
+ const failed = event.type.endsWith(".failed") || event.type.endsWith(".blocked");
2947
+ const typeColor = failed ? ansiCodes.red : color;
2948
+ return `${ansiCodes.dim}[${time}]${ansiCodes.reset} ${typeColor}${event.type}${ansiCodes.reset}${suffix}`;
2949
+ }
2950
+ function formatTraceEventDetail(event) {
2951
+ const parts = [];
2952
+ if ("agentName" in event && event.agentName !== void 0) {
2953
+ parts.push(`agent=${event.agentName}`);
2954
+ }
2955
+ if ("toolName" in event && event.toolName !== void 0) {
2956
+ parts.push(`tool=${event.toolName}`);
2957
+ }
2958
+ if ("selectedAgentName" in event) {
2959
+ parts.push(`selected=${event.selectedAgentName}`);
2960
+ }
2961
+ if ("action" in event) {
2962
+ parts.push(`action=${event.action}`);
2963
+ }
2964
+ if ("approvalId" in event) {
2965
+ parts.push(`approval=${event.approvalId}`);
2966
+ }
2967
+ if ("latencyMs" in event) {
2968
+ parts.push(`${Math.round(event.latencyMs)}ms`);
2969
+ }
2970
+ if ("error" in event) {
2971
+ parts.push(`error="${event.error.message}"`);
2972
+ }
2973
+ if ("reason" in event) {
2974
+ parts.push(`reason="${event.reason}"`);
2975
+ }
2976
+ return parts.join(" ");
2977
+ }
2978
+
2979
+ // src/tracing/jsonl-sink.ts
2980
+ import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
2981
+ import { join as join2 } from "path";
2982
+ var defaultTraceDirectory = join2(".assemble", "runs");
2983
+ function resolveTraceFilePath(runId, directory = defaultTraceDirectory) {
2984
+ return join2(directory, `${runId}.jsonl`);
2985
+ }
2986
+ function createJsonlSink(options) {
2987
+ const directory = options.directory ?? defaultTraceDirectory;
2988
+ const filePath = resolveTraceFilePath(options.runId, directory);
2989
+ mkdirSync3(directory, { recursive: true });
2990
+ return {
2991
+ name: "jsonl",
2992
+ writeTraceEvent(event) {
2993
+ appendFileSync(filePath, `${JSON.stringify(event)}
2994
+ `, "utf8");
2995
+ }
2996
+ };
2997
+ }
2998
+
2999
+ // src/platform/platform-trace-sink.ts
3000
+ import {
3001
+ resolveRedactionMode as resolveRedactionMode3
3002
+ } from "@assemble-dev/shared-utils/redaction";
3003
+
3004
+ // src/platform/platform-config.ts
3005
+ import { readFileSync as readFileSync3 } from "fs";
3006
+ import { homedir } from "os";
3007
+ import { join as join3 } from "path";
3008
+ import { z as z5 } from "zod";
3009
+ var defaultPlatformBaseUrl = "https://api.assemble.dev";
3010
+ var defaultPlatformDashboardUrl = "https://app.assemble.dev";
3011
+ var PlatformEnvSchema = z5.object({
3012
+ ASSEMBLE_API_KEY: z5.string().min(1).optional(),
3013
+ ASSEMBLE_BASE_URL: z5.string().min(1).optional(),
3014
+ ASSEMBLE_DASHBOARD_URL: z5.string().min(1).optional()
3015
+ });
3016
+ var PlatformConfigFileSchema = z5.object({
3017
+ apiKey: z5.string().min(1).optional(),
3018
+ baseUrl: z5.string().min(1).optional()
3019
+ });
3020
+ var PlatformConfigSchema = z5.object({
3021
+ apiKey: z5.string().min(1).nullable(),
3022
+ baseUrl: z5.string().min(1),
3023
+ dashboardUrl: z5.string().min(1)
3024
+ });
3025
+ function resolvePlatformConfigFilePath(homeDirectory) {
3026
+ return join3(homeDirectory, ".assemble", "config.json");
3027
+ }
3028
+ function parsePlatformEnvironment(environmentVariables) {
3029
+ const parsedEnvironment = PlatformEnvSchema.safeParse(environmentVariables);
3030
+ if (parsedEnvironment.success) {
3031
+ return parsedEnvironment.data;
3032
+ }
3033
+ return {};
3034
+ }
3035
+ function isMissingConfigFileError(failure) {
3036
+ return "code" in failure && failure.code === "ENOENT";
3037
+ }
3038
+ function readPlatformConfigFile(homeDirectory) {
3039
+ const configFilePath = resolvePlatformConfigFilePath(homeDirectory);
3040
+ try {
3041
+ const rawContents = readFileSync3(configFilePath, "utf8");
3042
+ return PlatformConfigFileSchema.parse(JSON.parse(rawContents));
3043
+ } catch (caughtError) {
3044
+ const failure = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
3045
+ if (!isMissingConfigFileError(failure)) {
3046
+ console.error(
3047
+ `assemble platform config: failed to load config file at ${configFilePath}: ${failure.message}`
3048
+ );
3049
+ }
3050
+ return {};
3051
+ }
3052
+ }
3053
+ function resolvePlatformConfig(options) {
3054
+ const environment = parsePlatformEnvironment(
3055
+ options?.environmentVariables ?? process.env
3056
+ );
3057
+ const configFile = readPlatformConfigFile(
3058
+ options?.homeDirectory ?? homedir()
3059
+ );
3060
+ return PlatformConfigSchema.parse({
3061
+ apiKey: options?.apiKey ?? environment.ASSEMBLE_API_KEY ?? configFile.apiKey ?? null,
3062
+ baseUrl: options?.baseUrl ?? environment.ASSEMBLE_BASE_URL ?? configFile.baseUrl ?? defaultPlatformBaseUrl,
3063
+ dashboardUrl: options?.dashboardUrl ?? environment.ASSEMBLE_DASHBOARD_URL ?? defaultPlatformDashboardUrl
3064
+ });
3065
+ }
3066
+
3067
+ // src/platform/redact-trace-event.ts
3068
+ import {
3069
+ TraceEventErrorSchema,
3070
+ TraceEventSchema as TraceEventSchema2
3071
+ } from "@assemble-dev/shared-types/trace-events";
3072
+ import {
3073
+ redactTracePayload as redactTracePayload2,
3074
+ redactedValue,
3075
+ resolveRedactionMode as resolveRedactionMode2
3076
+ } from "@assemble-dev/shared-utils/redaction";
3077
+ function buildErrorJsonPayload(error) {
3078
+ const payload = { message: error.message };
3079
+ if (error.code !== void 0) {
3080
+ payload.code = error.code;
3081
+ }
3082
+ if (error.stack !== void 0) {
3083
+ payload.stack = error.stack;
3084
+ }
3085
+ return payload;
3086
+ }
3087
+ function redactTraceEventReason(reason, mode, sensitiveFieldNames) {
3088
+ const stringPreservingMode = mode === "metadata-only" ? "redacted" : mode;
3089
+ const outcome = redactTracePayload2(reason, {
3090
+ mode: stringPreservingMode,
3091
+ sensitiveFieldNames
3092
+ });
3093
+ if (typeof outcome.payload === "string") {
3094
+ return {
3095
+ reason: outcome.payload,
3096
+ redacted: outcome.payload !== reason
3097
+ };
3098
+ }
3099
+ return { reason: redactedValue, redacted: true };
3100
+ }
3101
+ function redactTraceEventError(error, mode, sensitiveFieldNames) {
3102
+ const outcome = redactTracePayload2(buildErrorJsonPayload(error), {
3103
+ mode,
3104
+ sensitiveFieldNames
3105
+ });
3106
+ const parsedError = TraceEventErrorSchema.safeParse(outcome.payload);
3107
+ if (parsedError.success) {
3108
+ return { error: parsedError.data, redacted: outcome.redacted };
3109
+ }
3110
+ return { error: { message: redactedValue }, redacted: true };
3111
+ }
3112
+ function redactTraceEventPayloads(event, mode, sensitiveFieldNames) {
3113
+ const resolvedMode = resolveRedactionMode2(mode);
3114
+ const redactedEvent = { ...event };
3115
+ let payloadWasRedacted = false;
3116
+ if ("input" in redactedEvent) {
3117
+ const outcome = redactTracePayload2(redactedEvent.input, {
3118
+ mode: resolvedMode,
3119
+ sensitiveFieldNames
3120
+ });
3121
+ redactedEvent.input = outcome.payload;
3122
+ payloadWasRedacted = payloadWasRedacted || outcome.redacted;
3123
+ }
3124
+ if ("output" in redactedEvent) {
3125
+ const outcome = redactTracePayload2(redactedEvent.output, {
3126
+ mode: resolvedMode,
3127
+ sensitiveFieldNames
3128
+ });
3129
+ redactedEvent.output = outcome.payload;
3130
+ payloadWasRedacted = payloadWasRedacted || outcome.redacted;
3131
+ }
3132
+ if ("reason" in redactedEvent) {
3133
+ const outcome = redactTraceEventReason(
3134
+ redactedEvent.reason,
3135
+ resolvedMode,
3136
+ sensitiveFieldNames
3137
+ );
3138
+ redactedEvent.reason = outcome.reason;
3139
+ payloadWasRedacted = payloadWasRedacted || outcome.redacted;
3140
+ }
3141
+ if ("error" in redactedEvent) {
3142
+ const outcome = redactTraceEventError(
3143
+ redactedEvent.error,
3144
+ resolvedMode,
3145
+ sensitiveFieldNames
3146
+ );
3147
+ redactedEvent.error = outcome.error;
3148
+ payloadWasRedacted = payloadWasRedacted || outcome.redacted;
3149
+ }
3150
+ return TraceEventSchema2.parse({
3151
+ ...redactedEvent,
3152
+ redacted: redactedEvent.redacted || payloadWasRedacted || resolvedMode !== "full"
3153
+ });
3154
+ }
3155
+
3156
+ // src/platform/platform-trace-sink.ts
3157
+ var defaultTraceBatchSize = 20;
3158
+ var defaultTraceQueueSize = 1e3;
3159
+ function resolveTraceIngestUrl(baseUrl) {
3160
+ return `${baseUrl}/v1/ingest/trace-events`;
3161
+ }
3162
+ function buildPlatformSinkContext(options) {
3163
+ return {
3164
+ ingestUrl: resolveTraceIngestUrl(options.baseUrl ?? defaultPlatformBaseUrl),
3165
+ apiKey: options.apiKey,
3166
+ runId: options.runId,
3167
+ workflowName: options.workflowName,
3168
+ redactionMode: resolveRedactionMode3(options.redactionMode),
3169
+ sensitiveFieldNames: options.sensitiveFieldNames,
3170
+ batchSize: options.batchSize ?? defaultTraceBatchSize,
3171
+ maxQueueSize: options.maxQueueSize ?? defaultTraceQueueSize,
3172
+ fetchImplementation: options.fetchImplementation ?? fetch,
3173
+ onUploadError: options.onUploadError,
3174
+ queuedEvents: [],
3175
+ uploadStats: {
3176
+ uploadedEventCount: 0,
3177
+ droppedEventCount: 0,
3178
+ failedBatchCount: 0
3179
+ },
3180
+ uploadPromiseChain: Promise.resolve(),
3181
+ isClosed: false
3182
+ };
3183
+ }
3184
+ function reportUploadError(context, uploadError) {
3185
+ if (context.onUploadError !== void 0) {
3186
+ context.onUploadError(uploadError);
3187
+ }
3188
+ }
3189
+ function buildTraceEventBatchRequest(context, eventsToUpload) {
3190
+ return {
3191
+ method: "POST",
3192
+ headers: {
3193
+ authorization: `Bearer ${context.apiKey}`,
3194
+ "content-type": "application/json"
3195
+ },
3196
+ body: JSON.stringify({
3197
+ runId: context.runId,
3198
+ workflowName: context.workflowName,
3199
+ events: eventsToUpload
3200
+ })
3201
+ };
3202
+ }
3203
+ async function attemptTraceEventBatchUpload(context, eventsToUpload) {
3204
+ try {
3205
+ const response = await context.fetchImplementation(
3206
+ context.ingestUrl,
3207
+ buildTraceEventBatchRequest(context, eventsToUpload)
3208
+ );
3209
+ if (response.ok) {
3210
+ return true;
3211
+ }
3212
+ reportUploadError(
3213
+ context,
3214
+ new Error(`trace event upload failed with status ${response.status}`)
3215
+ );
3216
+ return false;
3217
+ } catch (caughtError) {
3218
+ reportUploadError(
3219
+ context,
3220
+ caughtError instanceof Error ? caughtError : new Error(String(caughtError))
3221
+ );
3222
+ return false;
3223
+ }
3224
+ }
3225
+ async function uploadTraceEventBatchWithSingleRetry(context, eventsToUpload) {
3226
+ const uploadedOnFirstAttempt = await attemptTraceEventBatchUpload(
3227
+ context,
3228
+ eventsToUpload
3229
+ );
3230
+ const uploaded = uploadedOnFirstAttempt || await attemptTraceEventBatchUpload(context, eventsToUpload);
3231
+ if (uploaded) {
3232
+ context.uploadStats.uploadedEventCount += eventsToUpload.length;
3233
+ return;
3234
+ }
3235
+ context.uploadStats.failedBatchCount += 1;
3236
+ context.uploadStats.droppedEventCount += eventsToUpload.length;
3237
+ }
3238
+ function enqueueTraceEventBatchUpload(context, eventsToUpload) {
3239
+ context.uploadPromiseChain = context.uploadPromiseChain.then(async () => {
3240
+ await uploadTraceEventBatchWithSingleRetry(context, eventsToUpload);
3241
+ });
3242
+ }
3243
+ function dropOldestQueuedEventsBeyondLimit(context) {
3244
+ while (context.queuedEvents.length > context.maxQueueSize) {
3245
+ context.queuedEvents.shift();
3246
+ context.uploadStats.droppedEventCount += 1;
3247
+ }
3248
+ }
3249
+ function scheduleFullBatchUploads(context) {
3250
+ while (context.queuedEvents.length >= context.batchSize) {
3251
+ enqueueTraceEventBatchUpload(
3252
+ context,
3253
+ context.queuedEvents.splice(0, context.batchSize)
3254
+ );
3255
+ }
3256
+ }
3257
+ function writeTraceEventToPlatformQueue(context, event) {
3258
+ if (context.isClosed) {
3259
+ return;
3260
+ }
3261
+ try {
3262
+ context.queuedEvents.push(
3263
+ redactTraceEventPayloads(
3264
+ event,
3265
+ context.redactionMode,
3266
+ context.sensitiveFieldNames
3267
+ )
3268
+ );
3269
+ } catch (caughtError) {
3270
+ context.uploadStats.droppedEventCount += 1;
3271
+ reportUploadError(
3272
+ context,
3273
+ caughtError instanceof Error ? caughtError : new Error(String(caughtError))
3274
+ );
3275
+ return;
3276
+ }
3277
+ dropOldestQueuedEventsBeyondLimit(context);
3278
+ scheduleFullBatchUploads(context);
3279
+ }
3280
+ async function flushQueuedTraceEvents(context) {
3281
+ if (context.queuedEvents.length > 0) {
3282
+ enqueueTraceEventBatchUpload(
3283
+ context,
3284
+ context.queuedEvents.splice(0, context.queuedEvents.length)
3285
+ );
3286
+ }
3287
+ await context.uploadPromiseChain;
3288
+ }
3289
+ function createPlatformTraceSink(options) {
3290
+ const context = buildPlatformSinkContext(options);
3291
+ return {
3292
+ name: "platform",
3293
+ writeTraceEvent: (event) => {
3294
+ writeTraceEventToPlatformQueue(context, event);
3295
+ },
3296
+ flush: async () => {
3297
+ await flushQueuedTraceEvents(context);
3298
+ },
3299
+ close: () => {
3300
+ context.isClosed = true;
3301
+ },
3302
+ getUploadStats: () => ({ ...context.uploadStats })
3303
+ };
3304
+ }
3305
+
3306
+ // src/platform/platform-messages.ts
3307
+ var platformKeySignupUrl = "https://app.assemble.dev/app/keys";
3308
+ function buildMissingKeyMessage() {
3309
+ return `Get a free developer key to see this trace hosted \u2192 ${platformKeySignupUrl}`;
3310
+ }
3311
+ function buildHostedTraceUrl(input) {
3312
+ return `${input.dashboardUrl}/app/runs/${input.runId}`;
3313
+ }
3314
+
3315
+ // src/replay/replay-engine.ts
3316
+ import { generateRunId as generateRunId2, generateTraceEventId as generateTraceEventId5 } from "@assemble-dev/shared-utils/ids";
3317
+
3318
+ // src/replay/replay-plan.ts
3319
+ import { AppErrorCode as AppErrorCode15 } from "@assemble-dev/shared-types/errors";
3320
+ import { AppError as AppError15 } from "@assemble-dev/shared-utils/errors";
3321
+
3322
+ // src/replay/replay-mocks.ts
3323
+ import { mockModel } from "@assemble-dev/providers";
3324
+ import { AppErrorCode as AppErrorCode14 } from "@assemble-dev/shared-types/errors";
3325
+ import { AppError as AppError14 } from "@assemble-dev/shared-utils/errors";
3326
+ function buildReplayToolMocks(events) {
3327
+ const queuesByToolName = /* @__PURE__ */ new Map();
3328
+ for (const event of events) {
3329
+ if (event.type !== "tool.completed") {
3330
+ continue;
3331
+ }
3332
+ const queue = queuesByToolName.get(event.toolName) ?? {
3333
+ remaining: [],
3334
+ recordedCount: 0
3335
+ };
3336
+ queue.remaining.push(event.output);
3337
+ queue.recordedCount += 1;
3338
+ queuesByToolName.set(event.toolName, queue);
3339
+ }
3340
+ return {
3341
+ takeNextRecordedToolOutput(toolName) {
3342
+ const queue = queuesByToolName.get(toolName);
3343
+ const nextOutput = queue?.remaining.shift();
3344
+ if (nextOutput === void 0) {
3345
+ throw buildReplayTraceExhaustionError(
3346
+ "tool",
3347
+ toolName,
3348
+ "tool.completed",
3349
+ queue?.recordedCount ?? 0
3350
+ );
3351
+ }
3352
+ return nextOutput;
3353
+ }
3354
+ };
3355
+ }
3356
+ function buildReplayAgentModels(events) {
3357
+ const scriptedResponsesByAgentName = /* @__PURE__ */ new Map();
3358
+ for (const event of events) {
3359
+ if (event.type !== "agent.completed") {
3360
+ continue;
3361
+ }
3362
+ const scriptedResponses = scriptedResponsesByAgentName.get(event.agentName) ?? [];
3363
+ scriptedResponses.push(serializeRecordedAgentOutput(event.output));
3364
+ scriptedResponsesByAgentName.set(event.agentName, scriptedResponses);
3365
+ }
3366
+ const adaptersByAgentName = /* @__PURE__ */ new Map();
3367
+ return {
3368
+ resolveReplayModelForAgent(agentName) {
3369
+ const existingAdapter = adaptersByAgentName.get(agentName);
3370
+ if (existingAdapter !== void 0) {
3371
+ return existingAdapter;
3372
+ }
3373
+ const adapter = buildScriptedReplayModel(
3374
+ agentName,
3375
+ scriptedResponsesByAgentName.get(agentName) ?? []
3376
+ );
3377
+ adaptersByAgentName.set(agentName, adapter);
3378
+ return adapter;
3379
+ }
3380
+ };
3381
+ }
3382
+ function buildScriptedReplayModel(agentName, scriptedResponses) {
3383
+ const recordedCount = scriptedResponses.length;
3384
+ return mockModel({
3385
+ name: `replay:${agentName}`,
3386
+ scriptedResponses: [...scriptedResponses],
3387
+ respond: () => {
3388
+ throw buildReplayTraceExhaustionError(
3389
+ "agent",
3390
+ agentName,
3391
+ "agent.completed",
3392
+ recordedCount
3393
+ );
3394
+ }
3395
+ });
3396
+ }
3397
+ function serializeRecordedAgentOutput(output) {
3398
+ return typeof output === "string" ? output : JSON.stringify(output);
3399
+ }
3400
+ function buildReplayTraceExhaustionError(kind, name, eventType, recordedCount) {
3401
+ return new AppError14({
3402
+ code: AppErrorCode14.Conflict,
3403
+ message: `replay requested recorded output ${recordedCount + 1} for ${kind} "${name}" but the source trace only holds ${recordedCount} "${eventType}" event(s) for it; the replay diverged from the source run, and a replay may only consume outputs the source run recorded. Record a new source run that covers this call, replay from an earlier step, or disable mocking for this ${kind}`,
3404
+ statusCode: 409,
3405
+ details: { kind, name, recordedCount }
3406
+ });
3407
+ }
3408
+
3409
+ // src/replay/replay-plan.ts
3410
+ function buildReplayStartPlan(definition, sourceRunId, source, fromStepIndex) {
3411
+ if (fromStepIndex === 0) {
3412
+ return {
3413
+ startInput: validateReplayStartInput(definition, sourceRunId, source.state),
3414
+ lastAgentName: null,
3415
+ fromEventId: null,
3416
+ replayEvents: source.events,
3417
+ seedSteps: []
3418
+ };
3419
+ }
3420
+ const startedEvents = source.events.filter(
3421
+ (event) => event.type === "agent.started"
3422
+ );
3423
+ const completedEvents = source.events.filter(
3424
+ (event) => event.type === "agent.completed"
3425
+ );
3426
+ const fromStepEvent = startedEvents[fromStepIndex];
3427
+ const previousStepEvent = completedEvents[fromStepIndex - 1];
3428
+ if (fromStepEvent === void 0 || previousStepEvent === void 0) {
3429
+ throw buildFromStepOutOfRangeError(
3430
+ sourceRunId,
3431
+ fromStepIndex,
3432
+ startedEvents.length
3433
+ );
3434
+ }
3435
+ return {
3436
+ startInput: previousStepEvent.output,
3437
+ lastAgentName: previousStepEvent.agentName,
3438
+ fromEventId: fromStepEvent.id,
3439
+ replayEvents: source.events.slice(source.events.indexOf(fromStepEvent)),
3440
+ seedSteps: buildReplaySeedSteps(
3441
+ sourceRunId,
3442
+ startedEvents,
3443
+ completedEvents,
3444
+ fromStepIndex
3445
+ )
3446
+ };
3447
+ }
3448
+ function buildReplaySeedSteps(sourceRunId, startedEvents, completedEvents, fromStepIndex) {
3449
+ const seedSteps = [];
3450
+ for (let stepIndex = 0; stepIndex < fromStepIndex; stepIndex += 1) {
3451
+ const startedEvent = startedEvents[stepIndex];
3452
+ const completedEvent = completedEvents[stepIndex];
3453
+ if (startedEvent === void 0 || completedEvent === void 0) {
3454
+ throw buildFromStepOutOfRangeError(
3455
+ sourceRunId,
3456
+ fromStepIndex,
3457
+ startedEvents.length
3458
+ );
3459
+ }
3460
+ seedSteps.push({
3461
+ agentName: completedEvent.agentName,
3462
+ input: startedEvent.input,
3463
+ output: completedEvent.output
3464
+ });
3465
+ }
3466
+ return seedSteps;
3467
+ }
3468
+ function buildFromStepOutOfRangeError(sourceRunId, fromStepIndex, recordedStepCount) {
3469
+ return new AppError15({
3470
+ code: AppErrorCode15.BadRequest,
3471
+ message: `fromStepIndex ${fromStepIndex} is out of range for source run "${sourceRunId}"; the source trace recorded ${recordedStepCount} completed agent step(s), so fromStepIndex must be between 0 and ${Math.max(0, recordedStepCount - 1)}`,
3472
+ statusCode: 400,
3473
+ details: { sourceRunId, fromStepIndex, recordedStepCount }
3474
+ });
3475
+ }
3476
+ function validateReplayStartInput(definition, sourceRunId, sourceState) {
3477
+ const parsedInput = definition.inputSchema.safeParse(sourceState.input);
3478
+ if (parsedInput.success) {
3479
+ return parsedInput.data;
3480
+ }
3481
+ throw new AppError15({
3482
+ code: AppErrorCode15.ValidationFailed,
3483
+ message: `source run "${sourceRunId}" input failed workflow "${definition.name}" input schema validation: ${parsedInput.error.message}`,
3484
+ statusCode: 422,
3485
+ details: { sourceRunId, workflowName: definition.name }
3486
+ });
3487
+ }
3488
+ function buildMockedReplayDefinition(definition, replayEvents, mockTools) {
3489
+ const agentModels = buildReplayAgentModels(replayEvents);
3490
+ const toolMocks = buildReplayToolMocks(replayEvents);
3491
+ return workflow({
3492
+ name: definition.name,
3493
+ inputSchema: definition.inputSchema,
3494
+ outputSchema: definition.outputSchema ?? void 0,
3495
+ agents: definition.agents.map((agentDefinition) => ({
3496
+ ...agentDefinition,
3497
+ model: agentModels.resolveReplayModelForAgent(agentDefinition.name)
3498
+ })),
3499
+ tools: definition.tools.map(
3500
+ (toolDefinition) => mockTools ? {
3501
+ ...toolDefinition,
3502
+ execute: async () => await Promise.resolve(
3503
+ toolMocks.takeNextRecordedToolOutput(toolDefinition.name)
3504
+ )
3505
+ } : toolDefinition
3506
+ ),
3507
+ agentToolBindings: [...definition.agentToolBindings],
3508
+ topology: definition.topology,
3509
+ policies: [...definition.policies],
3510
+ memory: definition.memory ?? void 0,
3511
+ stopCondition: definition.stopCondition ?? void 0,
3512
+ continueOnAgentFailure: definition.continueOnAgentFailure
3513
+ });
3514
+ }
3515
+
3516
+ // src/replay/replay-source.ts
3517
+ import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
3518
+ import { AppErrorCode as AppErrorCode16 } from "@assemble-dev/shared-types/errors";
3519
+ import { TraceEventSchema as TraceEventSchema3 } from "@assemble-dev/shared-types/trace-events";
3520
+ import { AppError as AppError16 } from "@assemble-dev/shared-utils/errors";
3521
+ function loadReplaySource(input) {
3522
+ const runStateDirectory = input.runStateDirectory ?? defaultRunStateDirectory;
3523
+ const traceDirectory = input.traceDirectory ?? defaultTraceDirectory;
3524
+ return {
3525
+ state: loadSourceRunState(input.runId, runStateDirectory),
3526
+ events: loadSourceTraceEvents(input.runId, traceDirectory)
3527
+ };
3528
+ }
3529
+ function loadSourceRunState(runId, directory) {
3530
+ const filePath = resolveRunStateFilePath(runId, directory);
3531
+ if (!existsSync3(filePath)) {
3532
+ throw new AppError16({
3533
+ code: AppErrorCode16.NotFound,
3534
+ message: `run state file not found at "${filePath}" for run "${runId}"; the run was never persisted or the runStateDirectory is wrong`,
3535
+ statusCode: 404,
3536
+ details: { runId, filePath }
3537
+ });
3538
+ }
3539
+ return loadRunStateFile({ runId, directory });
3540
+ }
3541
+ function loadSourceTraceEvents(runId, directory) {
3542
+ const filePath = resolveTraceFilePath(runId, directory);
3543
+ if (!existsSync3(filePath)) {
3544
+ throw new AppError16({
3545
+ code: AppErrorCode16.NotFound,
3546
+ message: `trace file not found at "${filePath}" for run "${runId}"; run the workflow with a jsonl sink or fix the traceDirectory`,
3547
+ statusCode: 404,
3548
+ details: { runId, filePath }
3549
+ });
3550
+ }
3551
+ const lines = readFileSync4(filePath, "utf8").split("\n");
3552
+ const events = [];
3553
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
3554
+ const line = lines[lineIndex];
3555
+ if (line === void 0 || line.trim().length === 0) {
3556
+ continue;
3557
+ }
3558
+ events.push(parseTraceEventLine(line, lineIndex + 1, filePath));
3559
+ }
3560
+ return events;
3561
+ }
3562
+ function parseTraceEventLine(line, lineNumber, filePath) {
3563
+ try {
3564
+ return TraceEventSchema3.parse(JSON.parse(line));
3565
+ } catch (thrownError) {
3566
+ const reason = thrownError instanceof Error ? thrownError.message : String(thrownError);
3567
+ throw new AppError16({
3568
+ code: AppErrorCode16.ValidationFailed,
3569
+ message: `trace file "${filePath}" has an invalid trace event on line ${lineNumber}: ${reason}`,
3570
+ statusCode: 422,
3571
+ details: { filePath, lineNumber }
3572
+ });
3573
+ }
3574
+ }
3575
+
3576
+ // src/replay/replay-engine.ts
3577
+ async function replayRun(replayInput) {
3578
+ const settings = resolveReplaySettings(replayInput.options);
3579
+ const source = loadReplaySource({
3580
+ runId: replayInput.sourceRunId,
3581
+ runStateDirectory: settings.runStateDirectory,
3582
+ traceDirectory: settings.traceDirectory
3583
+ });
3584
+ const plan = buildReplayStartPlan(
3585
+ replayInput.definition,
3586
+ replayInput.sourceRunId,
3587
+ source,
3588
+ settings.fromStepIndex
3589
+ );
3590
+ const replayDefinition = buildMockedReplayDefinition(
3591
+ replayInput.definition,
3592
+ plan.replayEvents,
3593
+ settings.mockTools
3594
+ );
3595
+ const context = createReplayRuntimeContext(
3596
+ replayDefinition,
3597
+ source,
3598
+ settings,
3599
+ replayInput.sourceRunId,
3600
+ plan.fromEventId
3601
+ );
3602
+ await persistWorkflowRunState(context);
3603
+ await seedReplayContextFromSource(context, source, plan, settings.fromStepIndex);
3604
+ const runResult = await continueWorkflowRunFromStep(context, {
3605
+ input: plan.startInput,
3606
+ startStepIndex: settings.fromStepIndex,
3607
+ lastAgentName: plan.lastAgentName
3608
+ });
3609
+ return await finishReplayRun(context, replayInput.sourceRunId, runResult);
3610
+ }
3611
+ function resolveReplaySettings(options) {
3612
+ return {
3613
+ fromStepIndex: options?.fromStepIndex ?? 0,
3614
+ mockTools: options?.mockTools ?? true,
3615
+ sinks: options?.sinks ?? [],
3616
+ runStateDirectory: options?.runStateDirectory ?? defaultRunStateDirectory,
3617
+ traceDirectory: options?.traceDirectory ?? defaultTraceDirectory,
3618
+ now: options?.now ?? (() => /* @__PURE__ */ new Date()),
3619
+ replayRunId: options?.replayRunId ?? null
3620
+ };
3621
+ }
3622
+ function createReplayRuntimeContext(replayDefinition, source, settings, sourceRunId, fromEventId) {
3623
+ const runId = settings.replayRunId ?? generateRunId2();
3624
+ const traceBus = createTraceBus({
3625
+ runId,
3626
+ workflowName: replayDefinition.name,
3627
+ input: source.state.input,
3628
+ sinks: settings.sinks,
3629
+ now: settings.now
3630
+ });
3631
+ emitReplayStartedEvent(traceBus, runId, replayDefinition.name, settings.now, {
3632
+ sourceRunId,
3633
+ fromEventId
3634
+ });
3635
+ const runStartedEvent = emitRunStartedEvent(
3636
+ { traceBus, runId, workflowName: replayDefinition.name, now: settings.now },
3637
+ source.state.input
3638
+ );
3639
+ const memoryConfig = resolveWorkflowMemoryConfig(
3640
+ replayDefinition.memory ?? void 0
3641
+ );
3642
+ return {
3643
+ definition: replayDefinition,
3644
+ traceBus,
3645
+ runId,
3646
+ runStartedEventId: runStartedEvent.id,
3647
+ threadMemory: createThreadMemory({
3648
+ provider: memoryConfig.provider,
3649
+ threadKey: runId
3650
+ }),
3651
+ memoryProvider: memoryConfig.provider,
3652
+ artifactStore: createArtifactStore({
3653
+ provider: memoryConfig.provider,
3654
+ runId
3655
+ }),
3656
+ now: settings.now,
3657
+ startedAtMs: settings.now().getTime(),
3658
+ persistRunState: true,
3659
+ runStateDirectory: settings.runStateDirectory
3660
+ };
3661
+ }
3662
+ function emitReplayStartedEvent(traceBus, runId, workflowName, now, replayLink) {
3663
+ traceBus.emitTraceEvent({
3664
+ id: generateTraceEventId5(),
3665
+ runId,
3666
+ workflowName,
3667
+ timestamp: now().toISOString(),
3668
+ parentEventId: null,
3669
+ redacted: false,
3670
+ type: "replay.started",
3671
+ sourceRunId: replayLink.sourceRunId,
3672
+ fromEventId: replayLink.fromEventId
3673
+ });
3674
+ }
3675
+ async function seedReplayContextFromSource(context, source, plan, fromStepIndex) {
3676
+ if (fromStepIndex === 0) {
3677
+ return;
3678
+ }
3679
+ for (const artifact of source.state.artifacts) {
3680
+ const artifactStep = parseArtifactStepIndex(artifact.key);
3681
+ if (artifactStep !== null && artifactStep < fromStepIndex) {
3682
+ await context.artifactStore.saveRunArtifact(artifact);
3683
+ }
3684
+ }
3685
+ for (const seedStep of plan.seedSteps) {
3686
+ await appendAgentExchangeToThreadMemory(
3687
+ context.threadMemory,
3688
+ seedStep.agentName,
3689
+ seedStep.input,
3690
+ serializeAgentInputToText(seedStep.output),
3691
+ context.now
3692
+ );
3693
+ }
3694
+ }
3695
+ function parseArtifactStepIndex(artifactKey) {
3696
+ const match = /^step-(\d+):/.exec(artifactKey);
3697
+ const capturedStepIndex = match?.[1];
3698
+ if (capturedStepIndex === void 0) {
3699
+ return null;
3700
+ }
3701
+ return Number(capturedStepIndex);
3702
+ }
3703
+ async function finishReplayRun(context, sourceRunId, runResult) {
3704
+ context.traceBus.emitTraceEvent({
3705
+ id: generateTraceEventId5(),
3706
+ runId: context.runId,
3707
+ workflowName: context.definition.name,
3708
+ timestamp: context.now().toISOString(),
3709
+ parentEventId: context.runStartedEventId,
3710
+ redacted: false,
3711
+ type: "replay.completed",
3712
+ sourceRunId,
3713
+ latencyMs: calculateWorkflowElapsedMs(context)
3714
+ });
3715
+ await context.traceBus.flushSinks();
3716
+ return {
3717
+ status: runResult.status,
3718
+ runId: context.runId,
3719
+ sourceRunId,
3720
+ output: runResult.status === "completed" ? runResult.output : null,
3721
+ approvalId: runResult.status === "awaiting_approval" ? runResult.approvalId : null,
3722
+ state: await buildWorkflowRunState(context),
3723
+ events: context.traceBus.listTraceEvents()
3724
+ };
3725
+ }
3726
+
3727
+ // src/replay/replay-comparison.ts
3728
+ var maxReplayOutputDiffCount = 50;
3729
+ function compareRunToReplay(input) {
3730
+ const outputDiff = collectJsonLeafDiffs(
3731
+ "$",
3732
+ input.original.finalOutput,
3733
+ input.replay.finalOutput,
3734
+ []
3735
+ );
3736
+ return {
3737
+ statusMatches: input.original.status === input.replay.status,
3738
+ originalStatus: input.original.status,
3739
+ replayStatus: input.replay.status,
3740
+ outputMatches: outputDiff.length === 0,
3741
+ outputDiff,
3742
+ latency: compareRunLatency(input.original, input.replay),
3743
+ tokenUsage: {
3744
+ original: input.original.metrics.tokenUsage,
3745
+ replay: input.replay.metrics.tokenUsage
3746
+ },
3747
+ routingMatches: compareRoutingSequences(input.original, input.replay)
3748
+ };
3749
+ }
3750
+ function compareRunLatency(original, replay) {
3751
+ const originalMs = original.metrics.totalLatencyMs;
3752
+ const replayMs = replay.metrics.totalLatencyMs;
3753
+ return {
3754
+ originalMs,
3755
+ replayMs,
3756
+ deltaMs: originalMs !== null && replayMs !== null ? replayMs - originalMs : null
3757
+ };
3758
+ }
3759
+ function compareRoutingSequences(original, replay) {
3760
+ const originalSequence = original.routingDecisions.map(
3761
+ (decision) => decision.selectedAgentName
3762
+ );
3763
+ const replaySequence = replay.routingDecisions.map(
3764
+ (decision) => decision.selectedAgentName
3765
+ );
3766
+ if (originalSequence.length !== replaySequence.length) {
3767
+ return false;
3768
+ }
3769
+ return originalSequence.every(
3770
+ (selectedAgentName, index) => selectedAgentName === replaySequence[index]
3771
+ );
3772
+ }
3773
+ function collectJsonLeafDiffs(path, original, replay, diffs) {
3774
+ if (diffs.length >= maxReplayOutputDiffCount) {
3775
+ return diffs;
3776
+ }
3777
+ if (isJsonObject(original) && isJsonObject(replay)) {
3778
+ return collectJsonObjectDiffs(path, original, replay, diffs);
3779
+ }
3780
+ if (Array.isArray(original) && Array.isArray(replay)) {
3781
+ return collectJsonArrayDiffs(path, original, replay, diffs);
3782
+ }
3783
+ if (!jsonValuesEqual(original, replay)) {
3784
+ diffs.push({ path, original, replay });
3785
+ }
3786
+ return diffs;
3787
+ }
3788
+ function collectJsonObjectDiffs(path, original, replay, diffs) {
3789
+ const keys = [
3790
+ .../* @__PURE__ */ new Set([...Object.keys(original), ...Object.keys(replay)])
3791
+ ].sort();
3792
+ for (const key of keys) {
3793
+ collectJsonLeafDiffs(
3794
+ `${path}.${key}`,
3795
+ original[key] ?? null,
3796
+ replay[key] ?? null,
3797
+ diffs
3798
+ );
3799
+ }
3800
+ return diffs;
3801
+ }
3802
+ function collectJsonArrayDiffs(path, original, replay, diffs) {
3803
+ const length = Math.max(original.length, replay.length);
3804
+ for (let index = 0; index < length; index += 1) {
3805
+ collectJsonLeafDiffs(
3806
+ `${path}[${index}]`,
3807
+ original[index] ?? null,
3808
+ replay[index] ?? null,
3809
+ diffs
3810
+ );
3811
+ }
3812
+ return diffs;
3813
+ }
3814
+ function jsonValuesEqual(original, replay) {
3815
+ if (original === replay) {
3816
+ return true;
3817
+ }
3818
+ if (Array.isArray(original) && Array.isArray(replay)) {
3819
+ return original.length === replay.length && original.every(
3820
+ (item, index) => jsonValuesEqual(item, replay[index] ?? null)
3821
+ );
3822
+ }
3823
+ if (isJsonObject(original) && isJsonObject(replay)) {
3824
+ const originalKeys = Object.keys(original).sort();
3825
+ const replayKeys = Object.keys(replay).sort();
3826
+ return originalKeys.length === replayKeys.length && originalKeys.every(
3827
+ (key, index) => key === replayKeys[index] && jsonValuesEqual(original[key] ?? null, replay[key] ?? null)
3828
+ );
3829
+ }
3830
+ return false;
3831
+ }
3832
+ function isJsonObject(value) {
3833
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3834
+ }
3835
+ export {
3836
+ agent,
3837
+ applyTraceEventToRunState,
3838
+ buildHostedTraceUrl,
3839
+ buildMissingKeyMessage,
3840
+ buildPipelineTeamWorkflowConfig,
3841
+ buildReviewPanelWorkflowConfig,
3842
+ buildSupervisorTeamWorkflowConfig,
3843
+ compareRunToReplay,
3844
+ createArtifactStore,
3845
+ createConsoleSink,
3846
+ createInMemoryMemoryProvider,
3847
+ createInitialRunState,
3848
+ createJsonFileMemoryProvider,
3849
+ createJsonlSink,
3850
+ createPlatformTraceSink,
3851
+ createThreadMemory,
3852
+ createTraceBus,
3853
+ defaultParallelMergedArtifactKey,
3854
+ defaultRunStateDirectory,
3855
+ defaultSupervisorMaxSteps,
3856
+ defaultTraceDirectory,
3857
+ defaultWorkflowTopology,
3858
+ evaluateToolPolicies,
3859
+ executeToolCall,
3860
+ finishWorkflow,
3861
+ formatTraceEventLine,
3862
+ loadReplaySource,
3863
+ loadRunStateFile,
3864
+ maxReplayOutputDiffCount,
3865
+ policy,
3866
+ replayRun,
3867
+ resolvePlatformConfig,
3868
+ resolveRunStateFilePath,
3869
+ resolveTraceFilePath,
3870
+ resolveWorkflowMemoryConfig,
3871
+ resumeRun,
3872
+ runAgent,
3873
+ runWorkflow,
3874
+ team,
3875
+ tool,
3876
+ workflow
3877
+ };