@swarmclawai/swarmclaw 1.9.7 → 1.9.9
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 +74 -0
- package/package.json +2 -2
- package/src/app/api/quality/release-readiness/route.ts +38 -0
- package/src/app/api/schedules/[id]/history/route.ts +15 -0
- package/src/app/api/schedules/[id]/route.test.ts +52 -3
- package/src/app/api/schedules/schedule-history-route.test.ts +60 -0
- package/src/cli/index.js +2 -0
- package/src/cli/index.ts +8 -0
- package/src/cli/spec.js +1 -0
- package/src/components/quality/quality-workspace.tsx +164 -4
- package/src/components/schedules/schedule-console.tsx +173 -15
- package/src/lib/quality/release-readiness.test.ts +129 -0
- package/src/lib/quality/release-readiness.ts +187 -0
- package/src/lib/schedules/schedules.ts +10 -1
- package/src/lib/server/runtime/scheduler.ts +52 -20
- package/src/lib/server/schedules/schedule-history.test.ts +121 -0
- package/src/lib/server/schedules/schedule-history.ts +234 -0
- package/src/lib/server/schedules/schedule-lifecycle.ts +34 -2
- package/src/lib/server/schedules/schedule-route-service.ts +11 -1
- package/src/lib/server/schedules/schedule-service.ts +39 -7
- package/src/lib/server/session-tools/crud.ts +2 -0
- package/src/lib/server/storage-normalization.ts +15 -0
- package/src/types/schedule.ts +22 -0
|
@@ -9,6 +9,11 @@ import type { Schedule, ScheduleStatus } from '@/types'
|
|
|
9
9
|
import { dedup } from '@/lib/shared-utils'
|
|
10
10
|
|
|
11
11
|
import { normalizeSchedulePayload } from '@/lib/server/schedules/schedule-normalization'
|
|
12
|
+
import {
|
|
13
|
+
applyScheduleCreationHistory,
|
|
14
|
+
applyScheduleUpdateHistory,
|
|
15
|
+
type ScheduleHistoryActor,
|
|
16
|
+
} from '@/lib/server/schedules/schedule-history'
|
|
12
17
|
|
|
13
18
|
export interface ScheduleCreatorScope {
|
|
14
19
|
agentId?: string | null
|
|
@@ -91,6 +96,7 @@ export interface PrepareScheduleCreateOptions {
|
|
|
91
96
|
dedupeCreatorScope?: ScheduleCreatorScope | null
|
|
92
97
|
followupTarget?: Partial<Schedule>
|
|
93
98
|
createId?: () => string
|
|
99
|
+
historyActor?: ScheduleHistoryActor | null
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
export type PrepareScheduleCreateResult =
|
|
@@ -150,7 +156,16 @@ export function prepareScheduleCreate(options: PrepareScheduleCreateOptions): Pr
|
|
|
150
156
|
nextSchedule.status = nextStatus
|
|
151
157
|
changed = true
|
|
152
158
|
}
|
|
153
|
-
if (changed)
|
|
159
|
+
if (changed) {
|
|
160
|
+
nextSchedule.updatedAt = options.now
|
|
161
|
+
const withHistory = applyScheduleUpdateHistory(duplicate as Schedule, nextSchedule as Schedule, {
|
|
162
|
+
now: options.now,
|
|
163
|
+
actor: options.historyActor?.actor || 'system',
|
|
164
|
+
actorId: options.historyActor?.actorId || null,
|
|
165
|
+
summary: `Schedule updated from duplicate create request: "${nextSchedule.name}"`,
|
|
166
|
+
})
|
|
167
|
+
Object.assign(nextSchedule, withHistory)
|
|
168
|
+
}
|
|
154
169
|
return {
|
|
155
170
|
ok: true,
|
|
156
171
|
kind: 'duplicate',
|
|
@@ -168,7 +183,7 @@ export function prepareScheduleCreate(options: PrepareScheduleCreateOptions): Pr
|
|
|
168
183
|
createdInSessionId: options.creatorScope.sessionId || null,
|
|
169
184
|
}
|
|
170
185
|
: {}
|
|
171
|
-
const schedule = {
|
|
186
|
+
const schedule = applyScheduleCreationHistory({
|
|
172
187
|
id,
|
|
173
188
|
...candidate,
|
|
174
189
|
...creatorFields,
|
|
@@ -178,7 +193,11 @@ export function prepareScheduleCreate(options: PrepareScheduleCreateOptions): Pr
|
|
|
178
193
|
lastRunAt: undefined,
|
|
179
194
|
createdAt: options.now,
|
|
180
195
|
updatedAt: options.now,
|
|
181
|
-
} as Schedule
|
|
196
|
+
} as Schedule, {
|
|
197
|
+
now: options.now,
|
|
198
|
+
actor: options.historyActor?.actor || 'system',
|
|
199
|
+
actorId: options.historyActor?.actorId || null,
|
|
200
|
+
})
|
|
182
201
|
|
|
183
202
|
return {
|
|
184
203
|
ok: true,
|
|
@@ -199,6 +218,7 @@ export interface PrepareScheduleUpdateOptions {
|
|
|
199
218
|
agentExists?: (agentId: string) => boolean
|
|
200
219
|
propagateEquivalentStatuses?: boolean
|
|
201
220
|
propagationSource?: Record<string, unknown> | null
|
|
221
|
+
historyActor?: ScheduleHistoryActor | null
|
|
202
222
|
}
|
|
203
223
|
|
|
204
224
|
export type PrepareScheduleUpdateResult =
|
|
@@ -243,7 +263,13 @@ export function prepareScheduleUpdate(options: PrepareScheduleUpdateOptions): Pr
|
|
|
243
263
|
taskPrompt: nextSchedule.taskPrompt,
|
|
244
264
|
})
|
|
245
265
|
|
|
246
|
-
const
|
|
266
|
+
const scheduleWithHistory = applyScheduleUpdateHistory(options.current as Schedule, nextSchedule as Schedule, {
|
|
267
|
+
now: options.now,
|
|
268
|
+
actor: options.historyActor?.actor || 'system',
|
|
269
|
+
actorId: options.historyActor?.actorId || null,
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
const entries: Array<[string, ScheduleLike]> = [[options.id, scheduleWithHistory]]
|
|
247
273
|
const normalizedStatus = normalizeScheduleStatus(nextSchedule.status)
|
|
248
274
|
if (options.propagateEquivalentStatuses && (normalizedStatus === 'paused' || normalizedStatus === 'completed' || normalizedStatus === 'failed' || normalizedStatus === 'archived')) {
|
|
249
275
|
const relatedIds = findRelatedScheduleIds(
|
|
@@ -254,17 +280,23 @@ export function prepareScheduleUpdate(options: PrepareScheduleUpdateOptions): Pr
|
|
|
254
280
|
for (const relatedId of relatedIds) {
|
|
255
281
|
const related = options.schedules[relatedId]
|
|
256
282
|
if (!related) continue
|
|
257
|
-
|
|
283
|
+
const relatedNext = {
|
|
258
284
|
...related,
|
|
259
285
|
status: normalizedStatus,
|
|
260
286
|
updatedAt: options.now,
|
|
261
|
-
}
|
|
287
|
+
} as Schedule
|
|
288
|
+
entries.push([relatedId, applyScheduleUpdateHistory(related as Schedule, relatedNext, {
|
|
289
|
+
now: options.now,
|
|
290
|
+
actor: options.historyActor?.actor || 'system',
|
|
291
|
+
actorId: options.historyActor?.actorId || null,
|
|
292
|
+
summary: `Schedule status updated with related schedule: "${scheduleWithHistory.name}"`,
|
|
293
|
+
})])
|
|
262
294
|
}
|
|
263
295
|
}
|
|
264
296
|
|
|
265
297
|
return {
|
|
266
298
|
ok: true,
|
|
267
|
-
schedule:
|
|
299
|
+
schedule: scheduleWithHistory,
|
|
268
300
|
entries,
|
|
269
301
|
affectedScheduleIds: dedup(entries.map(([id]) => id)),
|
|
270
302
|
}
|
|
@@ -672,6 +672,7 @@ export function buildCrudTools(bctx: ToolBuildContext): StructuredToolInterface[
|
|
|
672
672
|
sessionId: ctx?.sessionId || null,
|
|
673
673
|
},
|
|
674
674
|
followupTarget: deriveScheduleFollowupTarget(ctx?.sessionId || null),
|
|
675
|
+
historyActor: { actor: ctx?.agentId ? 'agent' : 'user', actorId: ctx?.agentId || null },
|
|
675
676
|
})
|
|
676
677
|
if (!prepared.ok) return prepared.error
|
|
677
678
|
if (prepared.kind === 'duplicate') {
|
|
@@ -880,6 +881,7 @@ export function buildCrudTools(bctx: ToolBuildContext): StructuredToolInterface[
|
|
|
880
881
|
agentExists: (agentId) => Boolean(managedAgents?.[agentId]),
|
|
881
882
|
propagateEquivalentStatuses: true,
|
|
882
883
|
propagationSource: previousEntry as Record<string, unknown>,
|
|
884
|
+
historyActor: { actor: ctx?.agentId ? 'agent' : 'user', actorId: ctx?.agentId || null },
|
|
883
885
|
})
|
|
884
886
|
if (!prepared.ok) return prepared.error
|
|
885
887
|
for (const [scheduleId, schedule] of prepared.entries) {
|
|
@@ -2,6 +2,7 @@ import { normalizeCapabilitySelection } from '@/lib/capability-selection'
|
|
|
2
2
|
import { normalizeAgentSandboxConfig } from '@/lib/agent-sandbox-defaults'
|
|
3
3
|
import { isDirectConnectorSession } from '@/lib/server/connectors/session-kind'
|
|
4
4
|
import { WORKER_ONLY_PROVIDER_IDS } from '@/lib/provider-sets'
|
|
5
|
+
import { normalizeScheduleHistory } from '@/lib/server/schedules/schedule-history'
|
|
5
6
|
|
|
6
7
|
type StoredObject = Record<string, unknown>
|
|
7
8
|
|
|
@@ -204,6 +205,20 @@ function normalizeStoredScheduleRecord(value: unknown, loadItem: CollectionItemL
|
|
|
204
205
|
delete schedule.followupThreadId
|
|
205
206
|
}
|
|
206
207
|
|
|
208
|
+
const history = normalizeScheduleHistory(schedule.history)
|
|
209
|
+
if (history.length > 0) {
|
|
210
|
+
schedule.history = history
|
|
211
|
+
const revision = typeof schedule.revision === 'number' && Number.isFinite(schedule.revision)
|
|
212
|
+
? Math.trunc(schedule.revision)
|
|
213
|
+
: 0
|
|
214
|
+
schedule.revision = Math.max(revision, ...history.map((entry) => entry.revision))
|
|
215
|
+
} else {
|
|
216
|
+
delete schedule.history
|
|
217
|
+
if (typeof schedule.revision !== 'number' || !Number.isFinite(schedule.revision) || schedule.revision <= 0) {
|
|
218
|
+
delete schedule.revision
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
207
222
|
return schedule
|
|
208
223
|
}
|
|
209
224
|
|
package/src/types/schedule.ts
CHANGED
|
@@ -3,6 +3,26 @@ import type { ExtensionManagedResourceMarker } from './extension'
|
|
|
3
3
|
export type ScheduleType = 'cron' | 'interval' | 'once'
|
|
4
4
|
export type ScheduleStatus = 'active' | 'paused' | 'completed' | 'failed' | 'archived'
|
|
5
5
|
export type ScheduleTaskMode = 'task' | 'wake_only' | 'protocol'
|
|
6
|
+
export type ScheduleHistoryAction = 'created' | 'updated' | 'archived' | 'restored' | 'run_started' | 'skipped' | 'failed'
|
|
7
|
+
|
|
8
|
+
export interface ScheduleHistoryChange {
|
|
9
|
+
field: string
|
|
10
|
+
label: string
|
|
11
|
+
before: string | null
|
|
12
|
+
after: string | null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ScheduleHistoryEntry {
|
|
16
|
+
id: string
|
|
17
|
+
at: number
|
|
18
|
+
actor: string
|
|
19
|
+
actorId?: string | null
|
|
20
|
+
action: ScheduleHistoryAction
|
|
21
|
+
revision: number
|
|
22
|
+
summary: string
|
|
23
|
+
changes?: ScheduleHistoryChange[]
|
|
24
|
+
metadata?: Record<string, string | number | boolean | null>
|
|
25
|
+
}
|
|
6
26
|
|
|
7
27
|
export interface Schedule {
|
|
8
28
|
id: string
|
|
@@ -57,6 +77,8 @@ export interface Schedule {
|
|
|
57
77
|
followupSenderId?: string | null
|
|
58
78
|
followupSenderName?: string | null
|
|
59
79
|
managedByExtension?: ExtensionManagedResourceMarker | null
|
|
80
|
+
revision?: number
|
|
81
|
+
history?: ScheduleHistoryEntry[]
|
|
60
82
|
createdAt: number
|
|
61
83
|
updatedAt?: number
|
|
62
84
|
}
|