@swarmclawai/swarmclaw 1.5.48 → 1.5.49

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.
@@ -355,6 +355,135 @@ function normalizeStoredMissionEventRecord(value: unknown): unknown {
355
355
  return event
356
356
  }
357
357
 
358
+ // --- Agent Mission normalizers (autonomous goal-driven runs, v1.5.49+) ---
359
+
360
+ const VALID_AGENT_MISSION_STATUSES = new Set([
361
+ 'draft',
362
+ 'running',
363
+ 'paused',
364
+ 'completed',
365
+ 'failed',
366
+ 'cancelled',
367
+ 'budget_exhausted',
368
+ ])
369
+
370
+ const VALID_AGENT_MISSION_REPORT_FORMATS = new Set(['markdown', 'slack', 'discord', 'email', 'audio'])
371
+
372
+ function normalizeFiniteNumber(value: unknown): number | null {
373
+ if (typeof value !== 'number' || !Number.isFinite(value)) return null
374
+ return value
375
+ }
376
+
377
+ function normalizeNonNegativeNumber(value: unknown, fallback: number): number {
378
+ const n = normalizeFiniteNumber(value)
379
+ if (n == null || n < 0) return fallback
380
+ return n
381
+ }
382
+
383
+ function normalizeStoredAgentMissionRecord(value: unknown): unknown {
384
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return value
385
+ const mission = value as StoredObject
386
+
387
+ const status = typeof mission.status === 'string' ? mission.status.trim().toLowerCase() : ''
388
+ mission.status = VALID_AGENT_MISSION_STATUSES.has(status) ? status : 'draft'
389
+
390
+ mission.successCriteria = normalizeStoredStringArray(mission.successCriteria, 64)
391
+ mission.agentIds = normalizeStoredStringArray(mission.agentIds, 32)
392
+ mission.reportConnectorIds = normalizeStoredStringArray(mission.reportConnectorIds, 16)
393
+
394
+ const budget = mission.budget && typeof mission.budget === 'object' && !Array.isArray(mission.budget)
395
+ ? mission.budget as StoredObject
396
+ : {}
397
+ budget.maxUsd = normalizeFiniteNumber(budget.maxUsd)
398
+ budget.maxTokens = normalizeFiniteNumber(budget.maxTokens)
399
+ budget.maxToolCalls = normalizeFiniteNumber(budget.maxToolCalls)
400
+ budget.maxWallclockSec = normalizeFiniteNumber(budget.maxWallclockSec)
401
+ budget.maxTurns = normalizeFiniteNumber(budget.maxTurns)
402
+ if (!Array.isArray(budget.warnAtFractions)) {
403
+ budget.warnAtFractions = [0.5, 0.8, 0.95]
404
+ } else {
405
+ budget.warnAtFractions = (budget.warnAtFractions as unknown[])
406
+ .map((entry) => normalizeFiniteNumber(entry))
407
+ .filter((entry): entry is number => entry != null && entry > 0 && entry < 1)
408
+ if ((budget.warnAtFractions as number[]).length === 0) {
409
+ budget.warnAtFractions = [0.5, 0.8, 0.95]
410
+ }
411
+ }
412
+ mission.budget = budget
413
+
414
+ const usage = mission.usage && typeof mission.usage === 'object' && !Array.isArray(mission.usage)
415
+ ? mission.usage as StoredObject
416
+ : {}
417
+ usage.usdSpent = normalizeNonNegativeNumber(usage.usdSpent, 0)
418
+ usage.tokensUsed = normalizeNonNegativeNumber(usage.tokensUsed, 0)
419
+ usage.toolCallsUsed = normalizeNonNegativeNumber(usage.toolCallsUsed, 0)
420
+ usage.turnsRun = normalizeNonNegativeNumber(usage.turnsRun, 0)
421
+ usage.wallclockMsElapsed = normalizeNonNegativeNumber(usage.wallclockMsElapsed, 0)
422
+ usage.startedAt = normalizeFiniteNumber(usage.startedAt)
423
+ usage.lastUpdatedAt = normalizeNonNegativeNumber(usage.lastUpdatedAt, 0)
424
+ if (!Array.isArray(usage.warnFractionsHit)) {
425
+ usage.warnFractionsHit = []
426
+ } else {
427
+ usage.warnFractionsHit = (usage.warnFractionsHit as unknown[])
428
+ .map((entry) => normalizeFiniteNumber(entry))
429
+ .filter((entry): entry is number => entry != null)
430
+ }
431
+ mission.usage = usage
432
+
433
+ if (!Array.isArray(mission.milestones)) mission.milestones = []
434
+ // Cap the stored tail so missions don't balloon
435
+ if ((mission.milestones as unknown[]).length > 200) {
436
+ mission.milestones = (mission.milestones as unknown[]).slice(-200)
437
+ }
438
+
439
+ const reportSchedule = mission.reportSchedule
440
+ && typeof mission.reportSchedule === 'object'
441
+ && !Array.isArray(mission.reportSchedule)
442
+ ? mission.reportSchedule as StoredObject
443
+ : null
444
+ if (reportSchedule) {
445
+ const format = typeof reportSchedule.format === 'string' ? reportSchedule.format.trim().toLowerCase() : ''
446
+ reportSchedule.format = VALID_AGENT_MISSION_REPORT_FORMATS.has(format) ? format : 'markdown'
447
+ reportSchedule.intervalSec = normalizeNonNegativeNumber(reportSchedule.intervalSec, 3600)
448
+ reportSchedule.enabled = reportSchedule.enabled !== false
449
+ reportSchedule.lastReportAt = normalizeFiniteNumber(reportSchedule.lastReportAt)
450
+ mission.reportSchedule = reportSchedule
451
+ } else if (mission.reportSchedule !== undefined) {
452
+ mission.reportSchedule = null
453
+ }
454
+
455
+ if (typeof mission.createdAt !== 'number') mission.createdAt = Date.now()
456
+ if (typeof mission.updatedAt !== 'number') mission.updatedAt = mission.createdAt as number
457
+ if (mission.startedAt === undefined) mission.startedAt = null
458
+ if (mission.endedAt === undefined) mission.endedAt = null
459
+ if (mission.endReason === undefined) mission.endReason = null
460
+
461
+ return mission
462
+ }
463
+
464
+ function normalizeStoredMissionReportRecord(value: unknown): unknown {
465
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return value
466
+ const report = value as StoredObject
467
+ const format = typeof report.format === 'string' ? report.format.trim().toLowerCase() : ''
468
+ report.format = VALID_AGENT_MISSION_REPORT_FORMATS.has(format) ? format : 'markdown'
469
+ if (!Array.isArray(report.highlights)) report.highlights = []
470
+ if (!Array.isArray(report.deliveredTo)) report.deliveredTo = []
471
+ if (typeof report.body !== 'string') report.body = ''
472
+ if (typeof report.title !== 'string') report.title = 'Mission report'
473
+ return report
474
+ }
475
+
476
+ function normalizeStoredAgentMissionEventRecord(value: unknown): unknown {
477
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return value
478
+ const event = value as StoredObject
479
+ if (!event.payload || typeof event.payload !== 'object' || Array.isArray(event.payload)) {
480
+ event.payload = {}
481
+ }
482
+ if (typeof event.kind !== 'string' || !event.kind.trim()) event.kind = 'unknown'
483
+ if (typeof event.at !== 'number' || !Number.isFinite(event.at)) event.at = Date.now()
484
+ return event
485
+ }
486
+
358
487
  // --- Delegation job normalizer ---
359
488
 
360
489
  function normalizeStoredDelegationJobRecord(value: unknown): unknown {
@@ -428,7 +557,7 @@ export function normalizeStoredRecord(
428
557
  value: unknown,
429
558
  loadItem: CollectionItemLoader,
430
559
  ): NormalizationResult {
431
- // Tables with no normalization early exit
560
+ // Tables with no normalization, early exit.
432
561
  if (
433
562
  table !== 'agents' && table !== 'tasks' && table !== 'missions'
434
563
  && table !== 'mission_events' && table !== 'delegation_jobs'
@@ -436,6 +565,9 @@ export function normalizeStoredRecord(
436
565
  && table !== 'provider_configs'
437
566
  && table !== 'runtime_runs' && table !== 'runtime_run_events'
438
567
  && table !== 'wallets'
568
+ && table !== 'agent_missions'
569
+ && table !== 'mission_reports'
570
+ && table !== 'agent_mission_events'
439
571
  ) {
440
572
  return { value, changed: false }
441
573
  }
@@ -590,6 +722,18 @@ function normalizeStoredRecordInner(
590
722
  return normalizeStoredMissionEventRecord(value)
591
723
  }
592
724
 
725
+ if (table === 'agent_missions') {
726
+ return normalizeStoredAgentMissionRecord(value)
727
+ }
728
+
729
+ if (table === 'mission_reports') {
730
+ return normalizeStoredMissionReportRecord(value)
731
+ }
732
+
733
+ if (table === 'agent_mission_events') {
734
+ return normalizeStoredAgentMissionEventRecord(value)
735
+ }
736
+
593
737
  if (table === 'delegation_jobs') {
594
738
  return normalizeStoredDelegationJobRecord(value)
595
739
  }
@@ -26,6 +26,9 @@ import type {
26
26
  KnowledgeSource,
27
27
  LearnedSkill,
28
28
  Message,
29
+ Mission,
30
+ MissionEvent,
31
+ MissionReport,
29
32
  ProtocolTemplate,
30
33
  ProtocolRun,
31
34
  ProtocolRunEvent,
@@ -176,6 +179,9 @@ const COLLECTIONS = [
176
179
  'wallets',
177
180
  'wallet_transactions',
178
181
  'goals',
182
+ 'agent_missions',
183
+ 'mission_reports',
184
+ 'agent_mission_events',
179
185
  ] as const
180
186
 
181
187
  export type StorageCollection = (typeof COLLECTIONS)[number]
@@ -1686,6 +1692,29 @@ export const loadGoal = goalsStore.loadItem
1686
1692
  export const upsertGoal = goalsStore.upsert
1687
1693
  export const deleteGoalItem = goalsStore.deleteItem
1688
1694
 
1695
+ // --- Agent Missions (autonomous goal-driven runs) ---
1696
+ const agentMissionsStore = createCollectionStore<Mission>('agent_missions', { ttlMs: 5_000 })
1697
+ export const loadAgentMissions = agentMissionsStore.load
1698
+ export const saveAgentMissions = agentMissionsStore.save
1699
+ export const loadAgentMission = agentMissionsStore.loadItem
1700
+ export const upsertAgentMission = agentMissionsStore.upsert
1701
+ export const patchAgentMission = agentMissionsStore.patch
1702
+ export const deleteAgentMission = agentMissionsStore.deleteItem
1703
+
1704
+ const missionReportsStore = createCollectionStore<MissionReport>('mission_reports')
1705
+ export const loadMissionReports = missionReportsStore.load
1706
+ export const saveMissionReports = missionReportsStore.save
1707
+ export const loadMissionReport = missionReportsStore.loadItem
1708
+ export const upsertMissionReport = missionReportsStore.upsert
1709
+ export const deleteMissionReport = missionReportsStore.deleteItem
1710
+
1711
+ const agentMissionEventsStore = createCollectionStore<MissionEvent>('agent_mission_events')
1712
+ export const loadAgentMissionEvents = agentMissionEventsStore.load
1713
+ export const saveAgentMissionEvents = agentMissionEventsStore.save
1714
+ export const loadAgentMissionEvent = agentMissionEventsStore.loadItem
1715
+ export const upsertAgentMissionEvent = agentMissionEventsStore.upsert
1716
+ export const deleteAgentMissionEvent = agentMissionEventsStore.deleteItem
1717
+
1689
1718
  function legacyMissionStatusToWorkingStatus(value: unknown): 'idle' | 'progress' | 'blocked' | 'completed' {
1690
1719
  const normalized = typeof value === 'string' ? value.trim().toLowerCase() : ''
1691
1720
  if (normalized === 'achieved' || normalized === 'completed' || normalized === 'ok') return 'completed'
@@ -14,6 +14,7 @@ export * from './run'
14
14
  export * from './approval'
15
15
  export * from './misc'
16
16
  export * from './goal'
17
+ export * from './mission'
17
18
  export * from './swarmdock'
18
19
  export * from './dream'
19
20
  export * from './swarmfeed'
@@ -0,0 +1,115 @@
1
+ export type MissionStatus =
2
+ | 'draft'
3
+ | 'running'
4
+ | 'paused'
5
+ | 'completed'
6
+ | 'failed'
7
+ | 'cancelled'
8
+ | 'budget_exhausted'
9
+
10
+ export type MissionReportFormat = 'markdown' | 'slack' | 'discord' | 'email' | 'audio'
11
+
12
+ export type MissionMilestoneKind =
13
+ | 'started'
14
+ | 'budget_warn'
15
+ | 'budget_hit'
16
+ | 'check_in'
17
+ | 'subgoal_done'
18
+ | 'report_sent'
19
+ | 'paused'
20
+ | 'resumed'
21
+ | 'completed'
22
+ | 'failed'
23
+ | 'cancelled'
24
+
25
+ export interface MissionBudget {
26
+ maxUsd?: number | null
27
+ maxTokens?: number | null
28
+ maxToolCalls?: number | null
29
+ maxWallclockSec?: number | null
30
+ maxTurns?: number | null
31
+ warnAtFractions?: number[]
32
+ }
33
+
34
+ export interface MissionUsage {
35
+ usdSpent: number
36
+ tokensUsed: number
37
+ toolCallsUsed: number
38
+ turnsRun: number
39
+ wallclockMsElapsed: number
40
+ startedAt: number | null
41
+ lastUpdatedAt: number
42
+ warnFractionsHit: number[]
43
+ }
44
+
45
+ export interface MissionMilestone {
46
+ id: string
47
+ at: number
48
+ kind: MissionMilestoneKind
49
+ summary: string
50
+ evidence?: string[]
51
+ sessionId?: string | null
52
+ runId?: string | null
53
+ }
54
+
55
+ export interface MissionReportSchedule {
56
+ intervalSec: number
57
+ format: MissionReportFormat
58
+ enabled: boolean
59
+ lastReportAt?: number | null
60
+ }
61
+
62
+ export interface MissionReportDelivery {
63
+ connectorId?: string | null
64
+ channelId?: string | null
65
+ deliveredAt: number
66
+ status: 'ok' | 'error'
67
+ error?: string | null
68
+ }
69
+
70
+ export interface MissionReport {
71
+ id: string
72
+ missionId: string
73
+ generatedAt: number
74
+ format: MissionReportFormat
75
+ fromAt: number
76
+ toAt: number
77
+ title: string
78
+ body: string
79
+ audioUrl?: string | null
80
+ deliveredTo: MissionReportDelivery[]
81
+ highlights: Array<{ kind: string; summary: string; evidenceRunId?: string | null }>
82
+ }
83
+
84
+ export interface MissionEvent {
85
+ id: string
86
+ missionId: string
87
+ at: number
88
+ kind: string
89
+ payload: Record<string, unknown>
90
+ sessionId?: string | null
91
+ runId?: string | null
92
+ }
93
+
94
+ export interface Mission {
95
+ id: string
96
+ title: string
97
+ goal: string
98
+ successCriteria: string[]
99
+ rootSessionId: string
100
+ agentIds: string[]
101
+ status: MissionStatus
102
+ budget: MissionBudget
103
+ usage: MissionUsage
104
+ milestones: MissionMilestone[]
105
+ reportSchedule?: MissionReportSchedule | null
106
+ reportConnectorIds: string[]
107
+ createdAt: number
108
+ updatedAt: number
109
+ startedAt?: number | null
110
+ endedAt?: number | null
111
+ endReason?: string | null
112
+ }
113
+
114
+ export const DEFAULT_MISSION_WARN_FRACTIONS = [0.5, 0.8, 0.95]
115
+ export const MISSION_MILESTONE_TAIL_CAP = 200
@@ -173,6 +173,8 @@ export interface Session {
173
173
  file?: string | null
174
174
  queuedCount?: number
175
175
  currentRunId?: string | null
176
+ /** Optional link to an autonomous Mission that drives this session. */
177
+ missionId?: string | null
176
178
  conversationTone?: string
177
179
  emoji?: string
178
180
  creature?: string
@@ -227,4 +229,4 @@ export type SessionTool =
227
229
  | 'crawl'
228
230
 
229
231
  export type SessionType = 'human'
230
- export type AppView = 'home' | 'agents' | 'org_chart' | 'inbox' | 'chatrooms' | 'protocols' | 'schedules' | 'memory' | 'tasks' | 'secrets' | 'wallets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'extensions' | 'usage' | 'runs' | 'autonomy' | 'logs' | 'settings' | 'projects' | 'activity' | 'swarmfeed' | 'marketplace'
232
+ export type AppView = 'home' | 'agents' | 'org_chart' | 'inbox' | 'chatrooms' | 'protocols' | 'schedules' | 'memory' | 'tasks' | 'secrets' | 'wallets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'extensions' | 'usage' | 'runs' | 'autonomy' | 'logs' | 'settings' | 'projects' | 'activity' | 'swarmfeed' | 'marketplace' | 'missions'