feat: add shadow LangGraph orchestrator runtime
This commit is contained in:
@@ -374,6 +374,98 @@ test('agent run creation stores code tool metadata in the queued message', async
|
||||
});
|
||||
});
|
||||
|
||||
test('code run shadow observation is fire-and-forget and records completion separately', async () => {
|
||||
const pool = createFakePool();
|
||||
let releaseObservation;
|
||||
let observedInput = null;
|
||||
const observation = new Promise((resolve) => {
|
||||
releaseObservation = resolve;
|
||||
});
|
||||
const gateway = createAgentRunGateway({
|
||||
pool,
|
||||
userAuth: {},
|
||||
tkmindProxy: {},
|
||||
autoDispatch: false,
|
||||
observeWorkflowRun: async (input) => {
|
||||
observedInput = input;
|
||||
return observation;
|
||||
},
|
||||
});
|
||||
|
||||
const run = await gateway.createRun('user-shadow', {
|
||||
requestId: 'req-shadow',
|
||||
sessionId: 'session-shadow',
|
||||
userMessage: { role: 'user', content: [{ type: 'text', text: 'refactor this' }] },
|
||||
toolMode: 'code',
|
||||
taskType: 'repo_refactor',
|
||||
});
|
||||
|
||||
assert.equal(run.status, 'queued');
|
||||
await waitFor(() => observedInput != null);
|
||||
assert.equal(observedInput.runId, run.id);
|
||||
assert.equal(observedInput.workflowName, 'code-run-v1');
|
||||
assert.equal(
|
||||
pool.events.some((event) => event.eventType === 'workflow_shadow_completed'),
|
||||
false,
|
||||
);
|
||||
|
||||
releaseObservation({
|
||||
observed: true,
|
||||
engine: 'langgraph',
|
||||
mode: 'shadow',
|
||||
configVersion: 3,
|
||||
shadowRun: { status: 'succeeded', phase: 'completed' },
|
||||
});
|
||||
await waitFor(() => pool.events.some(
|
||||
(event) => event.eventType === 'workflow_shadow_completed',
|
||||
));
|
||||
const event = pool.events.find((item) => item.eventType === 'workflow_shadow_completed');
|
||||
assert.deepEqual(JSON.parse(event.dataJson), {
|
||||
engine: 'langgraph',
|
||||
mode: 'shadow',
|
||||
configVersion: 3,
|
||||
status: 'succeeded',
|
||||
phase: 'completed',
|
||||
});
|
||||
});
|
||||
|
||||
test('shadow observer failure cannot fail a Native run and chat runs are not observed', async () => {
|
||||
const pool = createFakePool();
|
||||
let observations = 0;
|
||||
const gateway = createAgentRunGateway({
|
||||
pool,
|
||||
userAuth: {},
|
||||
tkmindProxy: {},
|
||||
autoDispatch: false,
|
||||
observeWorkflowRun: async () => {
|
||||
observations += 1;
|
||||
const error = new Error('shadow unavailable');
|
||||
error.code = 'SHADOW_UNAVAILABLE';
|
||||
throw error;
|
||||
},
|
||||
});
|
||||
|
||||
const codeRun = await gateway.createRun('user-shadow', {
|
||||
requestId: 'req-shadow-failure',
|
||||
userMessage: { role: 'user', content: 'change code' },
|
||||
toolMode: 'code',
|
||||
});
|
||||
const chatRun = await gateway.createRun('user-shadow', {
|
||||
requestId: 'req-chat-no-shadow',
|
||||
userMessage: { role: 'user', content: 'hello' },
|
||||
toolMode: 'chat',
|
||||
});
|
||||
|
||||
assert.equal(codeRun.status, 'queued');
|
||||
assert.equal(chatRun.status, 'queued');
|
||||
await waitFor(() => pool.events.some(
|
||||
(event) => event.eventType === 'workflow_shadow_failed',
|
||||
));
|
||||
assert.equal(observations, 1);
|
||||
const failure = pool.events.find((event) => event.eventType === 'workflow_shadow_failed');
|
||||
assert.equal(JSON.parse(failure.dataJson).code, 'SHADOW_UNAVAILABLE');
|
||||
});
|
||||
|
||||
test('agent run starts a session and marks submitted reply as succeeded', async () => {
|
||||
const pool = createFakePool();
|
||||
const submitted = [];
|
||||
|
||||
Reference in New Issue
Block a user