Skip to content

Commit e2a3309

Browse files
committed
feat(agent): record the origin that entered dynamic workflow mode
Record the origin of the prompt that entered Dynamic Workflow mode, so a fan-out started by a scheduled job or hook is attributable in the session records. The RPC triggers run between turns and record no origin.
1 parent 17967df commit e2a3309

5 files changed

Lines changed: 32 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pythoughts/pythinker-code": patch
3+
---
4+
5+
Record the origin of the prompt that entered Dynamic Workflow mode, so a fan-out started by a scheduled job or hook is attributable in the session records.

packages/agent-core/src/agent/dynamic-workflow/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@ export class DynamicWorkflowMode {
1818

1919
enter(trigger: DynamicWorkflowModeTrigger): void {
2020
if (this.active !== null) return;
21-
this.agent.records.logRecord({ type: 'dynamic_workflow_mode.enter', trigger });
21+
this.agent.records.logRecord({
22+
type: 'dynamic_workflow_mode.enter',
23+
trigger,
24+
// The 'tool' trigger always fires inside a turn, so the origin names
25+
// what drove the model to fan out (user, cron, hook, ...). The RPC
26+
// triggers run between turns and record no origin.
27+
origin: this.agent.turn.activeTurnOrigin,
28+
});
2229
this.active = trigger;
2330
if (trigger !== 'tool') {
2431
const sizeNote = workflowSizeGuidelineNote(

packages/agent-core/src/agent/records/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ export interface AgentRecordEvents {
5454

5555
'dynamic_workflow_mode.enter': {
5656
trigger: DynamicWorkflowModeTrigger;
57+
/**
58+
* Origin of the prompt whose turn entered the mode, so a cron- or
59+
* hook-originated fan-out is attributable after the fact. Absent when the
60+
* mode was entered outside a turn (e.g. the explicit RPC toggle).
61+
*/
62+
origin?: PromptOrigin;
5763
};
5864
'dynamic_workflow_mode.exit': {};
5965

packages/agent-core/src/agent/turn/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ interface ActiveTurn {
6262
readonly controller: AbortController;
6363
readonly promise: Promise<TurnEndResult>;
6464
readonly firstRequest: ControlledPromise<void>;
65+
/** Origin of the prompt that launched this turn, for consumers that need to know who is driving (e.g. the dynamic_workflow_mode.enter record). */
66+
readonly origin: PromptOrigin;
6567
}
6668

6769
interface BufferedSteer {
@@ -229,6 +231,7 @@ export class TurnFlow {
229231
controller,
230232
promise,
231233
firstRequest,
234+
origin,
232235
};
233236

234237
void firstRequest.catch(() => undefined);
@@ -297,6 +300,13 @@ export class TurnFlow {
297300
return this.activeTurn !== null && this.activeTurn !== 'resuming';
298301
}
299302

303+
/** Origin of the prompt that launched the active turn; undefined between turns and while resuming. */
304+
get activeTurnOrigin(): PromptOrigin | undefined {
305+
return this.activeTurn === null || this.activeTurn === 'resuming'
306+
? undefined
307+
: this.activeTurn.origin;
308+
}
309+
300310
private ensureActiveTurn(): ActiveTurn {
301311
if (this.activeTurn === null || this.activeTurn === 'resuming') {
302312
throw new Error('No active turn');

packages/agent-core/test/agent/turn.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,9 @@ describe('Agent turn flow', () => {
533533
.filter((origin) => origin?.kind === 'injection');
534534

535535
expect(runQueued).toHaveBeenCalledTimes(1);
536-
expect(enterEvent?.args).toMatchObject({ trigger: 'tool' });
536+
// The enter record names who drove the fan-out: a cron- or hook-originated
537+
// turn calling DynamicWorkflow is attributable after the fact.
538+
expect(enterEvent?.args).toMatchObject({ trigger: 'tool', origin: { kind: 'user' } });
537539
expect(ctx.agent.dynamicWorkflowMode.isActive).toBe(false);
538540
expect(eventIndex(ctx, '[wire]', 'dynamic_workflow_mode.exit')).toBeGreaterThan(
539541
eventIndex(ctx, '[rpc]', 'turn.ended'),

0 commit comments

Comments
 (0)