Skip to content

Commit ab4aea1

Browse files
committed
feat: Enhance gRPC client with trajectory pattern storage functionality
- Added `storeTrajectoryPattern` method to `AegisRuntimeClient` for storing trajectory patterns in Cortex. - Updated logging to reflect new response structure in validation process. refactor: Simplify server workflow registration process - Removed workflow registry registration from server logic. - Cleaned up workflow deletion process by removing unnecessary unregister calls. chore: Update types for judges and workflow states - Introduced `JudgeConfig` interface for per-state validation configuration. - Enhanced `WorkflowState` interface to include judges and pre-execution validation fields. fix: Improve validation request and response structures - Updated `ValidateRequest` and `ValidateResponse` interfaces to align with new validation logic. - Adjusted transition conditions to include consensus-based evaluations. refactor: Remove deprecated workflow generator and registry - Deleted `workflow-generator.ts` and `workflow-registry.ts` as part of the refactor. - Cleaned up associated tests and imports. feat: Implement trajectory pattern storage in workflow execution - Integrated trajectory pattern storage into the `aegis_workflow` function. - Enhanced state execution logic to handle judges and trajectory accumulation. test: Remove obsolete workflow registry tests - Deleted tests related to the removed workflow registry functionality.
1 parent 1746468 commit ab4aea1

File tree

12 files changed

+340
-734
lines changed

12 files changed

+340
-734
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ By participating in this project you agree to abide by our [Code of Conduct](COD
2828

2929
- **Node.js** 20+
3030
- **npm** 9+
31-
- **PostgreSQL** 15+ (for workflow registry)
31+
- **PostgreSQL** 15+
3232
- **Temporal Server** (run locally via `temporal server start-dev` or the compose stack in [aegis-examples](https://github.com/100monkeys-ai/aegis-examples))
3333
- **AEGIS Runtime** — the Rust gRPC service (`aegis-orchestrator`)
3434

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ TemporalWorkflowDefinition (JSON)
3636

3737
- **Generic Interpreter Workflow** — a single Temporal workflow (`aegis_workflow`) interprets all AEGIS FSM definitions at runtime; no code generation needed
3838
- **Dynamic workflow registration** — POST a JSON definition to `/register-workflow`; no worker restart required
39-
- **Multi-worker coordination** — PostgreSQL-backed workflow registry ensures all worker replicas share the same definitions
39+
- **Multi-worker coordination** — PostgreSQL ensures all worker replicas share the same definitions
4040
- **Full activity suite**`executeAgentActivity`, `executeSystemCommandActivity`, `validateOutputActivity`, `executeParallelAgentsActivity`
4141
- **Event bridge** — publishes `WorkflowStateEntered`, `IterationStarted`, `RefinementApplied`, etc. back to the Rust orchestrator so they become typed domain events
4242

@@ -161,7 +161,7 @@ aegis-temporal-worker/
161161
│ ├── config.ts # Zod-validated environment configuration
162162
│ ├── logger.ts # Pino structured logger
163163
│ ├── types.ts # TypeScript type definitions
164-
│ ├── database.ts # PostgreSQL client (workflow registry persistence)
164+
│ ├── database.ts # PostgreSQL client
165165
│ ├── server.ts # Express HTTP server (workflow registration API)
166166
│ ├── worker.ts # Temporal worker initialization
167167
│ ├── workflow-registry.ts # In-memory workflow function registry

src/activities/index.ts

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import type {
99
ExecuteAgentRequest,
1010
ExecuteSystemCommandRequest,
1111
ValidateRequest,
12+
StoreTrajectoryPatternRequest,
13+
TrajectoryStep,
1214
Blackboard,
1315
} from '../types.js';
1416
import { fetchWorkflowDefinition } from './workflow-activities.js';
@@ -20,6 +22,7 @@ export async function executeAgentActivity(params: {
2022
agentId: string;
2123
input: string;
2224
context: Blackboard;
25+
parentExecutionId?: string;
2326
}): Promise<any> {
2427
logger.info({ agent_id: params.agentId }, 'Executing agent activity');
2528

@@ -28,6 +31,7 @@ export async function executeAgentActivity(params: {
2831
input: params.input,
2932
context_json: JSON.stringify(params.context),
3033
timeout_seconds: 300,
34+
workflow_execution_id: params.parentExecutionId, // proto field 5 (keepCase:true)
3135
};
3236

3337
try {
@@ -99,25 +103,32 @@ export async function executeSystemCommandActivity(params: {
99103
*/
100104
export async function validateOutputActivity(params: {
101105
output: string;
102-
judges: Array<{ agent_id: string; weight?: number }>;
106+
task?: string;
107+
judges: Array<{ agent_id: string; weight?: number; input_template?: string }>;
103108
consensus_strategy?: string;
104109
consensus_threshold?: number;
110+
context_json?: string;
105111
}): Promise<any> {
106112
logger.info({ judge_count: params.judges.length }, 'Validating output with judges');
107113

108114
const request: ValidateRequest = {
109-
agent_output: params.output,
110-
judge_agent_ids: params.judges.map(j => j.agent_id),
111-
context: {},
115+
output: params.output,
116+
task: params.task,
117+
judges: params.judges.map(j => ({ agent_id: j.agent_id, weight: j.weight, input_template: j.input_template })),
118+
consensus: params.consensus_strategy
119+
? { strategy: params.consensus_strategy, threshold: params.consensus_threshold ?? 0.8 }
120+
: undefined,
121+
context_json: params.context_json,
112122
};
113123

114124
try {
115125
const response = await aegisRuntimeClient.validateWithJudges(request);
116126

117127
return {
118-
final_score: response.final_score,
128+
score: response.score,
119129
confidence: response.confidence,
120-
individual_scores: response.individual_scores,
130+
binary_valid: response.binary_valid,
131+
individual_results: response.individual_results,
121132
reasoning: response.reasoning,
122133
};
123134
} catch (error) {
@@ -131,6 +142,8 @@ export async function validateOutputActivity(params: {
131142
*/
132143
export async function executeParallelAgentsActivity(params: {
133144
agents: Array<{ agent: string; input: string; weight?: number }>;
145+
/** External judge agents from the state's `judges_for_parallel` field (ADR-016). */
146+
judges?: Array<{ agent_id: string; weight?: number; input_template?: string }>;
134147
consensus: {
135148
strategy: string;
136149
threshold: number;
@@ -154,6 +167,7 @@ export async function executeParallelAgentsActivity(params: {
154167
if (completedEvent) {
155168
return {
156169
output: completedEvent.final_output || '',
170+
agent: agentConfig.agent,
157171
weight: agentConfig.weight || 1.0,
158172
};
159173
}
@@ -162,28 +176,85 @@ export async function executeParallelAgentsActivity(params: {
162176
})
163177
);
164178

165-
// All agents completed - now validate with consensus
166-
const outputsForValidation = results.map(r => r.output).join('\n\n---\n\n');
179+
// All agents completed – validate the combined output with dedicated judge agents.
180+
// judges_for_parallel must be a *separate* set of agents from the workers above;
181+
// passing workers as their own judges would violate ADR-016 (agents cannot judge themselves).
182+
if (!params.judges || params.judges.length === 0) {
183+
// No external judges configured — return raw results without consensus scoring.
184+
return {
185+
consensus: {
186+
score: 1.0,
187+
confidence: 1.0,
188+
strategy: params.consensus.strategy,
189+
metadata: {
190+
individual_outputs: results.map(r => r.output),
191+
individual_results: [],
192+
reasoning: 'No judge agents configured for this ParallelAgents state',
193+
},
194+
},
195+
};
196+
}
197+
198+
const outputsForValidation = results.map(r => `[${r.agent}]:\n${r.output}`).join('\n\n---\n\n');
167199

168200
const validationResult = await aegisRuntimeClient.validateWithJudges({
169-
agent_output: outputsForValidation,
170-
judge_agent_ids: params.agents.map(a => a.agent),
171-
context: {},
201+
output: outputsForValidation,
202+
judges: params.judges,
203+
consensus: {
204+
strategy: params.consensus.strategy,
205+
threshold: params.consensus.threshold,
206+
},
172207
});
173208

174209
return {
175-
individual_outputs: results.map(r => r.output),
176-
individual_scores: validationResult.individual_scores,
177-
final_score: validationResult.final_score,
178-
confidence: validationResult.confidence,
179-
reasoning: validationResult.reasoning,
210+
consensus: {
211+
score: validationResult.score,
212+
confidence: validationResult.confidence,
213+
binary_valid: validationResult.binary_valid,
214+
strategy: params.consensus.strategy,
215+
metadata: {
216+
individual_outputs: results.map(r => r.output),
217+
individual_results: validationResult.individual_results,
218+
reasoning: validationResult.reasoning,
219+
},
220+
},
180221
};
181222
} catch (error) {
182223
logger.error({ error }, 'Parallel agents activity failed');
183224
throw error;
184225
}
185226
}
186227

228+
/**
229+
* Store a successful trajectory in Cortex memory (ADR-049 Pillar 2)
230+
*/
231+
export async function storeTrajectoryPatternActivity(params: {
232+
taskSignature: string;
233+
steps: TrajectoryStep[];
234+
successScore: number;
235+
}): Promise<any> {
236+
logger.info({ task_signature: params.taskSignature, step_count: params.steps.length }, 'Storing trajectory pattern');
237+
238+
const request: StoreTrajectoryPatternRequest = {
239+
task_signature: params.taskSignature,
240+
steps: params.steps,
241+
success_score: params.successScore,
242+
};
243+
244+
try {
245+
const response = await aegisRuntimeClient.storeTrajectoryPattern(request);
246+
return {
247+
trajectory_id: response.trajectory_id,
248+
new_weight: response.new_weight,
249+
deduplicated: response.deduplicated,
250+
};
251+
} catch (error) {
252+
// Cortex storage failure must never crash the workflow — log and swallow.
253+
logger.warn({ error, task_signature: params.taskSignature }, 'Trajectory pattern storage failed (non-fatal)');
254+
return null;
255+
}
256+
}
257+
187258
import { publishEventActivity } from './event-activities.js';
188259

189260
// Ensure all activities are exported for Temporal Worker
@@ -192,6 +263,7 @@ export const activities = {
192263
executeSystemCommandActivity,
193264
validateOutputActivity,
194265
executeParallelAgentsActivity,
266+
storeTrajectoryPatternActivity,
195267
fetchWorkflowDefinition,
196268
publishEventActivity,
197269
};

src/grpc/client.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import type {
1717
QueryCortexRequest,
1818
QueryCortexResponse,
1919
StoreCortexPatternRequest,
20-
StoreCortexPatternResponse
20+
StoreCortexPatternResponse,
21+
StoreTrajectoryPatternRequest,
22+
StoreTrajectoryPatternResponse,
2123
} from '../types.js';
2224

2325
// Load protobuf definition
@@ -101,7 +103,7 @@ class AegisRuntimeClient {
101103
logger.error({ error }, 'Validation with judges failed');
102104
reject(error);
103105
} else {
104-
logger.info({ final_score: response.final_score, confidence: response.confidence }, 'Validation completed');
106+
logger.info({ score: response.score, confidence: response.confidence, binary_valid: response.binary_valid }, 'Validation completed');
105107
resolve(response);
106108
}
107109
});
@@ -142,6 +144,23 @@ class AegisRuntimeClient {
142144
});
143145
}
144146

147+
/**
148+
* Store a trajectory pattern in Cortex (ADR-049 Pillar 2)
149+
*/
150+
async storeTrajectoryPattern(request: StoreTrajectoryPatternRequest): Promise<StoreTrajectoryPatternResponse> {
151+
return new Promise((resolve, reject) => {
152+
this.client.StoreTrajectoryPattern(request, (error: Error | null, response: StoreTrajectoryPatternResponse) => {
153+
if (error) {
154+
logger.error({ error }, 'Trajectory pattern storage failed');
155+
reject(error);
156+
} else {
157+
logger.info({ new_weight: response.new_weight }, 'Trajectory pattern stored');
158+
resolve(response);
159+
}
160+
});
161+
});
162+
}
163+
145164
/**
146165
* Close the gRPC connection
147166
*/

src/server.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import express, { type Request, type Response } from 'express';
77
import { config } from './config.js';
88
import { logger } from './logger.js';
99
import { database } from './database.js';
10-
import { workflowRegistry } from './workflow-registry.js';
1110
import type { TemporalWorkflowDefinition } from './types.js';
1211

1312
const app = express();
@@ -51,9 +50,6 @@ app.post('/register-workflow', async (req: Request, res: Response) => {
5150
// Save to database (for multi-worker coordination)
5251
await database.saveWorkflowDefinition(definition);
5352

54-
// Register with workflow registry (generates TypeScript workflow function)
55-
await workflowRegistry.registerWorkflow(definition);
56-
5753
logger.info({ workflow_id: definition.workflow_id, name: definition.name }, 'Workflow registered successfully');
5854

5955
res.status(200).json({
@@ -136,7 +132,6 @@ app.delete('/workflows/:id', async (req: Request, res: Response) => {
136132
}
137133

138134
await database.deleteWorkflowDefinition(id);
139-
workflowRegistry.unregisterWorkflow(id);
140135

141136
logger.info({ workflow_id: id }, 'Workflow deleted');
142137

0 commit comments

Comments
 (0)