@swarmclawai/swarmclaw 1.8.13 → 1.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/package.json +3 -3
- package/scripts/ensure-sandbox-browser-image.mjs +12 -2
- package/src/app/api/knowledge/hygiene/route.ts +19 -1
- package/src/app/api/portability/export/route.test.ts +17 -0
- package/src/app/api/portability/export/route.ts +11 -2
- package/src/app/api/tasks/task-workspace-route.test.ts +112 -0
- package/src/components/tasks/task-card.tsx +49 -1
- package/src/components/tasks/task-sheet.tsx +173 -1
- package/src/components/ui/info-chip.tsx +3 -2
- package/src/features/tasks/queries.ts +2 -1
- package/src/lib/server/agents/delegation-advisory.test.ts +1 -0
- package/src/lib/server/agents/delegation-advisory.ts +10 -0
- package/src/lib/server/chat-execution/iteration-event-handler.ts +24 -8
- package/src/lib/server/chat-execution/reasoning-tag-scrubber.test.ts +117 -0
- package/src/lib/server/chat-execution/reasoning-tag-scrubber.ts +219 -0
- package/src/lib/server/knowledge-sources.test.ts +45 -0
- package/src/lib/server/knowledge-sources.ts +33 -0
- package/src/lib/server/portability/export.ts +10 -0
- package/src/lib/server/session-tools/crud.ts +25 -2
- package/src/lib/server/session-tools/manage-tasks.test.ts +7 -2
- package/src/lib/server/tasks/task-execution-workspace.test.ts +117 -0
- package/src/lib/server/tasks/task-execution-workspace.ts +321 -0
- package/src/lib/server/tasks/task-route-service.ts +87 -9
- package/src/lib/server/tasks/task-service.test.ts +60 -2
- package/src/lib/server/tasks/task-service.ts +35 -0
- package/src/lib/tasks.ts +13 -5
- package/src/lib/validation/schemas.ts +19 -0
- package/src/types/misc.ts +1 -1
- package/src/types/task.ts +62 -0
|
@@ -20,6 +20,28 @@ const TASK_STATUS_VALUES = new Set([
|
|
|
20
20
|
'archived',
|
|
21
21
|
])
|
|
22
22
|
|
|
23
|
+
const ASSIGNMENT_START_WORKFLOW_STATES = new Set(['triage', 'backlog', 'todo'])
|
|
24
|
+
|
|
25
|
+
function hasOwn(record: Record<string, unknown>, key: string): boolean {
|
|
26
|
+
return Object.prototype.hasOwnProperty.call(record, key)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resolveAssignmentWorkflowStateTransition(params: {
|
|
30
|
+
previousAgentId?: string | null
|
|
31
|
+
nextAgentId?: string | null
|
|
32
|
+
previousWorkflowStateId?: string | null
|
|
33
|
+
explicitWorkflowState?: boolean
|
|
34
|
+
}): string | null {
|
|
35
|
+
if (params.explicitWorkflowState) return null
|
|
36
|
+
const previousAgentId = typeof params.previousAgentId === 'string' ? params.previousAgentId.trim() : ''
|
|
37
|
+
const nextAgentId = typeof params.nextAgentId === 'string' ? params.nextAgentId.trim() : ''
|
|
38
|
+
if (!nextAgentId || nextAgentId === previousAgentId) return null
|
|
39
|
+
const currentWorkflow = typeof params.previousWorkflowStateId === 'string' && params.previousWorkflowStateId.trim()
|
|
40
|
+
? params.previousWorkflowStateId.trim()
|
|
41
|
+
: 'backlog'
|
|
42
|
+
return ASSIGNMENT_START_WORKFLOW_STATES.has(currentWorkflow) ? 'in_progress' : null
|
|
43
|
+
}
|
|
44
|
+
|
|
23
45
|
export function deriveTaskTitle(input: { title?: unknown; description?: unknown }): string {
|
|
24
46
|
const explicit = typeof input.title === 'string' ? input.title.replace(/\s+/g, ' ').trim() : ''
|
|
25
47
|
if (explicit && !/^untitled task$/i.test(explicit)) return explicit.slice(0, 120)
|
|
@@ -157,6 +179,7 @@ export type PrepareTaskCreationResult =
|
|
|
157
179
|
|
|
158
180
|
export function prepareTaskCreation(options: PrepareTaskCreationOptions): PrepareTaskCreationResult {
|
|
159
181
|
const seed = options.seed ? { ...options.seed } : {}
|
|
182
|
+
delete seed.workType
|
|
160
183
|
const explicitTitle = typeof options.input.title === 'string' ? options.input.title.trim() : ''
|
|
161
184
|
const derivedTitle = deriveTaskTitle(options.input)
|
|
162
185
|
const nextTitle = options.deriveTitleFromDescription
|
|
@@ -194,6 +217,9 @@ export function prepareTaskCreation(options: PrepareTaskCreationOptions): Prepar
|
|
|
194
217
|
qualityGate,
|
|
195
218
|
},
|
|
196
219
|
})
|
|
220
|
+
if (!task.workflowStateId && task.agentId) {
|
|
221
|
+
task.workflowStateId = 'in_progress'
|
|
222
|
+
}
|
|
197
223
|
task.fingerprint = computeTaskFingerprint(task.title || 'Untitled Task', task.agentId || '')
|
|
198
224
|
|
|
199
225
|
const duplicate = task.fingerprint
|
|
@@ -227,6 +253,8 @@ export interface ApplyTaskPatchOptions {
|
|
|
227
253
|
|
|
228
254
|
export function applyTaskPatch(options: ApplyTaskPatchOptions): BoardTask {
|
|
229
255
|
const nextPatch = { ...options.patch }
|
|
256
|
+
const previousAgentId = options.task.agentId
|
|
257
|
+
const previousWorkflowStateId = options.task.workflowStateId || null
|
|
230
258
|
if (Object.prototype.hasOwnProperty.call(nextPatch, 'status')) {
|
|
231
259
|
const normalized = normalizeTaskStatusInput(nextPatch.status, options.task.status)
|
|
232
260
|
if (normalized) nextPatch.status = normalized
|
|
@@ -240,6 +268,13 @@ export function applyTaskPatch(options: ApplyTaskPatchOptions): BoardTask {
|
|
|
240
268
|
|
|
241
269
|
Object.assign(options.task, nextPatch, { updatedAt: options.now })
|
|
242
270
|
if (options.clearProjectIdWhenNull && nextPatch.projectId === null) delete options.task.projectId
|
|
271
|
+
const workflowTransition = resolveAssignmentWorkflowStateTransition({
|
|
272
|
+
previousAgentId,
|
|
273
|
+
nextAgentId: options.task.agentId,
|
|
274
|
+
previousWorkflowStateId,
|
|
275
|
+
explicitWorkflowState: hasOwn(nextPatch, 'workflowStateId'),
|
|
276
|
+
})
|
|
277
|
+
if (workflowTransition) options.task.workflowStateId = workflowTransition
|
|
243
278
|
|
|
244
279
|
if (options.task.status === 'completed') {
|
|
245
280
|
const { validation } = refreshTaskCompletionValidation(options.task, options.settings)
|
package/src/lib/tasks.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { api } from './app/api-client'
|
|
2
|
-
import type { BoardTask } from '../types'
|
|
2
|
+
import type { BoardTask, TaskComment, TaskPreviewLink, TaskRuntimeService } from '../types'
|
|
3
3
|
|
|
4
4
|
export const fetchTasks = (includeArchived = false) =>
|
|
5
5
|
api<Record<string, BoardTask>>('GET', `/tasks${includeArchived ? '?includeArchived=true' : ''}`)
|
|
@@ -29,16 +29,24 @@ export interface GitHubIssueImportResult {
|
|
|
29
29
|
skipped: GitHubIssueImportItem[]
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export
|
|
32
|
+
export type TaskWriteInput = Partial<BoardTask> & {
|
|
33
|
+
title?: string
|
|
34
|
+
description?: string
|
|
35
|
+
agentId?: string
|
|
36
|
+
provisionWorkspace?: boolean
|
|
37
|
+
previewLinks?: Array<Partial<TaskPreviewLink> & { url: string }>
|
|
38
|
+
runtimeServices?: Array<Partial<TaskRuntimeService>>
|
|
39
|
+
appendComment?: TaskComment
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const createTask = (data: TaskWriteInput & {
|
|
33
43
|
title: string
|
|
34
44
|
description: string
|
|
35
45
|
agentId: string
|
|
36
|
-
status?: string
|
|
37
|
-
qualityGate?: BoardTask['qualityGate']
|
|
38
46
|
}) =>
|
|
39
47
|
api<BoardTask>('POST', '/tasks', data)
|
|
40
48
|
|
|
41
|
-
export const updateTask = (id: string, data:
|
|
49
|
+
export const updateTask = (id: string, data: TaskWriteInput) =>
|
|
42
50
|
api<BoardTask>('PUT', `/tasks/${id}`, data)
|
|
43
51
|
|
|
44
52
|
export const deleteTask = (id: string) =>
|
|
@@ -200,6 +200,25 @@ export const TaskCreateSchema = z.object({
|
|
|
200
200
|
retryBackoffSec: z.number().optional(),
|
|
201
201
|
priority: z.enum(['low', 'medium', 'high', 'critical']).optional(),
|
|
202
202
|
dueAt: z.number().nullable().optional(),
|
|
203
|
+
workflowStateId: z.string().nullable().optional(),
|
|
204
|
+
requiredCapabilities: z.array(z.string()).optional(),
|
|
205
|
+
provisionWorkspace: z.boolean().optional(),
|
|
206
|
+
previewLinks: z.array(z.object({
|
|
207
|
+
id: z.string().optional(),
|
|
208
|
+
label: z.string().optional(),
|
|
209
|
+
url: z.string().min(1),
|
|
210
|
+
kind: z.enum(['web', 'api', 'docs', 'custom']).optional(),
|
|
211
|
+
port: z.number().nullable().optional(),
|
|
212
|
+
})).optional(),
|
|
213
|
+
runtimeServices: z.array(z.object({
|
|
214
|
+
id: z.string().optional(),
|
|
215
|
+
name: z.string().optional(),
|
|
216
|
+
status: z.enum(['planned', 'running', 'stopped', 'failed', 'unknown']).optional(),
|
|
217
|
+
command: z.string().nullable().optional(),
|
|
218
|
+
url: z.string().nullable().optional(),
|
|
219
|
+
port: z.number().nullable().optional(),
|
|
220
|
+
startedAt: z.number().nullable().optional(),
|
|
221
|
+
})).optional(),
|
|
203
222
|
qualityGate: z.object({
|
|
204
223
|
enabled: z.boolean().optional(),
|
|
205
224
|
minResultChars: z.number().optional(),
|
package/src/types/misc.ts
CHANGED
|
@@ -486,7 +486,7 @@ export interface MemoryEntry {
|
|
|
486
486
|
|
|
487
487
|
export type KnowledgeSourceKind = 'manual' | 'file' | 'url'
|
|
488
488
|
export type KnowledgeSyncStatus = 'ready' | 'syncing' | 'error'
|
|
489
|
-
export type KnowledgeHygieneActionKind = 'sync' | 'reindex' | 'archive' | 'restore' | 'supersede'
|
|
489
|
+
export type KnowledgeHygieneActionKind = 'sync' | 'reindex' | 'archive' | 'restore' | 'supersede' | 'prune'
|
|
490
490
|
export type KnowledgeHygieneFindingKind = 'stale' | 'duplicate' | 'overlap' | 'broken' | 'archived' | 'superseded'
|
|
491
491
|
|
|
492
492
|
export interface KnowledgeCitation {
|
package/src/types/task.ts
CHANGED
|
@@ -21,6 +21,64 @@ export interface TaskQualityGateConfig {
|
|
|
21
21
|
requireReport?: boolean
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export type TaskExecutionWorkspaceMode = 'task' | 'project' | 'custom'
|
|
25
|
+
|
|
26
|
+
export interface TaskPreviewLink {
|
|
27
|
+
id: string
|
|
28
|
+
label: string
|
|
29
|
+
url: string
|
|
30
|
+
kind: 'web' | 'api' | 'docs' | 'custom'
|
|
31
|
+
port?: number | null
|
|
32
|
+
addedAt: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface TaskRuntimeService {
|
|
36
|
+
id: string
|
|
37
|
+
name: string
|
|
38
|
+
status: 'planned' | 'running' | 'stopped' | 'failed' | 'unknown'
|
|
39
|
+
command?: string | null
|
|
40
|
+
url?: string | null
|
|
41
|
+
port?: number | null
|
|
42
|
+
startedAt?: number | null
|
|
43
|
+
updatedAt: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface TaskExecutionWorkspace {
|
|
47
|
+
path: string
|
|
48
|
+
mode: TaskExecutionWorkspaceMode
|
|
49
|
+
sourceCwd?: string | null
|
|
50
|
+
projectId?: string | null
|
|
51
|
+
preparedAt: number
|
|
52
|
+
preparedBy?: string | null
|
|
53
|
+
readmePath?: string | null
|
|
54
|
+
previewLinks: TaskPreviewLink[]
|
|
55
|
+
runtimeServices: TaskRuntimeService[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type TaskLivenessState =
|
|
59
|
+
| 'not_started'
|
|
60
|
+
| 'ready'
|
|
61
|
+
| 'queued'
|
|
62
|
+
| 'blocked'
|
|
63
|
+
| 'running'
|
|
64
|
+
| 'stale'
|
|
65
|
+
| 'retrying'
|
|
66
|
+
| 'dead_lettered'
|
|
67
|
+
| 'completed'
|
|
68
|
+
| 'failed'
|
|
69
|
+
| 'cancelled'
|
|
70
|
+
| 'archived'
|
|
71
|
+
|
|
72
|
+
export interface TaskLivenessSnapshot {
|
|
73
|
+
state: TaskLivenessState
|
|
74
|
+
reason: string
|
|
75
|
+
checkedAt: number
|
|
76
|
+
lastActivityAt?: number | null
|
|
77
|
+
nextWakeAt?: number | null
|
|
78
|
+
blockerTaskIds?: string[]
|
|
79
|
+
staleMs?: number | null
|
|
80
|
+
}
|
|
81
|
+
|
|
24
82
|
export interface BoardTask {
|
|
25
83
|
id: string
|
|
26
84
|
title: string
|
|
@@ -49,6 +107,10 @@ export interface BoardTask {
|
|
|
49
107
|
type: 'image' | 'video' | 'pdf' | 'file'
|
|
50
108
|
filename: string
|
|
51
109
|
}>
|
|
110
|
+
executionWorkspace?: TaskExecutionWorkspace | null
|
|
111
|
+
previewLinks?: TaskPreviewLink[]
|
|
112
|
+
runtimeServices?: TaskRuntimeService[]
|
|
113
|
+
liveness?: TaskLivenessSnapshot | null
|
|
52
114
|
comments?: TaskComment[]
|
|
53
115
|
images?: string[]
|
|
54
116
|
createdByAgentId?: string | null
|