@cuylabs/agent-runtime-dapr 4.8.1 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -118,6 +118,20 @@ tool still executes through `agent.getHost()`.
118
118
 
119
119
  For the full explanation, see [Tool Hosts In Durable Workflows](docs/tool-hosts.md).
120
120
 
121
+ ## Durable Context Compaction
122
+
123
+ Durable turns compact at the same model-step boundary as direct `Agent.chat()`,
124
+ but they do it through workflow state instead of the in-process chat loop.
125
+ Before a `model-step` activity runs, the workflow can call a
126
+ `context-compaction` activity. That activity applies the agent's normal
127
+ compaction policy to the serialized workflow messages, persists the compaction
128
+ entry to session storage, returns the compacted message snapshots, and the
129
+ workflow checkpoints `context-compaction-finish` before continuing.
130
+
131
+ This keeps the durable path crash-safe without importing direct-loop internals:
132
+ direct execution uses `ChatModelStepSnapshot`; Dapr durable execution uses
133
+ `AgentWorkflowTurnState.messages` and `AgentWorkflowModelStepPlan`.
134
+
121
135
  ## Quick Start
122
136
 
123
137
  ### Step 1: Define your agent
@@ -191,12 +191,38 @@ var DaprWorkflowClient = class {
191
191
  }
192
192
  };
193
193
 
194
+ // src/workflow/activity-names.ts
195
+ var DEFAULT_AGENT_TURN_WORKFLOW_NAME = "agent-turn";
196
+ function createAgentTurnActivityNames(workflowName, overrides = {}, options = {}) {
197
+ return {
198
+ ...options.includeSteerDrain || overrides.steerDrain ? {
199
+ steerDrain: overrides.steerDrain ?? `${workflowName}:steer-drain`
200
+ } : {},
201
+ ...options.includeContextCompaction || overrides.contextCompaction ? {
202
+ contextCompaction: overrides.contextCompaction ?? `${workflowName}:context-compaction`
203
+ } : {},
204
+ modelStep: overrides.modelStep ?? `${workflowName}:model-step`,
205
+ ...options.includeHumanInputCheck || overrides.humanInputCheck ? {
206
+ humanInputCheck: overrides.humanInputCheck ?? `${workflowName}:human-input-check`
207
+ } : {},
208
+ ...options.includeApprovalCheck || overrides.approvalCheck ? {
209
+ approvalCheck: overrides.approvalCheck ?? `${workflowName}:approval-check`
210
+ } : {},
211
+ toolCall: overrides.toolCall ?? `${workflowName}:tool-call`,
212
+ stepCommit: overrides.stepCommit ?? `${workflowName}:step-commit`,
213
+ outputCommit: overrides.outputCommit ?? `${workflowName}:output-commit`
214
+ };
215
+ }
216
+
194
217
  // src/workflow/runtime-activities.ts
195
218
  import {
196
219
  advanceAgentTurnState,
220
+ assembleModelContext,
197
221
  convertAgentMessagesToModelMessages,
198
222
  createAgentTurnEngine,
199
223
  createAgentTurnStepCommitBatch,
224
+ ContextWindowManager,
225
+ DEFAULT_CONTEXT_LIMITS,
200
226
  executeAgentToolCall,
201
227
  normalizeToolReplayPolicy,
202
228
  prepareModelStep,
@@ -224,14 +250,29 @@ function resolveOption(value) {
224
250
  async function* emptyCommitApplier() {
225
251
  yield* [];
226
252
  }
253
+ function createRuntimeContextWindow(runtime) {
254
+ if (!runtime.contextWindow) return void 0;
255
+ return new ContextWindowManager({
256
+ limits: {
257
+ contextWindow: runtime.contextWindow,
258
+ reserveTokens: runtime.reserveTokens ?? DEFAULT_CONTEXT_LIMITS.reserveTokens
259
+ }
260
+ });
261
+ }
227
262
  function createDaprAgentTurnSteerDrainActivity(options) {
228
263
  return {
229
264
  name: "steer-drain",
230
265
  handler: async (_context, input) => options.drain(input)
231
266
  };
232
267
  }
233
- async function withObserverContext(bridge, sessionId, fn) {
234
- const parentCtx = bridge?.getOtelContext(sessionId);
268
+ function createDaprAgentTurnContextCompactionActivity(options) {
269
+ return {
270
+ name: "context-compaction",
271
+ handler: async (_context, input) => await options.compact(input)
272
+ };
273
+ }
274
+ async function withObserverContext(bridge, sessionId, turnId, fn) {
275
+ const parentCtx = bridge?.getOtelContext(sessionId, turnId);
235
276
  if (!parentCtx) return fn();
236
277
  try {
237
278
  const otelApi = await import("@opentelemetry/api");
@@ -257,7 +298,8 @@ function createDaprAgentTurnModelStepActivity(options) {
257
298
  if (middleware?.hasMiddleware) {
258
299
  await middleware.runChatStart(
259
300
  plan.sessionId,
260
- `[workflow model-step ${plan.step}]`
301
+ `[workflow model-step ${plan.step}]`,
302
+ { sessionId: plan.sessionId, turnId: plan.turnId }
261
303
  );
262
304
  }
263
305
  let stepResult;
@@ -268,6 +310,22 @@ function createDaprAgentTurnModelStepActivity(options) {
268
310
  plan.systemPrompts
269
311
  ) : plan.systemPrompts;
270
312
  const restoredMessages = restoreAgentWorkflowMessages(plan.messages);
313
+ const resolvedMcpTools = typeof options.mcpTools === "function" ? await options.mcpTools() : options.mcpTools;
314
+ const abort = options.createAbortSignal?.() ?? new AbortController().signal;
315
+ const contextWindow = createRuntimeContextWindow(runtime);
316
+ const assembledContext = await assembleModelContext({
317
+ sessionId: plan.sessionId,
318
+ turnId: plan.turnId,
319
+ step: plan.step,
320
+ cwd: runtime.cwd,
321
+ messages: restoredMessages,
322
+ toolNames: Object.keys(tools),
323
+ mcpToolNames: Object.keys(resolvedMcpTools ?? {}),
324
+ abort,
325
+ ...middleware ? { contextFragmentCollector: middleware } : {},
326
+ ...contextWindow ? { window: contextWindow } : {},
327
+ ...plan.contextCompaction ? { compaction: plan.contextCompaction } : {}
328
+ });
271
329
  const turnEngine = createAgentTurnEngine({
272
330
  sessionId: plan.sessionId,
273
331
  startedAt: now(),
@@ -275,13 +333,15 @@ function createDaprAgentTurnModelStepActivity(options) {
275
333
  });
276
334
  const preparedStep = prepareModelStep({
277
335
  sessionId: plan.sessionId,
336
+ turnId: plan.turnId,
278
337
  step: plan.step,
279
338
  systemPrompts,
280
- messages: restoredMessages,
339
+ messages: assembledContext.messages,
340
+ contextReport: assembledContext.report,
281
341
  toModelMessages: options.toModelMessages ?? convertAgentMessagesToModelMessages,
282
- abort: options.createAbortSignal?.() ?? new AbortController().signal,
342
+ abort,
283
343
  tools,
284
- mcpTools: typeof options.mcpTools === "function" ? await options.mcpTools() : options.mcpTools,
344
+ mcpTools: resolvedMcpTools,
285
345
  config: runtime,
286
346
  ...host ? { host } : {},
287
347
  ...options.turnTracker ? { turnTracker: options.turnTracker } : {},
@@ -302,6 +362,7 @@ function createDaprAgentTurnModelStepActivity(options) {
302
362
  text: item.value.text,
303
363
  ...item.value.usage ? { usage: item.value.usage } : {},
304
364
  ...item.value.finishReason ? { finishReason: item.value.finishReason } : {},
365
+ contextReport: assembledContext.report,
305
366
  turnState: turnEngine.getState(),
306
367
  ...stepCommit ? { stepCommit } : {}
307
368
  };
@@ -315,11 +376,15 @@ function createDaprAgentTurnModelStepActivity(options) {
315
376
  stepError = normalizeError(err);
316
377
  }
317
378
  if (middleware?.hasMiddleware) {
318
- await middleware.runChatEnd(plan.sessionId, {
319
- usage: stepResult?.usage,
320
- error: stepError,
321
- output: stepResult?.text
322
- });
379
+ await middleware.runChatEnd(
380
+ plan.sessionId,
381
+ {
382
+ usage: stepResult?.usage,
383
+ error: stepError,
384
+ output: stepResult?.text
385
+ },
386
+ { sessionId: plan.sessionId, turnId: plan.turnId }
387
+ );
323
388
  }
324
389
  if (stepError) throw stepError;
325
390
  return stepResult;
@@ -327,6 +392,7 @@ function createDaprAgentTurnModelStepActivity(options) {
327
392
  return withObserverContext(
328
393
  options.observerBridge,
329
394
  plan.sessionId,
395
+ plan.turnId,
330
396
  runActivity
331
397
  );
332
398
  }
@@ -442,6 +508,7 @@ function createDaprAgentTurnToolCallActivity(options) {
442
508
  return withObserverContext(
443
509
  options.observerBridge,
444
510
  plan.sessionId,
511
+ void 0,
445
512
  runActivity
446
513
  );
447
514
  }
@@ -503,37 +570,18 @@ function createDaprAgentTurnOutputCommitActivity(options) {
503
570
 
504
571
  // src/workflow/runtime.ts
505
572
  import {
506
- advanceAgentTurnState as advanceAgentTurnState2,
507
573
  applyWorkflowInterventions,
508
574
  applyAgentWorkflowCommitResult,
575
+ applyAgentWorkflowContextCompactionResult,
509
576
  applyAgentWorkflowModelStepResult,
510
577
  applyAgentWorkflowToolCallResult,
511
- createApprovalCorrection,
512
578
  failAgentWorkflowTurnState,
513
- formatApprovalDeniedReason,
514
- planNextAgentWorkflowOperation,
515
- recordAgentWorkflowReplayDecision
579
+ formatApprovalDeniedReason as formatApprovalDeniedReason2,
580
+ planNextAgentWorkflowOperation
516
581
  } from "@cuylabs/agent-core";
517
- var MAX_CONTROL_EVENTS_PER_BOUNDARY = 32;
518
- var DEFAULT_WORKFLOW_NAME = "agent-turn";
519
- function defaultActivityNames(workflowName, overrides = {}, options = {}) {
520
- return {
521
- ...options.includeSteerDrain || overrides.steerDrain ? {
522
- steerDrain: overrides.steerDrain ?? `${workflowName}:steer-drain`
523
- } : {},
524
- modelStep: overrides.modelStep ?? `${workflowName}:model-step`,
525
- ...options.includeHumanInputCheck || overrides.humanInputCheck ? {
526
- humanInputCheck: overrides.humanInputCheck ?? `${workflowName}:human-input-check`
527
- } : {},
528
- ...options.includeApprovalCheck || overrides.approvalCheck ? {
529
- approvalCheck: overrides.approvalCheck ?? `${workflowName}:approval-check`
530
- } : {},
531
- toolCall: overrides.toolCall ?? `${workflowName}:tool-call`,
532
- stepCommit: overrides.stepCommit ?? `${workflowName}:step-commit`,
533
- outputCommit: overrides.outputCommit ?? `${workflowName}:output-commit`
534
- };
535
- }
536
- function normalizeError2(error) {
582
+
583
+ // src/workflow/runtime-helpers.ts
584
+ function normalizeWorkflowError(error) {
537
585
  if (error instanceof Error) return error;
538
586
  if (typeof error === "string") return new Error(error);
539
587
  if (error && typeof error === "object") {
@@ -556,12 +604,45 @@ function renameActivity(activity, name) {
556
604
  function summarizeWorkflowState(state) {
557
605
  return JSON.stringify({
558
606
  sessionId: state.sessionId,
607
+ turnId: state.turnId,
559
608
  phase: state.phase,
560
609
  step: state.step,
561
610
  finalResponse: state.finalResponse,
562
611
  error: state.error
563
612
  });
564
613
  }
614
+
615
+ // src/workflow/steering.ts
616
+ var MAX_CONTROL_EVENTS_PER_BOUNDARY = 32;
617
+ function normalizeSteerSnapshots(payload) {
618
+ if (!Array.isArray(payload)) {
619
+ return [];
620
+ }
621
+ return payload.flatMap((candidate) => {
622
+ if (!candidate || typeof candidate !== "object") {
623
+ return [];
624
+ }
625
+ const steer = candidate;
626
+ if (typeof steer.id !== "string" || !steer.id.trim() || typeof steer.message !== "string" || !steer.message.trim() || typeof steer.createdAt !== "string" || !steer.createdAt.trim()) {
627
+ return [];
628
+ }
629
+ return [
630
+ {
631
+ id: steer.id.trim(),
632
+ message: steer.message.trim(),
633
+ createdAt: steer.createdAt.trim()
634
+ }
635
+ ];
636
+ });
637
+ }
638
+
639
+ // src/workflow/tool-gates.ts
640
+ import {
641
+ advanceAgentTurnState as advanceAgentTurnState2,
642
+ createApprovalCorrection,
643
+ formatApprovalDeniedReason,
644
+ recordAgentWorkflowReplayDecision
645
+ } from "@cuylabs/agent-core";
565
646
  function ensureReplayDecision(state, plan, now) {
566
647
  if (plan.replayDecision) {
567
648
  return { state, decision: plan.replayDecision };
@@ -689,27 +770,6 @@ function createHumanInputToolCallResult(plan, response, updatedAt) {
689
770
  )
690
771
  };
691
772
  }
692
- function normalizeSteerSnapshots(payload) {
693
- if (!Array.isArray(payload)) {
694
- return [];
695
- }
696
- return payload.flatMap((candidate) => {
697
- if (!candidate || typeof candidate !== "object") {
698
- return [];
699
- }
700
- const steer = candidate;
701
- if (typeof steer.id !== "string" || !steer.id.trim() || typeof steer.message !== "string" || !steer.message.trim() || typeof steer.createdAt !== "string" || !steer.createdAt.trim()) {
702
- return [];
703
- }
704
- return [
705
- {
706
- id: steer.id.trim(),
707
- message: steer.message.trim(),
708
- createdAt: steer.createdAt.trim()
709
- }
710
- ];
711
- });
712
- }
713
773
  var EXTERNAL_EVENT_TIMEOUT = /* @__PURE__ */ Symbol("EXTERNAL_EVENT_TIMEOUT");
714
774
  function* waitForExternalEventWithTimeout(context, eventName, timeoutMs) {
715
775
  if (!context.waitForExternalEvent) {
@@ -730,13 +790,16 @@ function* waitForExternalEventWithTimeout(context, eventName, timeoutMs) {
730
790
  }
731
791
  return yield eventTask;
732
792
  }
793
+
794
+ // src/workflow/runtime.ts
733
795
  function createDaprAgentTurnWorkflowDefinition(options) {
734
- const workflowName = options.workflowName ?? DEFAULT_WORKFLOW_NAME;
735
- const activityNames = defaultActivityNames(
796
+ const workflowName = options.workflowName ?? DEFAULT_AGENT_TURN_WORKFLOW_NAME;
797
+ const activityNames = createAgentTurnActivityNames(
736
798
  workflowName,
737
799
  options.activityNames,
738
800
  {
739
801
  includeSteerDrain: Boolean(options.steerDrain),
802
+ includeContextCompaction: Boolean(options.contextCompaction),
740
803
  includeHumanInputCheck: Boolean(options.humanInputCheck),
741
804
  includeApprovalCheck: Boolean(options.approvalCheck)
742
805
  }
@@ -779,6 +842,44 @@ function createDaprAgentTurnWorkflowDefinition(options) {
779
842
  }
780
843
  }
781
844
  }
845
+ if (state.phase === "model-step" && activityNames.contextCompaction && options.contextCompaction) {
846
+ const compaction = yield context.callActivity(
847
+ activityNames.contextCompaction,
848
+ {
849
+ workflowInstanceId: context.getWorkflowInstanceId?.() ?? "",
850
+ sessionId: state.sessionId,
851
+ step: state.step,
852
+ messages: state.messages.map(
853
+ (message) => structuredClone(message)
854
+ )
855
+ }
856
+ );
857
+ state = applyAgentWorkflowContextCompactionResult(
858
+ state,
859
+ compaction,
860
+ now()
861
+ );
862
+ if (compaction.compacted && !context.isReplaying?.()) {
863
+ await options.onEvent?.(state.sessionId, {
864
+ type: "context-compaction",
865
+ phase: "pre-step",
866
+ inputTokens: compaction.inputTokens,
867
+ limit: compaction.limit,
868
+ decision: compaction.decision,
869
+ effectiveness: compaction.effectiveness,
870
+ removedCount: compaction.removedCount,
871
+ tokensRemoved: compaction.tokensRemoved,
872
+ summarized: compaction.summarized,
873
+ ...compaction.cutReason ? { cutReason: compaction.cutReason } : {}
874
+ });
875
+ await options.onEvent?.(state.sessionId, {
876
+ type: "context-overflow",
877
+ inputTokens: compaction.inputTokens,
878
+ limit: compaction.limit
879
+ });
880
+ await bridge?.notifyCheckpoint("context-compaction-finish", state);
881
+ }
882
+ }
782
883
  const plan = planNextAgentWorkflowOperation(state);
783
884
  if (!plan) {
784
885
  if (!context.isReplaying?.()) {
@@ -947,7 +1048,7 @@ function createDaprAgentTurnWorkflowDefinition(options) {
947
1048
  state,
948
1049
  createDeniedToolCallResult(
949
1050
  plan,
950
- formatApprovalDeniedReason(
1051
+ formatApprovalDeniedReason2(
951
1052
  plan.toolCall.toolName,
952
1053
  decision.feedback
953
1054
  ),
@@ -1133,7 +1234,7 @@ function createDaprAgentTurnWorkflowDefinition(options) {
1133
1234
  state,
1134
1235
  createDeniedToolCallResult(
1135
1236
  singlePlan,
1136
- formatApprovalDeniedReason(
1237
+ formatApprovalDeniedReason2(
1137
1238
  singlePlan.toolCall.toolName,
1138
1239
  decision.feedback
1139
1240
  ),
@@ -1234,7 +1335,7 @@ function createDaprAgentTurnWorkflowDefinition(options) {
1234
1335
  }
1235
1336
  }
1236
1337
  } catch (error) {
1237
- const normalized = normalizeError2(error);
1338
+ const normalized = normalizeWorkflowError(error);
1238
1339
  state = failAgentWorkflowTurnState(state, normalized, now());
1239
1340
  context.setCustomStatus?.(summarizeWorkflowState(state));
1240
1341
  if (!context.isReplaying?.()) {
@@ -1252,6 +1353,9 @@ function createDaprAgentTurnWorkflowDefinition(options) {
1252
1353
  function createDaprAgentTurnWorkflowKit(options) {
1253
1354
  const getBridge = () => options.observerBridge;
1254
1355
  const steerDrainActivity = options.steerDrainActivity ? createDaprAgentTurnSteerDrainActivity(options.steerDrainActivity) : void 0;
1356
+ const contextCompactionActivity = options.contextCompactionActivity ? createDaprAgentTurnContextCompactionActivity(
1357
+ options.contextCompactionActivity
1358
+ ) : void 0;
1255
1359
  const humanInputCheckActivity = options.humanInputCheckActivity ? createDaprAgentTurnHumanInputCheckActivity(
1256
1360
  options.humanInputCheckActivity
1257
1361
  ) : void 0;
@@ -1286,6 +1390,9 @@ function createDaprAgentTurnWorkflowKit(options) {
1286
1390
  ...steerDrainActivity ? {
1287
1391
  steerDrain: (input) => steerDrainActivity.handler(void 0, input)
1288
1392
  } : {},
1393
+ ...contextCompactionActivity ? {
1394
+ contextCompaction: (input) => contextCompactionActivity.handler(void 0, input)
1395
+ } : {},
1289
1396
  modelStep: (input) => modelStepActivity.handler(void 0, input),
1290
1397
  ...humanInputCheckActivity ? {
1291
1398
  humanInputCheck: (input) => humanInputCheckActivity.handler(void 0, input)
@@ -1307,6 +1414,12 @@ function createDaprAgentTurnWorkflowKit(options) {
1307
1414
  registration.activityNames.steerDrain
1308
1415
  )
1309
1416
  } : {},
1417
+ ...contextCompactionActivity && registration.activityNames.contextCompaction ? {
1418
+ contextCompaction: renameActivity(
1419
+ contextCompactionActivity,
1420
+ registration.activityNames.contextCompaction
1421
+ )
1422
+ } : {},
1310
1423
  modelStep: renameActivity(
1311
1424
  modelStepActivity,
1312
1425
  registration.activityNames.modelStep
@@ -1343,6 +1456,8 @@ export {
1343
1456
  isTerminalDaprWorkflowStatus,
1344
1457
  hasStartedDaprWorkflow,
1345
1458
  DaprWorkflowClient,
1459
+ DEFAULT_AGENT_TURN_WORKFLOW_NAME,
1460
+ createAgentTurnActivityNames,
1346
1461
  createDaprAgentTurnSteerDrainActivity,
1347
1462
  createDaprAgentTurnModelStepActivity,
1348
1463
  createDaprAgentTurnToolCallActivity,