fix(agent-jobs): recover failed claim decryption
This commit is contained in:
@@ -294,6 +294,40 @@ const deleteWorkspaceRows = async (ctx: MutationCtx, job: Doc<'agentJobs'>) => {
|
||||
await ctx.db.delete(job._id);
|
||||
};
|
||||
|
||||
const failJobClaim = async (
|
||||
ctx: MutationCtx,
|
||||
job: Doc<'agentJobs'>,
|
||||
reason: string,
|
||||
) => {
|
||||
const failedAt = Date.now();
|
||||
await ctx.db.patch(job._id, {
|
||||
status: 'failed',
|
||||
error: reason,
|
||||
completedAt: failedAt,
|
||||
updatedAt: failedAt,
|
||||
});
|
||||
await ctx.db.patch(job.agentRequestId, {
|
||||
status: 'failed',
|
||||
updatedAt: failedAt,
|
||||
});
|
||||
if (job.threadId) {
|
||||
await ctx.db.patch(job.threadId, {
|
||||
status: 'failed',
|
||||
updatedAt: failedAt,
|
||||
resolvedAt: failedAt,
|
||||
});
|
||||
}
|
||||
await ctx.db.insert('agentJobEvents', {
|
||||
jobId: job._id,
|
||||
spoonId: job.spoonId,
|
||||
ownerId: job.ownerId,
|
||||
level: 'error',
|
||||
phase: 'queued',
|
||||
message: `Job could not be claimed: ${reason}`,
|
||||
createdAt: failedAt,
|
||||
});
|
||||
};
|
||||
|
||||
const getAgentSettings = async (ctx: MutationCtx, spoon: Doc<'spoons'>) => {
|
||||
const settings = await ctx.db
|
||||
.query('spoonAgentSettings')
|
||||
@@ -1078,59 +1112,48 @@ export const claimNextInternal = internalMutation({
|
||||
aiProviderProfile?.ownerId === job.ownerId && aiProviderProfile.enabled
|
||||
? aiProviderProfile
|
||||
: null;
|
||||
const failClaim = async (reason: string) => {
|
||||
const failedAt = Date.now();
|
||||
await ctx.db.patch(job._id, {
|
||||
status: 'failed',
|
||||
error: reason,
|
||||
completedAt: failedAt,
|
||||
updatedAt: failedAt,
|
||||
});
|
||||
await ctx.db.patch(job.agentRequestId, {
|
||||
status: 'failed',
|
||||
updatedAt: failedAt,
|
||||
});
|
||||
if (job.threadId) {
|
||||
await ctx.db.patch(job.threadId, {
|
||||
status: 'failed',
|
||||
updatedAt: failedAt,
|
||||
resolvedAt: failedAt,
|
||||
});
|
||||
}
|
||||
await ctx.db.insert('agentJobEvents', {
|
||||
jobId: job._id,
|
||||
spoonId: job.spoonId,
|
||||
ownerId: job.ownerId,
|
||||
level: 'error',
|
||||
phase: 'queued',
|
||||
message: `Job could not be claimed: ${reason}`,
|
||||
createdAt: failedAt,
|
||||
});
|
||||
};
|
||||
if (!spoon) {
|
||||
await failClaim('The Spoon for this job no longer exists.');
|
||||
await failJobClaim(
|
||||
ctx,
|
||||
job,
|
||||
'The Spoon for this job no longer exists.',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
if (!ownedProfile) {
|
||||
await failClaim(
|
||||
await failJobClaim(
|
||||
ctx,
|
||||
job,
|
||||
'AI is not configured for this user. Add an AI provider in settings.',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
if (ownedProfile.authType !== 'none' && !ownedProfile.encryptedSecret) {
|
||||
await failClaim('Selected AI provider is missing credentials.');
|
||||
await failJobClaim(
|
||||
ctx,
|
||||
job,
|
||||
'Selected AI provider is missing credentials.',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
if (agentSettings && !agentSettings.enabled) {
|
||||
await failClaim('Agent jobs are disabled for this Spoon.');
|
||||
await failJobClaim(ctx, job, 'Agent jobs are disabled for this Spoon.');
|
||||
return null;
|
||||
}
|
||||
if ((job.runtime ?? 'opencode') !== 'opencode') {
|
||||
await failClaim('Legacy OpenAI direct jobs are no longer supported.');
|
||||
await failJobClaim(
|
||||
ctx,
|
||||
job,
|
||||
'Legacy OpenAI direct jobs are no longer supported.',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
if (secrets.length !== job.selectedSecretIds.length) {
|
||||
await failClaim('Selected secrets must belong to this Spoon.');
|
||||
await failJobClaim(
|
||||
ctx,
|
||||
job,
|
||||
'Selected secrets must belong to this Spoon.',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
const now = Date.now();
|
||||
@@ -1164,6 +1187,20 @@ export const claimNextInternal = internalMutation({
|
||||
},
|
||||
});
|
||||
|
||||
export const failClaimInternal = internalMutation({
|
||||
args: {
|
||||
jobId: v.id('agentJobs'),
|
||||
workerId: v.string(),
|
||||
reason: v.string(),
|
||||
},
|
||||
handler: async (ctx, { jobId, workerId, reason }) => {
|
||||
const job = await ctx.db.get(jobId);
|
||||
if (job?.status !== 'claimed' || job.claimedBy !== workerId) return null;
|
||||
await failJobClaim(ctx, job, reason);
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
export const updateStatus = mutation({
|
||||
args: {
|
||||
workerToken: v.string(),
|
||||
|
||||
Reference in New Issue
Block a user