v0.6.35: additional jira fields, HITL docs, logs cleanup efficiency

This commit is contained in:
Waleed
2026-04-09 22:53:05 -07:00
committed by GitHub
7 changed files with 64 additions and 31 deletions

View File

@@ -0,0 +1,9 @@
{
"pages": [
"listPausedExecutions",
"getPausedExecution",
"getPausedExecutionByResumePath",
"getPauseContext",
"resumeExecution"
]
}

View File

@@ -10,6 +10,7 @@
"typescript",
"---Endpoints---",
"(generated)/workflows",
"(generated)/human-in-the-loop",
"(generated)/logs",
"(generated)/usage",
"(generated)/audit-logs",

View File

@@ -1,5 +1,5 @@
import { db } from '@sim/db'
import { subscription, user, workflowExecutionLogs, workspace } from '@sim/db/schema'
import { subscription, workflowExecutionLogs, workspace } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { and, eq, inArray, isNull, lt } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
@@ -26,38 +26,19 @@ export async function GET(request: NextRequest) {
const retentionDate = new Date()
retentionDate.setDate(retentionDate.getDate() - Number(env.FREE_PLAN_LOG_RETENTION_DAYS || '7'))
const freeUsers = await db
.select({ userId: user.id })
.from(user)
const freeWorkspacesSubquery = db
.select({ id: workspace.id })
.from(workspace)
.leftJoin(
subscription,
and(
eq(user.id, subscription.referenceId),
eq(subscription.referenceId, workspace.billedAccountUserId),
inArray(subscription.status, ENTITLED_SUBSCRIPTION_STATUSES),
sqlIsPaid(subscription.plan)
)
)
.where(isNull(subscription.id))
if (freeUsers.length === 0) {
logger.info('No free users found for log cleanup')
return NextResponse.json({ message: 'No free users found for cleanup' })
}
const freeUserIds = freeUsers.map((u) => u.userId)
const workspacesQuery = await db
.select({ id: workspace.id })
.from(workspace)
.where(inArray(workspace.billedAccountUserId, freeUserIds))
if (workspacesQuery.length === 0) {
logger.info('No workspaces found for free users')
return NextResponse.json({ message: 'No workspaces found for cleanup' })
}
const workspaceIds = workspacesQuery.map((w) => w.id)
const results = {
enhancedLogs: {
total: 0,
@@ -83,7 +64,7 @@ export async function GET(request: NextRequest) {
let batchesProcessed = 0
let hasMoreLogs = true
logger.info(`Starting enhanced logs cleanup for ${workspaceIds.length} workspaces`)
logger.info('Starting enhanced logs cleanup for free-plan workspaces')
while (hasMoreLogs && batchesProcessed < MAX_BATCHES) {
const oldEnhancedLogs = await db
@@ -105,8 +86,8 @@ export async function GET(request: NextRequest) {
.from(workflowExecutionLogs)
.where(
and(
inArray(workflowExecutionLogs.workspaceId, workspaceIds),
lt(workflowExecutionLogs.createdAt, retentionDate)
inArray(workflowExecutionLogs.workspaceId, freeWorkspacesSubquery),
lt(workflowExecutionLogs.startedAt, retentionDate)
)
)
.limit(BATCH_SIZE)

View File

@@ -479,6 +479,13 @@ Return ONLY the JQL query - no explanations or markdown formatting.`,
placeholder: 'Maximum results to return (default: 50)',
condition: { field: 'operation', value: ['search', 'get_comments', 'get_worklogs'] },
},
{
id: 'fields',
title: 'Fields',
type: 'short-input',
placeholder: 'Comma-separated fields to return (e.g., key,summary,status)',
condition: { field: 'operation', value: 'search' },
},
// Comment fields
{
id: 'commentBody',
@@ -922,6 +929,12 @@ Return ONLY the comment text - no explanations.`,
jql: params.jql,
nextPageToken: params.nextPageToken || undefined,
maxResults: params.maxResults ? Number.parseInt(params.maxResults) : undefined,
fields: params.fields
? params.fields
.split(',')
.map((f: string) => f.trim())
.filter(Boolean)
: undefined,
}
}
case 'add_comment': {
@@ -1114,6 +1127,10 @@ Return ONLY the comment text - no explanations.`,
startAt: { type: 'string', description: 'Pagination start index' },
jql: { type: 'string', description: 'JQL (Jira Query Language) search query' },
maxResults: { type: 'string', description: 'Maximum number of results to return' },
fields: {
type: 'string',
description: 'Comma-separated field names to return (e.g., key,summary,status)',
},
// Comment operation inputs
commentBody: { type: 'string', description: 'Text content for comment operations' },
commentId: { type: 'string', description: 'Comment ID for update/delete operations' },

View File

@@ -667,6 +667,7 @@ describe('AgentBlockHandler', () => {
expect(result).toEqual({
result: 'Success',
score: 0.95,
model: 'mock-model',
tokens: { input: 10, output: 20, total: 30 },
toolCalls: { list: [], count: 0 },
providerTiming: { total: 100 },

View File

@@ -1070,19 +1070,20 @@ export class AgentBlockHandler implements BlockHandler {
private processStandardResponse(result: any): BlockOutput {
return {
content: result.content,
model: result.model,
...this.createResponseMetadata(result),
...(result.interactionId && { interactionId: result.interactionId }),
}
}
private createResponseMetadata(result: {
model?: string
tokens?: { input?: number; output?: number; total?: number }
toolCalls?: Array<any>
timing?: any
cost?: any
}) {
return {
model: result.model,
tokens: result.tokens || {
input: DEFAULTS.TOKENS.PROMPT,
output: DEFAULTS.TOKENS.COMPLETION,

View File

@@ -50,13 +50,36 @@ export function parseJsmErrorMessage(
): string {
try {
const errorData = JSON.parse(errorText)
// JSM Service Desk: singular errorMessage
if (errorData.errorMessage) {
return `JSM Forms API error: ${errorData.errorMessage}`
return errorData.errorMessage
}
// Jira Platform: errorMessages array
if (Array.isArray(errorData.errorMessages) && errorData.errorMessages.length > 0) {
return errorData.errorMessages.join(', ')
}
// Confluence v2 / Forms API: RFC 7807 errors array
if (Array.isArray(errorData.errors) && errorData.errors.length > 0) {
const err = errorData.errors[0]
if (err?.title) {
return err.detail ? `${err.title}: ${err.detail}` : err.title
}
}
// Jira Platform field-level errors object
if (errorData.errors && !Array.isArray(errorData.errors)) {
const fieldErrors = Object.entries(errorData.errors)
.map(([field, msg]) => `${field}: ${msg}`)
.join(', ')
if (fieldErrors) return fieldErrors
}
// Generic message fallback
if (errorData.message) {
return errorData.message
}
} catch {
if (errorText) {
return `JSM Forms API error: ${errorText}`
return errorText
}
}
return `JSM Forms API error: ${status} ${statusText}`
return `${status} ${statusText}`
}