@studio-foundation/engine 0.3.0-beta.1 → 0.3.0-beta.6
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/__tests__/engine.conditions-group.test.d.ts +2 -0
- package/dist/__tests__/engine.conditions-group.test.d.ts.map +1 -0
- package/dist/__tests__/engine.conditions-group.test.js +222 -0
- package/dist/__tests__/engine.conditions-group.test.js.map +1 -0
- package/dist/__tests__/engine.conditions.test.d.ts +2 -0
- package/dist/__tests__/engine.conditions.test.d.ts.map +1 -0
- package/dist/__tests__/engine.conditions.test.js +164 -0
- package/dist/__tests__/engine.conditions.test.js.map +1 -0
- package/dist/__tests__/engine.resume.test.d.ts +2 -0
- package/dist/__tests__/engine.resume.test.d.ts.map +1 -0
- package/dist/__tests__/engine.resume.test.js +93 -0
- package/dist/__tests__/engine.resume.test.js.map +1 -0
- package/dist/__tests__/engine.script-stage.test.d.ts +2 -0
- package/dist/__tests__/engine.script-stage.test.d.ts.map +1 -0
- package/dist/__tests__/engine.script-stage.test.js +108 -0
- package/dist/__tests__/engine.script-stage.test.js.map +1 -0
- package/dist/db/client.d.ts +3 -0
- package/dist/db/client.d.ts.map +1 -0
- package/{src/db/client.ts → dist/db/client.js} +1 -2
- package/dist/db/client.js.map +1 -0
- package/dist/engine.context-event.test.d.ts +2 -0
- package/dist/engine.context-event.test.d.ts.map +1 -0
- package/dist/engine.context-event.test.js +111 -0
- package/dist/engine.context-event.test.js.map +1 -0
- package/dist/engine.d.ts +52 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +364 -0
- package/dist/engine.js.map +1 -0
- package/dist/events.d.ts +163 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +12 -0
- package/dist/events.js.map +1 -0
- package/{src/index.ts → dist/index.d.ts} +3 -45
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline/agent-loader.d.ts +4 -0
- package/dist/pipeline/agent-loader.d.ts.map +1 -0
- package/dist/pipeline/agent-loader.js +28 -0
- package/dist/pipeline/agent-loader.js.map +1 -0
- package/dist/pipeline/agent-loader.test.d.ts +2 -0
- package/dist/pipeline/agent-loader.test.d.ts.map +1 -0
- package/dist/pipeline/agent-loader.test.js +134 -0
- package/dist/pipeline/agent-loader.test.js.map +1 -0
- package/dist/pipeline/condition-evaluator.d.ts +6 -0
- package/dist/pipeline/condition-evaluator.d.ts.map +1 -0
- package/dist/pipeline/condition-evaluator.js +105 -0
- package/dist/pipeline/condition-evaluator.js.map +1 -0
- package/dist/pipeline/condition-evaluator.test.d.ts +2 -0
- package/dist/pipeline/condition-evaluator.test.d.ts.map +1 -0
- package/dist/pipeline/condition-evaluator.test.js +103 -0
- package/dist/pipeline/condition-evaluator.test.js.map +1 -0
- package/dist/pipeline/context-pack-loader.d.ts +3 -0
- package/dist/pipeline/context-pack-loader.d.ts.map +1 -0
- package/dist/pipeline/context-pack-loader.js +51 -0
- package/dist/pipeline/context-pack-loader.js.map +1 -0
- package/dist/pipeline/context-propagation.d.ts +28 -0
- package/dist/pipeline/context-propagation.d.ts.map +1 -0
- package/dist/pipeline/context-propagation.js +161 -0
- package/dist/pipeline/context-propagation.js.map +1 -0
- package/dist/pipeline/context-propagation.test.d.ts +2 -0
- package/dist/pipeline/context-propagation.test.d.ts.map +1 -0
- package/dist/pipeline/context-propagation.test.js +194 -0
- package/dist/pipeline/context-propagation.test.js.map +1 -0
- package/dist/pipeline/contract-loader.d.ts +4 -0
- package/dist/pipeline/contract-loader.d.ts.map +1 -0
- package/dist/pipeline/contract-loader.js +30 -0
- package/dist/pipeline/contract-loader.js.map +1 -0
- package/dist/pipeline/group-orchestrator.d.ts +19 -0
- package/dist/pipeline/group-orchestrator.d.ts.map +1 -0
- package/dist/pipeline/group-orchestrator.js +376 -0
- package/dist/pipeline/group-orchestrator.js.map +1 -0
- package/dist/pipeline/hook-executor.d.ts +28 -0
- package/dist/pipeline/hook-executor.d.ts.map +1 -0
- package/dist/pipeline/hook-executor.js +63 -0
- package/dist/pipeline/hook-executor.js.map +1 -0
- package/dist/pipeline/hook-executor.test.d.ts +2 -0
- package/dist/pipeline/hook-executor.test.d.ts.map +1 -0
- package/dist/pipeline/hook-executor.test.js +71 -0
- package/dist/pipeline/hook-executor.test.js.map +1 -0
- package/dist/pipeline/invariants-loader.d.ts +6 -0
- package/dist/pipeline/invariants-loader.d.ts.map +1 -0
- package/dist/pipeline/invariants-loader.js +15 -0
- package/dist/pipeline/invariants-loader.js.map +1 -0
- package/dist/pipeline/invariants-loader.test.d.ts +2 -0
- package/dist/pipeline/invariants-loader.test.d.ts.map +1 -0
- package/dist/pipeline/invariants-loader.test.js +39 -0
- package/dist/pipeline/invariants-loader.test.js.map +1 -0
- package/dist/pipeline/loader.d.ts +5 -0
- package/dist/pipeline/loader.d.ts.map +1 -0
- package/dist/pipeline/loader.js +132 -0
- package/dist/pipeline/loader.js.map +1 -0
- package/dist/pipeline/loader.test.d.ts +2 -0
- package/dist/pipeline/loader.test.d.ts.map +1 -0
- package/dist/pipeline/loader.test.js +120 -0
- package/dist/pipeline/loader.test.js.map +1 -0
- package/dist/pipeline/output-validator.d.ts +10 -0
- package/dist/pipeline/output-validator.d.ts.map +1 -0
- package/dist/pipeline/output-validator.js +20 -0
- package/dist/pipeline/output-validator.js.map +1 -0
- package/dist/pipeline/output-validator.test.d.ts +2 -0
- package/dist/pipeline/output-validator.test.d.ts.map +1 -0
- package/dist/pipeline/output-validator.test.js +77 -0
- package/dist/pipeline/output-validator.test.js.map +1 -0
- package/dist/pipeline/post-validator.d.ts +8 -0
- package/dist/pipeline/post-validator.d.ts.map +1 -0
- package/dist/pipeline/post-validator.js +103 -0
- package/dist/pipeline/post-validator.js.map +1 -0
- package/dist/pipeline/skill-loader.d.ts +10 -0
- package/dist/pipeline/skill-loader.d.ts.map +1 -0
- package/dist/pipeline/skill-loader.js +26 -0
- package/dist/pipeline/skill-loader.js.map +1 -0
- package/dist/pipeline/skill-loader.test.d.ts +2 -0
- package/dist/pipeline/skill-loader.test.d.ts.map +1 -0
- package/dist/pipeline/skill-loader.test.js +38 -0
- package/dist/pipeline/skill-loader.test.js.map +1 -0
- package/dist/pipeline/stage-executor.d.ts +26 -0
- package/dist/pipeline/stage-executor.d.ts.map +1 -0
- package/dist/pipeline/stage-executor.js +540 -0
- package/dist/pipeline/stage-executor.js.map +1 -0
- package/dist/pipeline/stage-resolver.d.ts +3 -0
- package/dist/pipeline/stage-resolver.d.ts.map +1 -0
- package/dist/pipeline/stage-resolver.js +6 -0
- package/dist/pipeline/stage-resolver.js.map +1 -0
- package/dist/pipeline/startup-executor.d.ts +3 -0
- package/dist/pipeline/startup-executor.d.ts.map +1 -0
- package/dist/pipeline/startup-executor.js +23 -0
- package/dist/pipeline/startup-executor.js.map +1 -0
- package/dist/pipeline/startup-executor.test.d.ts +2 -0
- package/dist/pipeline/startup-executor.test.d.ts.map +1 -0
- package/dist/pipeline/startup-executor.test.js +34 -0
- package/dist/pipeline/startup-executor.test.js.map +1 -0
- package/dist/pipeline/types.d.ts +28 -0
- package/dist/pipeline/types.d.ts.map +1 -0
- package/dist/pipeline/types.js +12 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/repo-resolver.d.ts +10 -0
- package/dist/repo-resolver.d.ts.map +1 -0
- package/dist/repo-resolver.js +39 -0
- package/dist/repo-resolver.js.map +1 -0
- package/dist/spawners/direct-engine-spawner.d.ts +8 -0
- package/dist/spawners/direct-engine-spawner.d.ts.map +1 -0
- package/dist/spawners/direct-engine-spawner.js +24 -0
- package/dist/spawners/direct-engine-spawner.js.map +1 -0
- package/dist/state/run-store.d.ts +74 -0
- package/dist/state/run-store.d.ts.map +1 -0
- package/dist/state/run-store.js +269 -0
- package/dist/state/run-store.js.map +1 -0
- package/dist/state/run-store.test.d.ts +2 -0
- package/dist/state/run-store.test.d.ts.map +1 -0
- package/dist/state/run-store.test.js +130 -0
- package/dist/state/run-store.test.js.map +1 -0
- package/dist/state/state-machine.d.ts +6 -0
- package/dist/state/state-machine.d.ts.map +1 -0
- package/dist/state/state-machine.js +27 -0
- package/dist/state/state-machine.js.map +1 -0
- package/dist/state/status-derivation.d.ts +12 -0
- package/dist/state/status-derivation.d.ts.map +1 -0
- package/dist/state/status-derivation.js +30 -0
- package/dist/state/status-derivation.js.map +1 -0
- package/package.json +7 -4
- package/ARCHITECTURE.md +0 -57
- package/src/__tests__/__fixtures__/script-stage/contracts/book-context.contract.yaml +0 -4
- package/src/__tests__/engine.conditions-group.test.ts +0 -244
- package/src/__tests__/engine.conditions.test.ts +0 -186
- package/src/__tests__/engine.resume.test.ts +0 -108
- package/src/__tests__/engine.script-stage.test.ts +0 -125
- package/src/engine.context-event.test.ts +0 -175
- package/src/engine.ts +0 -491
- package/src/events.ts +0 -167
- package/src/pipeline/agent-loader.test.ts +0 -151
- package/src/pipeline/agent-loader.ts +0 -39
- package/src/pipeline/condition-evaluator.test.ts +0 -129
- package/src/pipeline/condition-evaluator.ts +0 -121
- package/src/pipeline/context-pack-loader.ts +0 -63
- package/src/pipeline/context-propagation.test.ts +0 -237
- package/src/pipeline/context-propagation.ts +0 -235
- package/src/pipeline/contract-loader.ts +0 -41
- package/src/pipeline/group-orchestrator.ts +0 -483
- package/src/pipeline/hook-executor.test.ts +0 -121
- package/src/pipeline/hook-executor.ts +0 -87
- package/src/pipeline/invariants-loader.test.ts +0 -51
- package/src/pipeline/invariants-loader.ts +0 -14
- package/src/pipeline/loader.test.ts +0 -128
- package/src/pipeline/loader.ts +0 -149
- package/src/pipeline/output-validator.test.ts +0 -100
- package/src/pipeline/output-validator.ts +0 -40
- package/src/pipeline/post-validator.ts +0 -124
- package/src/pipeline/skill-loader.test.ts +0 -44
- package/src/pipeline/skill-loader.ts +0 -32
- package/src/pipeline/stage-executor.ts +0 -654
- package/src/pipeline/stage-resolver.ts +0 -8
- package/src/pipeline/startup-executor.test.ts +0 -37
- package/src/pipeline/startup-executor.ts +0 -32
- package/src/pipeline/types.ts +0 -42
- package/src/repo-resolver.ts +0 -61
- package/src/spawners/direct-engine-spawner.ts +0 -26
- package/src/state/run-store.test.ts +0 -157
- package/src/state/run-store.ts +0 -362
- package/src/state/state-machine.ts +0 -35
- package/src/state/status-derivation.ts +0 -36
- package/tests/context-pack-loader.test.ts +0 -113
- package/tests/context-propagation.test.ts +0 -267
- package/tests/direct-engine-spawner.test.ts +0 -102
- package/tests/e2e/feature-v5.test.ts +0 -57
- package/tests/events.test.ts +0 -56
- package/tests/fixtures/agents/test-agent.agent.yaml +0 -5
- package/tests/fixtures/contracts/code-gen.contract.yaml +0 -6
- package/tests/fixtures/contracts/qa-gate.contract.yaml +0 -15
- package/tests/fixtures/contracts/test-contract.contract.yaml +0 -6
- package/tests/fixtures/pipelines/group-test.pipeline.yaml +0 -40
- package/tests/fixtures/pipelines/simple.pipeline.yaml +0 -15
- package/tests/fixtures/pipelines/two-stage.pipeline.yaml +0 -24
- package/tests/fixtures/software/pipelines/feature-builder.pipeline.yaml +0 -75
- package/tests/fixtures/test-project/agents/test-agent.agent.yaml +0 -5
- package/tests/fixtures/test-project/contracts/basic-result.contract.yaml +0 -6
- package/tests/fixtures/test-project/contracts/code-gen.contract.yaml +0 -6
- package/tests/fixtures/test-project/contracts/maximum-only.contract.yaml +0 -8
- package/tests/fixtures/test-project/contracts/qa-gate.contract.yaml +0 -15
- package/tests/fixtures/test-project/contracts/strict-result.contract.yaml +0 -7
- package/tests/fixtures/test-project/contracts/test-contract.contract.yaml +0 -6
- package/tests/fixtures/test-project/pipelines/group-simple.pipeline.yaml +0 -26
- package/tests/fixtures/test-project/pipelines/group-test.pipeline.yaml +0 -40
- package/tests/fixtures/test-project/pipelines/hook-output-template.pipeline.yaml +0 -19
- package/tests/fixtures/test-project/pipelines/hook-reject-on-failure.pipeline.yaml +0 -19
- package/tests/fixtures/test-project/pipelines/maximum-only.pipeline.yaml +0 -15
- package/tests/fixtures/test-project/pipelines/parallel-collect-all-test.pipeline.yaml +0 -39
- package/tests/fixtures/test-project/pipelines/parallel-context-isolation-test.pipeline.yaml +0 -38
- package/tests/fixtures/test-project/pipelines/parallel-fail-test.pipeline.yaml +0 -39
- package/tests/fixtures/test-project/pipelines/parallel-test.pipeline.yaml +0 -39
- package/tests/fixtures/test-project/pipelines/parallel-then-sequential-test.pipeline.yaml +0 -38
- package/tests/fixtures/test-project/pipelines/simple.pipeline.yaml +0 -15
- package/tests/fixtures/test-project/pipelines/two-stage.pipeline.yaml +0 -24
- package/tests/fixtures/test-project/pipelines/with-startup.pipeline.yaml +0 -19
- package/tests/loader.test.ts +0 -385
- package/tests/post-validator.test.ts +0 -297
- package/tests/repo-resolver.test.ts +0 -102
- package/tests/run-store.test.ts +0 -143
- package/tests/state-machine.test.ts +0 -110
- package/tests/unit/context-propagation.test.ts +0 -45
- package/tests/unit/engine.test.ts +0 -770
- package/tests/unit/group-loop.test.ts +0 -389
- package/tests/unit/group-parallel.test.ts +0 -423
- package/tests/unit/state/status-derivation.test.ts +0 -88
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -14
package/src/engine.ts
DELETED
|
@@ -1,491 +0,0 @@
|
|
|
1
|
-
// Main pipeline engine — the orchestrator
|
|
2
|
-
// Connects ralph (retry loop) + runner (agent execution) + contracts (validation)
|
|
3
|
-
|
|
4
|
-
import { randomUUID } from 'node:crypto';
|
|
5
|
-
import type {
|
|
6
|
-
PipelineEntry,
|
|
7
|
-
PipelineRun,
|
|
8
|
-
PipelineDefinition,
|
|
9
|
-
RunSpawner,
|
|
10
|
-
StageRun,
|
|
11
|
-
ToolCall,
|
|
12
|
-
} from '@studio-foundation/contracts';
|
|
13
|
-
import { isStageGroup } from '@studio-foundation/contracts';
|
|
14
|
-
import {
|
|
15
|
-
type ToolRegistry,
|
|
16
|
-
type ProviderRegistry,
|
|
17
|
-
AnonymizationMiddleware,
|
|
18
|
-
createStudioRunTool,
|
|
19
|
-
STUDIO_RUN_PROMPT_SNIPPET,
|
|
20
|
-
} from '@studio-foundation/runner';
|
|
21
|
-
import { loadPipelineByName } from './pipeline/loader.js';
|
|
22
|
-
import { executeStartupCommands } from './pipeline/startup-executor.js';
|
|
23
|
-
import { loadInvariantsFile } from './pipeline/invariants-loader.js';
|
|
24
|
-
import {
|
|
25
|
-
createInitialContext,
|
|
26
|
-
addStageOutput,
|
|
27
|
-
addStageToolResults,
|
|
28
|
-
clearGroupFeedback,
|
|
29
|
-
} from './pipeline/context-propagation.js';
|
|
30
|
-
import type { AnyRunStore } from './state/run-store.js';
|
|
31
|
-
import type { EngineEvents } from './events.js';
|
|
32
|
-
import { PipelineEventEmitter } from './events.js';
|
|
33
|
-
import { resolveProjectPaths } from './pipeline/types.js';
|
|
34
|
-
import { StageExecutor } from './pipeline/stage-executor.js';
|
|
35
|
-
import { GroupOrchestrator } from './pipeline/group-orchestrator.js';
|
|
36
|
-
|
|
37
|
-
export interface EngineConfig {
|
|
38
|
-
configsDir: string;
|
|
39
|
-
repoPath?: string;
|
|
40
|
-
providerRegistry: ProviderRegistry;
|
|
41
|
-
toolRegistry?: ToolRegistry; // optional — not needed for script-only pipelines
|
|
42
|
-
db?: AnyRunStore;
|
|
43
|
-
providerOverride?: string;
|
|
44
|
-
/**
|
|
45
|
-
* Skills content from active plugins, keyed by plugin name.
|
|
46
|
-
* Each entry is an array of formatted markdown strings to inject
|
|
47
|
-
* into the system prompt of agents that declare the plugin.
|
|
48
|
-
*/
|
|
49
|
-
pluginSkills?: Record<string, string[]>;
|
|
50
|
-
spawner?: RunSpawner; // if set, studio-run tool is available to agents
|
|
51
|
-
maxDepth?: number; // max nesting depth for spawned runs, default 3
|
|
52
|
-
defaultProvider?: string; // fallback when agent YAML omits provider
|
|
53
|
-
defaultModel?: string; // fallback when agent YAML omits model
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface RunInput {
|
|
57
|
-
id?: string; // ← pre-generated run ID (e.g. from the API)
|
|
58
|
-
pipeline?: string; // pipeline name (loads from YAML file)
|
|
59
|
-
pipelineDef?: PipelineDefinition; // inline pipeline definition (skips YAML loading — for tests/programmatic use)
|
|
60
|
-
input?: string | Record<string, unknown>;
|
|
61
|
-
userInput?: string | Record<string, unknown>; // alias for input (used in tests and programmatic API)
|
|
62
|
-
meta?: Record<string, unknown>;
|
|
63
|
-
anonymize?: boolean;
|
|
64
|
-
signal?: AbortSignal;
|
|
65
|
-
depth?: number; // nesting depth (0 = top-level)
|
|
66
|
-
parentRunId?: string; // parent run ID if spawned by another pipeline
|
|
67
|
-
// Resume fields — used by `studio replay --restart`
|
|
68
|
-
resumeFromStage?: string;
|
|
69
|
-
priorStageOutputs?: Map<string, unknown>;
|
|
70
|
-
priorStageToolResults?: Map<string, ToolCall[]>;
|
|
71
|
-
originalRunId?: string;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Collect all leaf stage names in pipeline order (group containers are transparent).
|
|
76
|
-
*/
|
|
77
|
-
function collectLeafStageNames(entries: PipelineEntry[]): string[] {
|
|
78
|
-
const names: string[] = [];
|
|
79
|
-
for (const entry of entries) {
|
|
80
|
-
if (isStageGroup(entry)) {
|
|
81
|
-
for (const stage of entry.stages) names.push(stage.name);
|
|
82
|
-
} else {
|
|
83
|
-
names.push(entry.name);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return names;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Returns the set of stage names that should be skipped (all leaf stages before resumeFromStage).
|
|
91
|
-
* Throws if resumeFromStage is not found in the pipeline.
|
|
92
|
-
*/
|
|
93
|
-
function buildSkipSet(entries: PipelineEntry[], resumeFromStage: string): Set<string> {
|
|
94
|
-
const leafNames = collectLeafStageNames(entries);
|
|
95
|
-
const targetIndex = leafNames.indexOf(resumeFromStage);
|
|
96
|
-
if (targetIndex < 0) {
|
|
97
|
-
throw new Error(`Stage "${resumeFromStage}" not found in pipeline`);
|
|
98
|
-
}
|
|
99
|
-
return new Set(leafNames.slice(0, targetIndex));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function countTotalStages(entries: PipelineEntry[]): number {
|
|
103
|
-
let count = 0;
|
|
104
|
-
for (const entry of entries) {
|
|
105
|
-
if (isStageGroup(entry)) {
|
|
106
|
-
count += entry.stages.length;
|
|
107
|
-
} else {
|
|
108
|
-
count++;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return count;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Create a StageRun for a skipped stage (used during resume operations).
|
|
116
|
-
* Uses randomUUID() to ensure unique IDs across multiple resume runs.
|
|
117
|
-
*/
|
|
118
|
-
function makeSkippedStageRun(stageName: string, reason: string): StageRun {
|
|
119
|
-
const now = new Date().toISOString();
|
|
120
|
-
return {
|
|
121
|
-
id: randomUUID(),
|
|
122
|
-
stage_name: stageName,
|
|
123
|
-
status: 'skipped',
|
|
124
|
-
started_at: now,
|
|
125
|
-
completed_at: now,
|
|
126
|
-
tasks: [],
|
|
127
|
-
skipped_reason: reason,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export class PipelineEngine {
|
|
132
|
-
private emitter: PipelineEventEmitter;
|
|
133
|
-
private pipelineTotals = { tokens: 0, toolCalls: 0 };
|
|
134
|
-
private stageExecutor: StageExecutor;
|
|
135
|
-
private groupOrchestrator: GroupOrchestrator;
|
|
136
|
-
|
|
137
|
-
constructor(
|
|
138
|
-
private config: EngineConfig,
|
|
139
|
-
private events?: EngineEvents,
|
|
140
|
-
) {
|
|
141
|
-
this.emitter = new PipelineEventEmitter();
|
|
142
|
-
this.stageExecutor = new StageExecutor({
|
|
143
|
-
events,
|
|
144
|
-
emitter: this.emitter,
|
|
145
|
-
providerRegistry: config.providerRegistry,
|
|
146
|
-
repoPath: config.repoPath,
|
|
147
|
-
configsDir: config.configsDir,
|
|
148
|
-
pluginSkills: config.pluginSkills,
|
|
149
|
-
providerOverride: config.providerOverride,
|
|
150
|
-
defaultProvider: config.defaultProvider,
|
|
151
|
-
defaultModel: config.defaultModel,
|
|
152
|
-
});
|
|
153
|
-
this.groupOrchestrator = new GroupOrchestrator({
|
|
154
|
-
events,
|
|
155
|
-
emitter: this.emitter,
|
|
156
|
-
stageExecutor: this.stageExecutor,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async run(input: RunInput): Promise<PipelineRun> {
|
|
161
|
-
const signal = input.signal;
|
|
162
|
-
|
|
163
|
-
// Resolve the effective user input (support both 'input' and 'userInput' aliases)
|
|
164
|
-
const userInputValue: string | Record<string, unknown> = input.userInput ?? input.input ?? '';
|
|
165
|
-
|
|
166
|
-
if (!input.pipeline && !input.pipelineDef) {
|
|
167
|
-
throw new Error('RunInput must provide either "pipeline" (pipeline name) or "pipelineDef" (inline definition)');
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// 1. Resolve paths — configsDir is now the project root directly
|
|
171
|
-
const projectPaths = resolveProjectPaths(this.config.configsDir);
|
|
172
|
-
|
|
173
|
-
// 2. Load the pipeline — either from an inline definition or a YAML file
|
|
174
|
-
const pipeline: PipelineDefinition = input.pipelineDef
|
|
175
|
-
? input.pipelineDef
|
|
176
|
-
: await loadPipelineByName(input.pipeline!, projectPaths.pipelinesDir);
|
|
177
|
-
|
|
178
|
-
// 2. Create the PipelineRun
|
|
179
|
-
const pipelineRun: PipelineRun = {
|
|
180
|
-
id: input.id ?? randomUUID(),
|
|
181
|
-
pipeline_name: pipeline.name,
|
|
182
|
-
status: 'running',
|
|
183
|
-
started_at: new Date().toISOString(),
|
|
184
|
-
stages: [],
|
|
185
|
-
...(typeof userInputValue === 'object' && userInputValue !== null
|
|
186
|
-
? { input: userInputValue as Record<string, unknown> }
|
|
187
|
-
: {}),
|
|
188
|
-
...(input.parentRunId ? { parent_run_id: input.parentRunId } : {}),
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
// Reset totals for this run
|
|
192
|
-
this.pipelineTotals = { tokens: 0, toolCalls: 0 };
|
|
193
|
-
const pipelineStartTime = Date.now();
|
|
194
|
-
|
|
195
|
-
// Create anonymization middleware for this run if requested via RunInput flag
|
|
196
|
-
const runAnonymize = input.anonymize === true;
|
|
197
|
-
const runMiddleware = runAnonymize ? new AnonymizationMiddleware() : null;
|
|
198
|
-
|
|
199
|
-
// Persist the run immediately so log_path can be written before terminal states
|
|
200
|
-
await this.config.db?.savePipelineRun(pipelineRun);
|
|
201
|
-
|
|
202
|
-
// Build per-run tool registry: clone the shared registry and inject studio-run
|
|
203
|
-
// with run-specific context (run ID, depth) if a spawner is configured.
|
|
204
|
-
// For script-only pipelines, toolRegistry may be undefined.
|
|
205
|
-
const runToolRegistry = this.config.spawner && this.config.toolRegistry
|
|
206
|
-
? (() => {
|
|
207
|
-
const registry = this.config.toolRegistry.clone();
|
|
208
|
-
registry.registerPlugin(
|
|
209
|
-
'studio_run',
|
|
210
|
-
createStudioRunTool({
|
|
211
|
-
spawner: this.config.spawner!,
|
|
212
|
-
currentRunId: pipelineRun.id,
|
|
213
|
-
currentDepth: input.depth ?? 0,
|
|
214
|
-
maxDepth: this.config.maxDepth ?? 3,
|
|
215
|
-
}),
|
|
216
|
-
STUDIO_RUN_PROMPT_SNIPPET
|
|
217
|
-
);
|
|
218
|
-
return registry;
|
|
219
|
-
})()
|
|
220
|
-
: this.config.toolRegistry;
|
|
221
|
-
|
|
222
|
-
this.events?.onPipelineStart?.({
|
|
223
|
-
pipeline_name: pipeline.name,
|
|
224
|
-
run_id: pipelineRun.id,
|
|
225
|
-
});
|
|
226
|
-
this.emitter.emit({ type: 'pipeline_start', pipelineId: pipelineRun.id });
|
|
227
|
-
|
|
228
|
-
// 3. Initialize context
|
|
229
|
-
const pipelineContext = createInitialContext(userInputValue, this.config.repoPath);
|
|
230
|
-
|
|
231
|
-
// Run on_pipeline_start commands to bootstrap dynamic context
|
|
232
|
-
if (pipeline.on_pipeline_start?.length) {
|
|
233
|
-
const cwd = this.config.repoPath ?? this.config.configsDir;
|
|
234
|
-
pipelineContext.startupContext = await executeStartupCommands(
|
|
235
|
-
pipeline.on_pipeline_start,
|
|
236
|
-
cwd
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Load .studio/invariants.md if present — injected into every agent's system_prompt
|
|
241
|
-
pipelineContext.invariantsContent = await loadInvariantsFile(projectPaths.projectDir);
|
|
242
|
-
|
|
243
|
-
// Pre-populate context from prior run if resuming
|
|
244
|
-
const skipSet: Set<string> =
|
|
245
|
-
input.resumeFromStage
|
|
246
|
-
? buildSkipSet(pipeline.stages, input.resumeFromStage)
|
|
247
|
-
: new Set();
|
|
248
|
-
|
|
249
|
-
if (input.resumeFromStage) {
|
|
250
|
-
for (const [stageName, output] of input.priorStageOutputs ?? []) {
|
|
251
|
-
addStageOutput(pipelineContext, stageName, output);
|
|
252
|
-
}
|
|
253
|
-
for (const [stageName, toolCalls] of input.priorStageToolResults ?? []) {
|
|
254
|
-
addStageToolResults(pipelineContext, stageName, toolCalls);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// 4. Execute stages sequentially (handling groups)
|
|
259
|
-
const totalStages = countTotalStages(pipeline.stages);
|
|
260
|
-
let stageCounter = 0;
|
|
261
|
-
let previousStageName: string | undefined;
|
|
262
|
-
|
|
263
|
-
for (const entry of pipeline.stages) {
|
|
264
|
-
// Check for cancellation before each pipeline entry
|
|
265
|
-
if (signal?.aborted) {
|
|
266
|
-
pipelineRun.status = 'cancelled';
|
|
267
|
-
pipelineRun.completed_at = new Date().toISOString();
|
|
268
|
-
const lastStage = pipelineRun.stages[pipelineRun.stages.length - 1];
|
|
269
|
-
this.events?.onPipelineCancelled?.({
|
|
270
|
-
run_id: pipelineRun.id,
|
|
271
|
-
cancelled_at_stage: lastStage?.stage_name ?? 'before_first_stage',
|
|
272
|
-
duration_ms: Date.now() - pipelineStartTime,
|
|
273
|
-
});
|
|
274
|
-
await this.config.db?.savePipelineRun(pipelineRun);
|
|
275
|
-
this.events?.onPipelineComplete?.({
|
|
276
|
-
pipeline_name: pipeline.name,
|
|
277
|
-
run_id: pipelineRun.id,
|
|
278
|
-
status: 'cancelled',
|
|
279
|
-
duration_ms: Date.now() - pipelineStartTime,
|
|
280
|
-
total_tokens: this.pipelineTotals.tokens,
|
|
281
|
-
total_tool_calls: this.pipelineTotals.toolCalls,
|
|
282
|
-
});
|
|
283
|
-
this.emitter.emit({ type: 'pipeline_complete', pipelineId: pipelineRun.id });
|
|
284
|
-
return pipelineRun;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (isStageGroup(entry)) {
|
|
288
|
-
// ========== GROUP ==========
|
|
289
|
-
|
|
290
|
-
// Check if all group stages should be skipped
|
|
291
|
-
const allGroupStagesSkipped = entry.stages.every(s => skipSet.has(s.name));
|
|
292
|
-
|
|
293
|
-
if (allGroupStagesSkipped) {
|
|
294
|
-
const groupSkipReason = input.originalRunId
|
|
295
|
-
? `resumed from run ${input.originalRunId}`
|
|
296
|
-
: 'resumed from prior run';
|
|
297
|
-
for (const stage of entry.stages) {
|
|
298
|
-
stageCounter++;
|
|
299
|
-
const skippedRun = makeSkippedStageRun(stage.name, groupSkipReason);
|
|
300
|
-
this.events?.onStageComplete?.({
|
|
301
|
-
stage_name: stage.name,
|
|
302
|
-
stage_index: stageCounter - 1,
|
|
303
|
-
total_stages: totalStages,
|
|
304
|
-
status: 'skipped',
|
|
305
|
-
attempts: 0,
|
|
306
|
-
duration_ms: 0,
|
|
307
|
-
skipped_reason: groupSkipReason,
|
|
308
|
-
});
|
|
309
|
-
pipelineRun.stages.push(skippedRun);
|
|
310
|
-
previousStageName = stage.name;
|
|
311
|
-
}
|
|
312
|
-
clearGroupFeedback(pipelineContext);
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const groupResult = await this.groupOrchestrator.run(
|
|
317
|
-
entry,
|
|
318
|
-
pipelineContext,
|
|
319
|
-
stageCounter,
|
|
320
|
-
totalStages,
|
|
321
|
-
userInputValue,
|
|
322
|
-
projectPaths,
|
|
323
|
-
runToolRegistry,
|
|
324
|
-
runMiddleware,
|
|
325
|
-
pipelineRun.id,
|
|
326
|
-
signal,
|
|
327
|
-
skipSet,
|
|
328
|
-
input.originalRunId,
|
|
329
|
-
);
|
|
330
|
-
this.pipelineTotals.tokens += groupResult.totalTokensDelta;
|
|
331
|
-
this.pipelineTotals.toolCalls += groupResult.totalToolCallsDelta;
|
|
332
|
-
|
|
333
|
-
pipelineRun.stages.push(...groupResult.stageRuns);
|
|
334
|
-
stageCounter += groupResult.stagesExecuted;
|
|
335
|
-
// Update previousStageName to the last stage in the group
|
|
336
|
-
if (entry.stages.length > 0) {
|
|
337
|
-
previousStageName = entry.stages[entry.stages.length - 1].name;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Clear group feedback after group completes
|
|
341
|
-
clearGroupFeedback(pipelineContext);
|
|
342
|
-
|
|
343
|
-
if (groupResult.status === 'rejected' || groupResult.status === 'failed' || groupResult.status === 'cancelled') {
|
|
344
|
-
pipelineRun.status = groupResult.status;
|
|
345
|
-
pipelineRun.completed_at = new Date().toISOString();
|
|
346
|
-
if (groupResult.status === 'cancelled') {
|
|
347
|
-
const lastStage = pipelineRun.stages[pipelineRun.stages.length - 1];
|
|
348
|
-
this.events?.onPipelineCancelled?.({
|
|
349
|
-
run_id: pipelineRun.id,
|
|
350
|
-
cancelled_at_stage: lastStage?.stage_name ?? 'unknown',
|
|
351
|
-
duration_ms: Date.now() - pipelineStartTime,
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
await this.config.db?.savePipelineRun(pipelineRun);
|
|
355
|
-
if (runMiddleware) {
|
|
356
|
-
await this.persistKeymap(pipelineRun.id, runMiddleware.getKeymap());
|
|
357
|
-
}
|
|
358
|
-
this.events?.onPipelineComplete?.({
|
|
359
|
-
pipeline_name: pipeline.name,
|
|
360
|
-
run_id: pipelineRun.id,
|
|
361
|
-
status: pipelineRun.status,
|
|
362
|
-
duration_ms: Date.now() - pipelineStartTime,
|
|
363
|
-
total_tokens: this.pipelineTotals.tokens,
|
|
364
|
-
total_tool_calls: this.pipelineTotals.toolCalls,
|
|
365
|
-
});
|
|
366
|
-
this.emitter.emit({ type: 'pipeline_complete', pipelineId: pipelineRun.id });
|
|
367
|
-
return pipelineRun;
|
|
368
|
-
}
|
|
369
|
-
} else {
|
|
370
|
-
// ========== SIMPLE STAGE ==========
|
|
371
|
-
stageCounter++;
|
|
372
|
-
|
|
373
|
-
// Skip stage if resuming from a later stage
|
|
374
|
-
if (skipSet.has(entry.name)) {
|
|
375
|
-
const skippedReason = input.originalRunId
|
|
376
|
-
? `resumed from run ${input.originalRunId}`
|
|
377
|
-
: 'resumed from prior run';
|
|
378
|
-
const skippedRun = makeSkippedStageRun(entry.name, skippedReason);
|
|
379
|
-
this.events?.onStageComplete?.({
|
|
380
|
-
stage_name: entry.name,
|
|
381
|
-
stage_index: stageCounter - 1,
|
|
382
|
-
total_stages: totalStages,
|
|
383
|
-
status: 'skipped',
|
|
384
|
-
attempts: 0,
|
|
385
|
-
duration_ms: 0,
|
|
386
|
-
skipped_reason: skippedReason,
|
|
387
|
-
});
|
|
388
|
-
pipelineRun.stages.push(skippedRun);
|
|
389
|
-
previousStageName = entry.name;
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const result = await this.stageExecutor.execute(
|
|
394
|
-
entry,
|
|
395
|
-
pipelineContext,
|
|
396
|
-
previousStageName,
|
|
397
|
-
userInputValue,
|
|
398
|
-
stageCounter - 1,
|
|
399
|
-
totalStages,
|
|
400
|
-
projectPaths,
|
|
401
|
-
runToolRegistry,
|
|
402
|
-
runMiddleware,
|
|
403
|
-
pipelineRun.id,
|
|
404
|
-
signal,
|
|
405
|
-
);
|
|
406
|
-
this.pipelineTotals.tokens += result.tokensDelta ?? 0;
|
|
407
|
-
this.pipelineTotals.toolCalls += result.toolCallsDelta ?? 0;
|
|
408
|
-
|
|
409
|
-
pipelineRun.stages.push(result.stageRun);
|
|
410
|
-
|
|
411
|
-
if (result.status === 'failed' || result.status === 'rejected' || result.status === 'cancelled') {
|
|
412
|
-
pipelineRun.status = result.stageRun.status;
|
|
413
|
-
pipelineRun.completed_at = new Date().toISOString();
|
|
414
|
-
if (result.status === 'cancelled') {
|
|
415
|
-
this.events?.onPipelineCancelled?.({
|
|
416
|
-
run_id: pipelineRun.id,
|
|
417
|
-
cancelled_at_stage: result.stageRun.stage_name,
|
|
418
|
-
duration_ms: Date.now() - pipelineStartTime,
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
await this.config.db?.savePipelineRun(pipelineRun);
|
|
422
|
-
if (runMiddleware) {
|
|
423
|
-
await this.persistKeymap(pipelineRun.id, runMiddleware.getKeymap());
|
|
424
|
-
}
|
|
425
|
-
this.events?.onPipelineComplete?.({
|
|
426
|
-
pipeline_name: pipeline.name,
|
|
427
|
-
run_id: pipelineRun.id,
|
|
428
|
-
status: pipelineRun.status,
|
|
429
|
-
duration_ms: Date.now() - pipelineStartTime,
|
|
430
|
-
total_tokens: this.pipelineTotals.tokens,
|
|
431
|
-
total_tool_calls: this.pipelineTotals.toolCalls,
|
|
432
|
-
});
|
|
433
|
-
this.emitter.emit({ type: 'pipeline_complete', pipelineId: pipelineRun.id });
|
|
434
|
-
return pipelineRun;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (result.lastAgentOutput !== undefined) {
|
|
438
|
-
addStageOutput(pipelineContext, entry.name, result.lastAgentOutput);
|
|
439
|
-
}
|
|
440
|
-
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
441
|
-
addStageToolResults(pipelineContext, entry.name, result.toolCalls);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
previousStageName = entry.name;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// 5. All stages succeeded
|
|
449
|
-
pipelineRun.status = 'success';
|
|
450
|
-
pipelineRun.completed_at = new Date().toISOString();
|
|
451
|
-
|
|
452
|
-
// 6. Persist then emit (DB must be updated before SSE fires, so spawnAndWait GET sees final status)
|
|
453
|
-
await this.config.db?.savePipelineRun(pipelineRun);
|
|
454
|
-
if (runMiddleware) {
|
|
455
|
-
await this.persistKeymap(pipelineRun.id, runMiddleware.getKeymap());
|
|
456
|
-
}
|
|
457
|
-
this.events?.onPipelineComplete?.({
|
|
458
|
-
pipeline_name: pipeline.name,
|
|
459
|
-
run_id: pipelineRun.id,
|
|
460
|
-
status: pipelineRun.status,
|
|
461
|
-
duration_ms: Date.now() - pipelineStartTime,
|
|
462
|
-
total_tokens: this.pipelineTotals.tokens,
|
|
463
|
-
total_tool_calls: this.pipelineTotals.toolCalls,
|
|
464
|
-
});
|
|
465
|
-
this.emitter.emit({ type: 'pipeline_complete', pipelineId: pipelineRun.id });
|
|
466
|
-
|
|
467
|
-
return pipelineRun;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/** For external listeners via the generic event bus */
|
|
471
|
-
onEvent(listener: (event: import('./events.js').PipelineEvent) => void): void {
|
|
472
|
-
this.emitter.on(listener);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// -- Private --
|
|
476
|
-
|
|
477
|
-
private async persistKeymap(runId: string, keymap: Record<string, string>): Promise<void> {
|
|
478
|
-
if (Object.keys(keymap).length === 0) return;
|
|
479
|
-
try {
|
|
480
|
-
const { mkdir, writeFile } = await import('node:fs/promises');
|
|
481
|
-
const { join } = await import('node:path');
|
|
482
|
-
// configsDir is .studio/ directly — keymap goes in .studio/runs/anonymization/
|
|
483
|
-
const anonDir = join(this.config.configsDir, 'runs', 'anonymization');
|
|
484
|
-
await mkdir(anonDir, { recursive: true });
|
|
485
|
-
const keymapPath = join(anonDir, `${runId}.keymap.json`);
|
|
486
|
-
await writeFile(keymapPath, JSON.stringify(keymap, null, 2), 'utf-8');
|
|
487
|
-
} catch {
|
|
488
|
-
// Non-fatal — keymap persistence is best-effort
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
package/src/events.ts
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import type { ToolCall, ToolCallStartEvent, ToolCallCompleteEvent, AgentThinkingEvent, AgentProgressEvent, AgentTokenEvent } from '@studio-foundation/contracts';
|
|
2
|
-
|
|
3
|
-
// Event types for pipeline observability
|
|
4
|
-
// Dedicated event types — separate from contract types (PipelineRun, StageRun)
|
|
5
|
-
|
|
6
|
-
export interface ToolCallSummary {
|
|
7
|
-
name: string;
|
|
8
|
-
arguments_summary: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface TokenUsage {
|
|
12
|
-
prompt_tokens: number;
|
|
13
|
-
completion_tokens: number;
|
|
14
|
-
total_tokens: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface PipelineStartEvent {
|
|
18
|
-
pipeline_name: string;
|
|
19
|
-
run_id: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface PipelineCompleteEvent {
|
|
23
|
-
pipeline_name: string;
|
|
24
|
-
run_id: string;
|
|
25
|
-
status: string;
|
|
26
|
-
duration_ms: number;
|
|
27
|
-
total_tokens: number;
|
|
28
|
-
total_tool_calls: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface PipelineCancelledEvent {
|
|
32
|
-
run_id: string;
|
|
33
|
-
cancelled_at_stage: string;
|
|
34
|
-
duration_ms: number;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface StageStartEvent {
|
|
38
|
-
stage_name: string;
|
|
39
|
-
stage_index: number;
|
|
40
|
-
total_stages: number;
|
|
41
|
-
max_attempts: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface StageCompleteEvent {
|
|
45
|
-
stage_name: string;
|
|
46
|
-
stage_index: number;
|
|
47
|
-
total_stages: number;
|
|
48
|
-
status: string;
|
|
49
|
-
attempts: number;
|
|
50
|
-
duration_ms: number;
|
|
51
|
-
output_summary?: string;
|
|
52
|
-
output?: unknown;
|
|
53
|
-
tool_calls?: ToolCall[];
|
|
54
|
-
token_usage?: TokenUsage;
|
|
55
|
-
rejection_reason?: string;
|
|
56
|
-
rejection_details?: string[];
|
|
57
|
-
skipped_reason?: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface StageRetryEvent {
|
|
61
|
-
stage: string;
|
|
62
|
-
attempt: number;
|
|
63
|
-
max_attempts: number;
|
|
64
|
-
failures: string[];
|
|
65
|
-
agent_output_raw?: string;
|
|
66
|
-
tool_calls_count?: number;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface GroupStartEvent {
|
|
70
|
-
group_name: string;
|
|
71
|
-
max_iterations: number;
|
|
72
|
-
parallel?: boolean;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface GroupIterationEvent {
|
|
76
|
-
group_name: string;
|
|
77
|
-
iteration: number;
|
|
78
|
-
max_iterations: number;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export interface GroupFeedbackEvent {
|
|
82
|
-
group_name: string;
|
|
83
|
-
iteration: number;
|
|
84
|
-
rejection_reason: string;
|
|
85
|
-
rejection_details: string[];
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface GroupCompleteEvent {
|
|
89
|
-
group_name: string;
|
|
90
|
-
iterations: number;
|
|
91
|
-
status: string;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface StageContextEvent {
|
|
95
|
-
stage: string;
|
|
96
|
-
run_id: string;
|
|
97
|
-
context_keys: Record<string, number>;
|
|
98
|
-
context_content?: Record<string, unknown>;
|
|
99
|
-
system_prompt?: string;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface StagedToolCallStartEvent extends ToolCallStartEvent {
|
|
103
|
-
stage: string;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface StagedToolCallCompleteEvent extends ToolCallCompleteEvent {
|
|
107
|
-
stage: string;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export interface StagedAgentThinkingEvent extends AgentThinkingEvent {
|
|
111
|
-
stage: string;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface StagedAgentProgressEvent extends AgentProgressEvent {
|
|
115
|
-
stage: string;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export interface StagedAgentTokenEvent extends AgentTokenEvent {
|
|
119
|
-
stage: string;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export interface EngineEvents {
|
|
123
|
-
onPipelineStart?: (event: PipelineStartEvent) => void;
|
|
124
|
-
onPipelineComplete?: (event: PipelineCompleteEvent) => void;
|
|
125
|
-
onPipelineCancelled?: (event: PipelineCancelledEvent) => void;
|
|
126
|
-
onStageStart?: (event: StageStartEvent) => void;
|
|
127
|
-
onStageComplete?: (event: StageCompleteEvent) => void;
|
|
128
|
-
onTaskRetry?: (event: StageRetryEvent) => void;
|
|
129
|
-
onGroupStart?: (event: GroupStartEvent) => void;
|
|
130
|
-
onGroupIteration?: (event: GroupIterationEvent) => void;
|
|
131
|
-
onGroupFeedback?: (event: GroupFeedbackEvent) => void;
|
|
132
|
-
onGroupComplete?: (event: GroupCompleteEvent) => void;
|
|
133
|
-
onStageContext?: (event: StageContextEvent) => void;
|
|
134
|
-
// Real-time tool call streaming (used by --live mode)
|
|
135
|
-
onToolCallStart?: (event: StagedToolCallStartEvent) => void;
|
|
136
|
-
onToolCallComplete?: (event: StagedToolCallCompleteEvent) => void;
|
|
137
|
-
// Agent thinking/progress (text content emitted alongside tool calls)
|
|
138
|
-
onAgentThinking?: (event: StagedAgentThinkingEvent) => void;
|
|
139
|
-
onAgentProgress?: (event: StagedAgentProgressEvent) => void;
|
|
140
|
-
onAgentToken?: (event: StagedAgentTokenEvent) => void;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Keep the generic event bus for other use cases
|
|
144
|
-
export type PipelineEvent =
|
|
145
|
-
| { type: 'pipeline_start'; pipelineId: string }
|
|
146
|
-
| { type: 'pipeline_complete'; pipelineId: string }
|
|
147
|
-
| { type: 'stage_start'; stageId: string; stageName: string }
|
|
148
|
-
| { type: 'stage_complete'; stageId: string; stageName: string }
|
|
149
|
-
| { type: 'task_retry'; stageName: string; attempt: number; failures: string[]; rawOutput?: string }
|
|
150
|
-
| { type: 'group_start'; groupName: string; maxIterations: number }
|
|
151
|
-
| { type: 'group_iteration'; groupName: string; iteration: number; maxIterations: number }
|
|
152
|
-
| { type: 'group_feedback'; groupName: string; iteration: number; rejectionReason: string }
|
|
153
|
-
| { type: 'group_complete'; groupName: string; iterations: number; status: string };
|
|
154
|
-
|
|
155
|
-
export class PipelineEventEmitter {
|
|
156
|
-
private listeners: Array<(event: PipelineEvent) => void> = [];
|
|
157
|
-
|
|
158
|
-
on(listener: (event: PipelineEvent) => void): void {
|
|
159
|
-
this.listeners.push(listener);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
emit(event: PipelineEvent): void {
|
|
163
|
-
for (const listener of this.listeners) {
|
|
164
|
-
listener(event);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|