Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions packages/seats/src/supervisor.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,22 @@ const runAttempt = ({ adapter, prompt, ctx, run, settings, attempt, sessionId, e
* @param {(info: {runId, adapter, attempt, idleMs, windowMs}) => void} [args.policy.onStalled]
* @param {string} [args.runId]
* @param {string} [args.label]
* @param {string} [args.taskId] the board task this run works; needs `podId`
* @param {string} [args.podId]
* @param {object} [args.actor] the run's own party, {type, id, owner?, onBehalfOf?};
* entries the run writes without naming an actor (run.started, tool calls,
* permission requests) record this one instead of null
* @returns {Promise<{ runId: string, outcome: string, attempts: number, result?: object, failure?: Error }>}
*/
export const superviseRun = async ({ adapter, prompt, ctx = {}, store, policy = {}, runId, label } = {}) => {
export const superviseRun = async ({
adapter, prompt, ctx = {}, store, policy = {}, runId, label, taskId, podId, actor,
} = {}) => {
if (!adapter || typeof adapter.spawn !== 'function') throw new Error('superviseRun needs an adapter with spawn()');
if (!store || typeof store.startRun !== 'function') throw new Error('superviseRun needs a trace store');
const settings = resolvePolicy(policy);
const run = store.startRun({ runId, adapter: adapter.name, label });
const run = store.startRun({
runId, adapter: adapter.name, label, taskId, podId, actor,
});
// The runtime token rides in ctx, not through the valet, so the store would
// not otherwise know to scrub it from agent text.
if (typeof run.redact === 'function' && typeof ctx.runtimeToken === 'string' && ctx.runtimeToken) {
Expand Down
37 changes: 37 additions & 0 deletions packages/seats/test/supervisor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,43 @@ test('a successful run: started, the adapter\'s events, settled ok — in that o
assert.equal(store.listRuns()[0].outcome, 'ok');
}));

test('the run\'s actor, task and pod reach the record, so run-owned entries are attributed', () => withStore(async (store) => {
const actor = { type: 'agent', id: 'u-agent-quire', owner: 'u-sam', onBehalfOf: { id: 'u-cy', role: 'assigner' } };
const adapter = {
name: 'ok',
async spawn(prompt, ctx) {
ctx.trace.emit('agent.message_chunk', { text: 'PONG' });
return { text: 'PONG' };
},
};
const out = await superviseRun({
adapter, prompt: 'ping', store, policy: FAST, taskId: 'TASK-548', podId: 'pod-sharpen', actor,
});
assert.equal(out.outcome, 'ok');
const entries = store.readRun(out.runId);
for (const entry of entries) {
assert.equal(entry.taskId, 'TASK-548', `${entry.type} carries the task`);
assert.equal(entry.podId, 'pod-sharpen', `${entry.type} carries the pod`);
}
const byType = Object.fromEntries(entries.map((e) => [e.type, e]));
assert.deepEqual(byType['run.started'].actor, actor, 'the supervisor\'s own run start is the run\'s');
assert.deepEqual(byType['agent.message_chunk'].actor, actor, 'an emit that names no actor takes the run\'s');
const [indexed] = store.listRuns();
assert.equal(indexed.taskId, 'TASK-548');
assert.equal(indexed.podId, 'pod-sharpen');
assert.deepEqual(indexed.actor, actor);
}));

test('a taskId without a podId is refused before the adapter is spawned', () => withStore(async (store) => {
let spawned = false;
const adapter = { name: 'ok', async spawn() { spawned = true; return { text: '' }; } };
await assert.rejects(
superviseRun({ adapter, prompt: 'ping', store, policy: FAST, taskId: 'TASK-548' }),
/a taskId needs a podId/,
);
assert.equal(spawned, false);
}));

test('a hung adapter that honours abort settles stuck, with the reason on its signal', () => withStore(async (store) => {
const calls = [];
const stalled = [];
Expand Down
Loading