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