Clean up old stuff & fix ui errors
Build and Push Spoon Images / quality (push) Successful in 2m22s
Build and Push Spoon Images / build-images (push) Successful in 23m10s

This commit is contained in:
Gabriel Brown
2026-06-23 14:57:05 -04:00
parent d207b8b0b8
commit a6f7ea7f78
34 changed files with 1565 additions and 551 deletions
+122
View File
@@ -48,6 +48,7 @@ const createAgentJob = async (
spoonId: Id<'spoons'>;
status: 'running' | 'failed' | 'cancelled';
workspaceStatus?: 'active' | 'stopped' | 'failed' | 'expired';
threadId?: Id<'threads'>;
},
) =>
await t.mutation(async (ctx) => {
@@ -64,6 +65,7 @@ const createAgentJob = async (
spoonId: args.spoonId,
ownerId: args.ownerId,
agentRequestId: requestId,
threadId: args.threadId,
status: args.status,
prompt: 'Clean this workspace',
runtime: 'opencode',
@@ -299,6 +301,126 @@ describe('convex-test harness', () => {
).rejects.toThrow('Agent job not found.');
});
test('persists and clamps workspace agent thread width', async () => {
const t = convexTest(schema, modules);
const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>;
const spoonId = await authed(t, ownerId).mutation(
api.spoons.createManual,
spoonInput,
);
const jobId = await createAgentJob(t, {
ownerId,
spoonId,
status: 'running',
workspaceStatus: 'active',
});
const defaults = await authed(t, ownerId).query(
api.agentJobs.getWorkspaceUiState,
{ jobId },
);
expect(defaults.agentThreadWidth).toBe(420);
await authed(t, ownerId).mutation(api.agentJobs.patchWorkspaceUiState, {
jobId,
agentThreadWidth: 999,
});
const wide = await authed(t, ownerId).query(
api.agentJobs.getWorkspaceUiState,
{ jobId },
);
expect(wide.agentThreadWidth).toBe(720);
await authed(t, ownerId).mutation(api.agentJobs.patchWorkspaceUiState, {
jobId,
agentThreadWidth: 100,
});
const narrow = await authed(t, ownerId).query(
api.agentJobs.getWorkspaceUiState,
{ jobId },
);
expect(narrow.agentThreadWidth).toBe(320);
});
test('deletes terminal threads and attached terminal workspace rows', async () => {
const t = convexTest(schema, modules);
const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>;
const spoonId = await authed(t, ownerId).mutation(
api.spoons.createManual,
spoonInput,
);
const threadId = await t.mutation(async (ctx) => {
return await ctx.db.insert('threads', {
ownerId,
spoonId,
title: 'Failed attempt',
source: 'user_request',
status: 'failed',
priority: 'normal',
createdAt: Date.now(),
updatedAt: Date.now(),
});
});
const jobId = await createAgentJob(t, {
ownerId,
spoonId,
threadId,
status: 'failed',
workspaceStatus: 'failed',
});
await t.mutation(async (ctx) => {
await ctx.db.patch(threadId, { latestAgentJobId: jobId });
});
await authed(t, ownerId).mutation(api.threads.deleteThread, { threadId });
const [thread, job, messages] = await t.run(async (ctx) => {
const rows = await ctx.db
.query('agentJobMessages')
.withIndex('by_job', (q) => q.eq('jobId', jobId))
.collect();
return [await ctx.db.get(threadId), await ctx.db.get(jobId), rows];
});
expect(thread).toBeNull();
expect(job).toBeNull();
expect(messages).toHaveLength(0);
});
test('does not delete threads with active workspace runs', async () => {
const t = convexTest(schema, modules);
const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>;
const spoonId = await authed(t, ownerId).mutation(
api.spoons.createManual,
spoonInput,
);
const threadId = await t.mutation(async (ctx) => {
return await ctx.db.insert('threads', {
ownerId,
spoonId,
title: 'Active attempt',
source: 'user_request',
status: 'running',
priority: 'normal',
createdAt: Date.now(),
updatedAt: Date.now(),
});
});
const jobId = await createAgentJob(t, {
ownerId,
spoonId,
threadId,
status: 'running',
workspaceStatus: 'active',
});
await t.mutation(async (ctx) => {
await ctx.db.patch(threadId, { latestAgentJobId: jobId });
});
await expect(
authed(t, ownerId).mutation(api.threads.deleteThread, { threadId }),
).rejects.toThrow('Stop or cancel active workspace runs');
});
test('queues a new thread job after the previous job is terminal', async () => {
const t = convexTest(schema, modules);
const ownerId = (await createUser(t, 'owner@example.com')) as Id<'users'>;