@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.
@@ -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) nextSchedule.updatedAt = options.now
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 entries: Array<[string, ScheduleLike]> = [[options.id, nextSchedule]]
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
- entries.push([relatedId, {
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: nextSchedule,
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
 
@@ -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
  }