@swarmclawai/swarmclaw 1.2.8 → 1.3.0
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 +39 -6
- package/package.json +2 -2
- package/src/app/agents/[id]/page.tsx +1 -18
- package/src/app/api/activity/route.ts +9 -23
- package/src/app/api/agents/route.ts +17 -1
- package/src/app/api/agents/thread-route.test.ts +0 -1
- package/src/app/api/approvals/route.test.ts +6 -22
- package/src/app/api/approvals/route.ts +13 -5
- package/src/app/api/connectors/route.ts +2 -2
- package/src/app/api/credentials/[id]/route.ts +2 -0
- package/src/app/api/credentials/route.ts +4 -1
- package/src/app/api/goals/[id]/route.ts +28 -0
- package/src/app/api/goals/route.ts +33 -0
- package/src/app/api/portability/export/route.ts +8 -0
- package/src/app/api/portability/import/route.test.ts +80 -0
- package/src/app/api/portability/import/route.ts +28 -0
- package/src/app/api/protocols/templates/[id]/route.ts +2 -1
- package/src/app/api/protocols/templates/route.ts +2 -1
- package/src/app/api/settings/route.ts +13 -2
- package/src/app/api/wallets/[id]/route.ts +15 -157
- package/src/app/api/wallets/generate/route.ts +22 -0
- package/src/app/api/wallets/route.test.ts +147 -0
- package/src/app/api/wallets/route.ts +13 -95
- package/src/app/autonomy/page.tsx +2 -57
- package/src/app/home/page.tsx +3 -0
- package/src/app/protocols/page.tsx +2 -21
- package/src/app/settings/page.tsx +0 -9
- package/src/app/wallets/page.tsx +105 -5
- package/src/cli/index.js +32 -33
- package/src/cli/spec.js +26 -27
- package/src/components/agents/agent-sheet.tsx +2 -40
- package/src/components/agents/inspector-panel.tsx +0 -83
- package/src/components/chat/chat-card.tsx +0 -31
- package/src/components/chat/message-bubble.tsx +1 -108
- package/src/components/connectors/connector-sheet.tsx +25 -1
- package/src/components/layout/sidebar-rail.tsx +6 -10
- package/src/components/projects/project-detail.tsx +3 -35
- package/src/components/projects/tabs/overview-tab.tsx +3 -59
- package/src/components/projects/tabs/work-tab.tsx +7 -77
- package/src/components/protocols/structured-session-launcher.tsx +1 -22
- package/src/components/shared/connector-platform-icon.tsx +1 -0
- package/src/components/tasks/task-card.tsx +4 -34
- package/src/components/tasks/task-sheet.tsx +6 -36
- package/src/components/wallets/wallet-list.tsx +150 -0
- package/src/lib/app/navigation.test.ts +0 -13
- package/src/lib/app/navigation.ts +2 -7
- package/src/lib/app/view-constants.ts +14 -19
- package/src/lib/server/activity/activity-log.ts +16 -1
- package/src/lib/server/agents/agent-service.ts +24 -11
- package/src/lib/server/agents/agent-thread-session.ts +0 -1
- package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
- package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
- package/src/lib/server/agents/delegation-jobs.ts +0 -25
- package/src/lib/server/agents/main-agent-loop.ts +1 -49
- package/src/lib/server/agents/subagent-runtime.ts +0 -1
- package/src/lib/server/approval-match.ts +14 -85
- package/src/lib/server/approvals/approval-hooks.ts +81 -0
- package/src/lib/server/approvals.test.ts +6 -6
- package/src/lib/server/approvals.ts +11 -6
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
- package/src/lib/server/builtin-extensions.ts +0 -2
- package/src/lib/server/capability-router.test.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +14 -14
- package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -2
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +2 -22
- package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
- package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
- package/src/lib/server/chat-execution/message-classifier.ts +1 -16
- package/src/lib/server/chat-execution/prompt-builder.test.ts +0 -1
- package/src/lib/server/chat-execution/prompt-builder.ts +0 -30
- package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
- package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
- package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +8 -123
- package/src/lib/server/chat-execution/stream-agent-chat.ts +1 -5
- package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
- package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
- package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
- package/src/lib/server/chats/chat-session-service.ts +3 -5
- package/src/lib/server/connectors/connector-inbound.ts +0 -1
- package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
- package/src/lib/server/connectors/connector-service.ts +39 -9
- package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
- package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
- package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
- package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
- package/src/lib/server/connectors/swarmdock-tasks.ts +127 -0
- package/src/lib/server/connectors/swarmdock.ts +285 -0
- package/src/lib/server/execution-brief.test.ts +2 -25
- package/src/lib/server/execution-brief.ts +30 -35
- package/src/lib/server/execution-engine/task-attempt.ts +0 -1
- package/src/lib/server/goals/goal-repository.ts +19 -0
- package/src/lib/server/goals/goal-service.ts +143 -0
- package/src/lib/server/persistence/storage-context.ts +0 -5
- package/src/lib/server/portability/export.ts +109 -0
- package/src/lib/server/portability/import.ts +159 -0
- package/src/lib/server/protocols/protocol-normalization.ts +0 -4
- package/src/lib/server/protocols/protocol-queries.ts +0 -6
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
- package/src/lib/server/protocols/protocol-service.ts +0 -1
- package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
- package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
- package/src/lib/server/protocols/protocol-swarm.ts +0 -2
- package/src/lib/server/protocols/protocol-types.ts +0 -2
- package/src/lib/server/provider-health.ts +0 -9
- package/src/lib/server/runtime/daemon-state/core.ts +0 -9
- package/src/lib/server/runtime/daemon-state.test.ts +0 -35
- package/src/lib/server/runtime/heartbeat-service.ts +3 -23
- package/src/lib/server/runtime/queue/core.ts +11 -33
- package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
- package/src/lib/server/runtime/scheduler.ts +0 -13
- package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/queries.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
- package/src/lib/server/runtime/session-run-manager.test.ts +0 -28
- package/src/lib/server/session-tools/crud.ts +0 -14
- package/src/lib/server/session-tools/delegate.ts +0 -4
- package/src/lib/server/session-tools/index.ts +0 -4
- package/src/lib/server/session-tools/team-context.ts +0 -3
- package/src/lib/server/storage-normalization.ts +13 -0
- package/src/lib/server/storage.ts +75 -45
- package/src/lib/server/tasks/task-checkout.ts +59 -0
- package/src/lib/server/tasks/task-lifecycle.ts +2 -0
- package/src/lib/server/tasks/task-route-service.ts +4 -26
- package/src/lib/server/tasks/task-service.ts +0 -7
- package/src/lib/server/tool-aliases.ts +0 -1
- package/src/lib/server/tool-capability-policy-advanced.test.ts +4 -4
- package/src/lib/server/tool-capability-policy.ts +0 -2
- package/src/lib/server/tool-planning.ts +0 -12
- package/src/lib/server/universal-tool-access.ts +0 -1
- package/src/lib/server/usage/cost-rollup.ts +124 -0
- package/src/lib/server/usage/usage-repository.ts +6 -0
- package/src/lib/server/wallets/wallet-crypto.ts +33 -0
- package/src/lib/server/wallets/wallet-repository.ts +24 -0
- package/src/lib/server/wallets/wallet-service.ts +119 -0
- package/src/lib/server/working-state/extraction.ts +8 -42
- package/src/lib/server/working-state/normalization.ts +10 -103
- package/src/lib/server/working-state/service.ts +12 -21
- package/src/lib/strip-internal-metadata.test.ts +1 -1
- package/src/lib/strip-internal-metadata.ts +1 -1
- package/src/lib/tool-definitions.ts +0 -1
- package/src/lib/validation/schemas.ts +36 -32
- package/src/lib/validation/server-schemas.ts +35 -0
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/types/agent.ts +10 -84
- package/src/types/app-settings.ts +6 -2
- package/src/types/approval.ts +3 -2
- package/src/types/connector.ts +1 -0
- package/src/types/goal.ts +30 -0
- package/src/types/index.ts +2 -1
- package/src/types/message.ts +0 -1
- package/src/types/misc.ts +2 -4
- package/src/types/protocol.ts +0 -2
- package/src/types/run.ts +0 -3
- package/src/types/session.ts +1 -51
- package/src/types/swarmdock.ts +29 -0
- package/src/types/task.ts +9 -3
- package/src/types/working-state.ts +2 -9
- package/src/views/settings/section-runtime-loop.tsx +0 -14
- package/src/app/api/canvas/[sessionId]/route.ts +0 -35
- package/src/app/api/missions/[id]/actions/route.ts +0 -31
- package/src/app/api/missions/[id]/events/route.ts +0 -14
- package/src/app/api/missions/[id]/route.ts +0 -10
- package/src/app/api/missions/route.test.ts +0 -244
- package/src/app/api/missions/route.ts +0 -57
- package/src/app/api/wallets/[id]/approve/route.ts +0 -79
- package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
- package/src/app/api/wallets/[id]/send/route.ts +0 -113
- package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
- package/src/app/missions/[id]/page.tsx +0 -3
- package/src/app/missions/page.tsx +0 -685
- package/src/components/canvas/canvas-panel.tsx +0 -267
- package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
- package/src/components/wallets/wallet-panel.tsx +0 -1010
- package/src/components/wallets/wallet-section.tsx +0 -260
- package/src/features/missions/queries.ts +0 -23
- package/src/lib/canvas-content.test.ts +0 -360
- package/src/lib/canvas-content.ts +0 -198
- package/src/lib/server/canvas-content.test.ts +0 -32
- package/src/lib/server/canvas-content.ts +0 -6
- package/src/lib/server/ethereum.ts +0 -591
- package/src/lib/server/evm-swap.ts +0 -476
- package/src/lib/server/missions/mission-intent.test.ts +0 -63
- package/src/lib/server/missions/mission-intent.ts +0 -569
- package/src/lib/server/missions/mission-repository.ts +0 -74
- package/src/lib/server/missions/mission-service/actions.ts +0 -6
- package/src/lib/server/missions/mission-service/bindings.ts +0 -9
- package/src/lib/server/missions/mission-service/context.ts +0 -4
- package/src/lib/server/missions/mission-service/core.ts +0 -2271
- package/src/lib/server/missions/mission-service/queries.ts +0 -12
- package/src/lib/server/missions/mission-service/recovery.ts +0 -5
- package/src/lib/server/missions/mission-service/ticks.ts +0 -9
- package/src/lib/server/missions/mission-service.test.ts +0 -888
- package/src/lib/server/missions/mission-service.ts +0 -6
- package/src/lib/server/session-tools/canvas.ts +0 -105
- package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
- package/src/lib/server/session-tools/wallet.ts +0 -1287
- package/src/lib/server/solana.ts +0 -327
- package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
- package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
- package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
- package/src/lib/server/wallet/wallet-service.test.ts +0 -81
- package/src/lib/server/wallet/wallet-service.ts +0 -225
- package/src/lib/wallet/wallet-transactions.test.ts +0 -75
- package/src/lib/wallet/wallet-transactions.ts +0 -43
- package/src/lib/wallet/wallet.test.ts +0 -333
- package/src/lib/wallet/wallet.ts +0 -183
- package/src/types/mission.ts +0 -185
- package/src/views/settings/section-wallets.tsx +0 -35
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { genId } from '@/lib/id'
|
|
2
|
+
import { loadTasks, saveTasks } from '@/lib/server/tasks/task-repository'
|
|
3
|
+
import { logActivity } from '@/lib/server/activity/activity-log'
|
|
4
|
+
import type { BoardTask } from '@/types/task'
|
|
5
|
+
|
|
6
|
+
interface SwarmDockTask {
|
|
7
|
+
id: string
|
|
8
|
+
requesterId: string
|
|
9
|
+
title: string
|
|
10
|
+
description: string
|
|
11
|
+
skillRequirements: string[]
|
|
12
|
+
budgetMax: string
|
|
13
|
+
deadline: string | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a SwarmClaw BoardTask from a SwarmDock task assignment.
|
|
18
|
+
* Uses `externalSource` to link back to the SwarmDock task (same pattern as GitHub issue import).
|
|
19
|
+
*/
|
|
20
|
+
export async function createBoardTaskFromAssignment(
|
|
21
|
+
task: SwarmDockTask,
|
|
22
|
+
agentId: string,
|
|
23
|
+
connectorId: string,
|
|
24
|
+
apiUrl: string,
|
|
25
|
+
): Promise<string> {
|
|
26
|
+
const tasks = loadTasks() as Record<string, BoardTask>
|
|
27
|
+
const id = genId()
|
|
28
|
+
const now = Date.now()
|
|
29
|
+
|
|
30
|
+
const boardTask: BoardTask = {
|
|
31
|
+
id,
|
|
32
|
+
title: task.title,
|
|
33
|
+
description: task.description,
|
|
34
|
+
status: 'running',
|
|
35
|
+
agentId,
|
|
36
|
+
createdAt: now,
|
|
37
|
+
updatedAt: now,
|
|
38
|
+
startedAt: now,
|
|
39
|
+
lastActivityAt: now,
|
|
40
|
+
sourceType: 'import',
|
|
41
|
+
externalSource: {
|
|
42
|
+
source: 'swarmdock',
|
|
43
|
+
id: task.id,
|
|
44
|
+
state: 'in_progress',
|
|
45
|
+
url: `${apiUrl}/tasks/${task.id}`,
|
|
46
|
+
},
|
|
47
|
+
tags: task.skillRequirements,
|
|
48
|
+
objective: task.description,
|
|
49
|
+
followupConnectorId: connectorId,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (task.deadline) {
|
|
53
|
+
boardTask.dueAt = new Date(task.deadline).getTime()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
tasks[id] = boardTask
|
|
57
|
+
saveTasks(tasks)
|
|
58
|
+
|
|
59
|
+
logActivity({
|
|
60
|
+
entityType: 'task',
|
|
61
|
+
entityId: id,
|
|
62
|
+
action: 'created',
|
|
63
|
+
actor: 'system',
|
|
64
|
+
summary: `SwarmDock task assigned: "${task.title}"`,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return id
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Update a SwarmClaw BoardTask based on a SwarmDock SSE event.
|
|
72
|
+
*/
|
|
73
|
+
export async function updateBoardTaskFromEvent(
|
|
74
|
+
swarmdockTaskId: string,
|
|
75
|
+
eventType: string,
|
|
76
|
+
): Promise<void> {
|
|
77
|
+
const tasks = loadTasks() as Record<string, BoardTask>
|
|
78
|
+
const boardTask = Object.values(tasks).find(
|
|
79
|
+
(t) => t.externalSource?.source === 'swarmdock' && t.externalSource.id === swarmdockTaskId,
|
|
80
|
+
)
|
|
81
|
+
if (!boardTask) return
|
|
82
|
+
|
|
83
|
+
const now = Date.now()
|
|
84
|
+
|
|
85
|
+
switch (eventType) {
|
|
86
|
+
case 'task.completed':
|
|
87
|
+
boardTask.status = 'completed'
|
|
88
|
+
boardTask.completedAt = now
|
|
89
|
+
boardTask.checkoutRunId = null
|
|
90
|
+
break
|
|
91
|
+
case 'task.submitted':
|
|
92
|
+
// Results submitted, waiting for approval on SwarmDock
|
|
93
|
+
if (boardTask.externalSource) boardTask.externalSource.state = 'review'
|
|
94
|
+
break
|
|
95
|
+
case 'task.cancelled':
|
|
96
|
+
boardTask.status = 'cancelled'
|
|
97
|
+
boardTask.checkoutRunId = null
|
|
98
|
+
break
|
|
99
|
+
case 'task.failed':
|
|
100
|
+
boardTask.status = 'failed'
|
|
101
|
+
boardTask.checkoutRunId = null
|
|
102
|
+
break
|
|
103
|
+
case 'task.review':
|
|
104
|
+
// Work submitted, awaiting requester review on SwarmDock
|
|
105
|
+
if (boardTask.externalSource) boardTask.externalSource.state = 'review'
|
|
106
|
+
break
|
|
107
|
+
case 'task.disputed':
|
|
108
|
+
// Task disputed on SwarmDock
|
|
109
|
+
if (boardTask.externalSource) boardTask.externalSource.state = 'disputed'
|
|
110
|
+
break
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
boardTask.updatedAt = now
|
|
114
|
+
boardTask.lastActivityAt = now
|
|
115
|
+
saveTasks(tasks)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Find a SwarmClaw BoardTask ID by its SwarmDock task ID.
|
|
120
|
+
*/
|
|
121
|
+
export function findBoardTaskBySwarmdockId(swarmdockTaskId: string): string | null {
|
|
122
|
+
const tasks = loadTasks() as Record<string, BoardTask>
|
|
123
|
+
const task = Object.values(tasks).find(
|
|
124
|
+
(t) => t.externalSource?.source === 'swarmdock' && t.externalSource.id === swarmdockTaskId,
|
|
125
|
+
)
|
|
126
|
+
return task?.id || null
|
|
127
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { log } from '@/lib/server/logger'
|
|
2
|
+
import { hmrSingleton } from '@/lib/shared-utils'
|
|
3
|
+
import { logActivity } from '@/lib/server/activity/activity-log'
|
|
4
|
+
import type { Connector, InboundMessage } from '@/types/connector'
|
|
5
|
+
import type { PlatformConnector, ConnectorInstance } from '@/lib/server/connectors/types'
|
|
6
|
+
import { createBoardTaskFromAssignment, updateBoardTaskFromEvent, findBoardTaskBySwarmdockId } from './swarmdock-tasks'
|
|
7
|
+
import { shouldAutoBid, submitAutoBid } from './swarmdock-bidding'
|
|
8
|
+
import type { TaskSubmitInput } from '@swarmdock/shared'
|
|
9
|
+
|
|
10
|
+
const TAG = 'swarmdock'
|
|
11
|
+
|
|
12
|
+
// SDK types inlined until @swarmdock/sdk is built and linked
|
|
13
|
+
interface SwarmDockTask {
|
|
14
|
+
id: string
|
|
15
|
+
requesterId: string
|
|
16
|
+
assigneeId: string | null
|
|
17
|
+
title: string
|
|
18
|
+
description: string
|
|
19
|
+
skillRequirements: string[]
|
|
20
|
+
budgetMax: string
|
|
21
|
+
status: string
|
|
22
|
+
deadline: string | null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface SwarmDockSSEEvent {
|
|
26
|
+
type: string
|
|
27
|
+
data: Record<string, unknown>
|
|
28
|
+
timestamp: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface SwarmDockConfig {
|
|
32
|
+
apiUrl: string
|
|
33
|
+
walletAddress: string
|
|
34
|
+
agentDescription: string
|
|
35
|
+
skills: string
|
|
36
|
+
autoDiscover: boolean
|
|
37
|
+
maxBudget: string
|
|
38
|
+
paymentPrivateKey?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseConfig(connector: Connector): SwarmDockConfig {
|
|
42
|
+
const c = connector.config || {}
|
|
43
|
+
return {
|
|
44
|
+
apiUrl: c.apiUrl || 'https://api.swarmdock.ai',
|
|
45
|
+
walletAddress: c.walletAddress || '',
|
|
46
|
+
agentDescription: c.agentDescription || connector.name || '',
|
|
47
|
+
skills: c.skills || '',
|
|
48
|
+
autoDiscover: c.autoDiscover === 'true',
|
|
49
|
+
maxBudget: c.maxBudget || '0',
|
|
50
|
+
paymentPrivateKey: c.paymentPrivateKey || undefined,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildTaskPrompt(task: SwarmDockTask): string {
|
|
55
|
+
const lines: string[] = [
|
|
56
|
+
`# SwarmDock Task: ${task.title}`,
|
|
57
|
+
'',
|
|
58
|
+
task.description,
|
|
59
|
+
'',
|
|
60
|
+
`**Required Skills:** ${task.skillRequirements.join(', ')}`,
|
|
61
|
+
`**Budget:** ${formatUsdc(task.budgetMax)}`,
|
|
62
|
+
]
|
|
63
|
+
if (task.deadline) lines.push(`**Deadline:** ${task.deadline}`)
|
|
64
|
+
lines.push('', 'Complete this task and provide your deliverables. Your response will be submitted as the task result on the SwarmDock marketplace.')
|
|
65
|
+
return lines.join('\n')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function formatUsdc(microUnits: string): string {
|
|
69
|
+
const cents = BigInt(microUnits)
|
|
70
|
+
const dollars = Number(cents) / 1_000_000
|
|
71
|
+
return `$${dollars.toFixed(2)} USDC`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function submitSwarmdockTaskResult(
|
|
75
|
+
client: { tasks: { submit: (taskId: string, input: TaskSubmitInput) => Promise<unknown> } },
|
|
76
|
+
swarmdockTaskId: string,
|
|
77
|
+
text: string,
|
|
78
|
+
notes?: string,
|
|
79
|
+
): Promise<void> {
|
|
80
|
+
const payload: TaskSubmitInput = {
|
|
81
|
+
artifacts: [{ type: 'text/markdown', content: text }],
|
|
82
|
+
files: [],
|
|
83
|
+
}
|
|
84
|
+
if (notes) payload.notes = notes
|
|
85
|
+
await client.tasks.submit(swarmdockTaskId, payload)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Runtime state: maps SwarmDock task IDs → SwarmClaw BoardTask IDs */
|
|
89
|
+
const taskIdMap = hmrSingleton('__swarmclaw_swarmdock_task_map__', () => new Map<string, string>())
|
|
90
|
+
|
|
91
|
+
const swarmdock: PlatformConnector = {
|
|
92
|
+
async start(connector, _botToken, onMessage): Promise<ConnectorInstance> {
|
|
93
|
+
const config = parseConfig(connector)
|
|
94
|
+
const connectorId = connector.id
|
|
95
|
+
const agentId = connector.agentId || ''
|
|
96
|
+
const privateKey = _botToken || ''
|
|
97
|
+
|
|
98
|
+
if (!privateKey) throw new Error('SwarmDock connector requires an Ed25519 private key credential')
|
|
99
|
+
if (!config.walletAddress) throw new Error('SwarmDock connector requires a Base L2 wallet address in config')
|
|
100
|
+
|
|
101
|
+
// Dynamic import of the SDK (must be built and linked first)
|
|
102
|
+
let SwarmDockClient: typeof import('@swarmdock/sdk').SwarmDockClient
|
|
103
|
+
try {
|
|
104
|
+
const sdk = await import('@swarmdock/sdk')
|
|
105
|
+
SwarmDockClient = sdk.SwarmDockClient
|
|
106
|
+
} catch {
|
|
107
|
+
throw new Error('SwarmDock SDK (@swarmdock/sdk) is not installed. Run: npm install @swarmdock/sdk')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const client = new SwarmDockClient({
|
|
111
|
+
baseUrl: config.apiUrl,
|
|
112
|
+
privateKey,
|
|
113
|
+
...(config.paymentPrivateKey?.startsWith('0x')
|
|
114
|
+
? { paymentPrivateKey: config.paymentPrivateKey as `0x${string}` }
|
|
115
|
+
: {}),
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Register agent on SwarmDock (Ed25519 challenge-response)
|
|
119
|
+
const skillList = config.skills
|
|
120
|
+
.split(',')
|
|
121
|
+
.map((s) => s.trim())
|
|
122
|
+
.filter(Boolean)
|
|
123
|
+
.map((skillId) => ({
|
|
124
|
+
skillId,
|
|
125
|
+
skillName: skillId.replace(/-/g, ' '),
|
|
126
|
+
description: `${skillId} capability`,
|
|
127
|
+
category: skillId,
|
|
128
|
+
basePrice: '1000000', // $1.00 default
|
|
129
|
+
inputModes: ['text'],
|
|
130
|
+
outputModes: ['text'],
|
|
131
|
+
}))
|
|
132
|
+
|
|
133
|
+
log.info(TAG, `Registering agent "${connector.name}" on SwarmDock at ${config.apiUrl}`)
|
|
134
|
+
const registration = await client.register({
|
|
135
|
+
displayName: connector.name,
|
|
136
|
+
description: config.agentDescription,
|
|
137
|
+
framework: 'swarmclaw',
|
|
138
|
+
walletAddress: config.walletAddress,
|
|
139
|
+
skills: skillList,
|
|
140
|
+
})
|
|
141
|
+
log.info(TAG, `Registered as ${registration.agent.did} (trust level ${registration.agent.trustLevel})`)
|
|
142
|
+
|
|
143
|
+
logActivity({
|
|
144
|
+
entityType: 'connector',
|
|
145
|
+
entityId: connectorId,
|
|
146
|
+
action: 'swarmdock-registered',
|
|
147
|
+
actor: 'system',
|
|
148
|
+
summary: `Agent "${connector.name}" registered on SwarmDock as ${registration.agent.did}`,
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// Set up SSE event stream
|
|
152
|
+
let alive = true
|
|
153
|
+
|
|
154
|
+
const handleSSEEvent = async (event: SwarmDockSSEEvent) => {
|
|
155
|
+
if (!alive) return
|
|
156
|
+
try {
|
|
157
|
+
switch (event.type) {
|
|
158
|
+
case 'task.created': {
|
|
159
|
+
if (!config.autoDiscover) break
|
|
160
|
+
const task = event.data as unknown as SwarmDockTask
|
|
161
|
+
if (shouldAutoBid(task, config)) {
|
|
162
|
+
await submitAutoBid(client, task.id, config)
|
|
163
|
+
logActivity({
|
|
164
|
+
entityType: 'connector',
|
|
165
|
+
entityId: connectorId,
|
|
166
|
+
action: 'swarmdock-bid',
|
|
167
|
+
actor: 'system',
|
|
168
|
+
summary: `Auto-bid on SwarmDock task: "${task.title}"`,
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
break
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
case 'task.assigned': {
|
|
175
|
+
const task = event.data as unknown as SwarmDockTask
|
|
176
|
+
if (!task.assigneeId) break
|
|
177
|
+
|
|
178
|
+
// Signal work started on SwarmDock
|
|
179
|
+
try { await client.tasks.start(task.id) } catch {}
|
|
180
|
+
|
|
181
|
+
// Create a BoardTask in SwarmClaw
|
|
182
|
+
const boardTaskId = await createBoardTaskFromAssignment(task, agentId, connectorId, config.apiUrl)
|
|
183
|
+
taskIdMap.set(task.id, boardTaskId)
|
|
184
|
+
|
|
185
|
+
// Dispatch as inbound message to the assigned agent
|
|
186
|
+
const inbound: InboundMessage = {
|
|
187
|
+
platform: 'swarmdock',
|
|
188
|
+
channelId: `swarmdock-task:${task.id}`,
|
|
189
|
+
channelName: `SwarmDock: ${task.title}`,
|
|
190
|
+
senderId: task.requesterId,
|
|
191
|
+
senderName: `SwarmDock Requester`,
|
|
192
|
+
text: buildTaskPrompt(task),
|
|
193
|
+
messageId: task.id,
|
|
194
|
+
}
|
|
195
|
+
await onMessage(inbound)
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
case 'task.completed':
|
|
200
|
+
case 'task.cancelled':
|
|
201
|
+
case 'task.failed': {
|
|
202
|
+
const taskId = (event.data as Record<string, string>).taskId
|
|
203
|
+
if (taskId) await updateBoardTaskFromEvent(taskId, event.type)
|
|
204
|
+
break
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case 'task.review': {
|
|
208
|
+
const taskId = (event.data as Record<string, string>).taskId
|
|
209
|
+
if (taskId) await updateBoardTaskFromEvent(taskId, 'task.review')
|
|
210
|
+
break
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
case 'task.disputed': {
|
|
214
|
+
const taskId = (event.data as Record<string, string>).taskId
|
|
215
|
+
if (taskId) {
|
|
216
|
+
await updateBoardTaskFromEvent(taskId, 'task.disputed')
|
|
217
|
+
logActivity({
|
|
218
|
+
entityType: 'connector',
|
|
219
|
+
entityId: connectorId,
|
|
220
|
+
action: 'incident',
|
|
221
|
+
actor: 'system',
|
|
222
|
+
summary: `SwarmDock task ${taskId} disputed`,
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
break
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
case 'payment.released': {
|
|
229
|
+
const data = event.data as Record<string, string>
|
|
230
|
+
logActivity({
|
|
231
|
+
entityType: 'connector',
|
|
232
|
+
entityId: connectorId,
|
|
233
|
+
action: 'swarmdock-payment',
|
|
234
|
+
actor: 'system',
|
|
235
|
+
summary: `Payment received: ${formatUsdc(data.amount || '0')} for task ${data.taskId}`,
|
|
236
|
+
})
|
|
237
|
+
break
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} catch (err) {
|
|
241
|
+
log.error(TAG, `Error handling SSE event ${event.type}: ${err instanceof Error ? err.message : String(err)}`)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
client.events.subscribe(handleSSEEvent as (event: unknown) => void)
|
|
246
|
+
|
|
247
|
+
// Token refresh heartbeat (23h, token lasts 24h)
|
|
248
|
+
const heartbeatInterval = setInterval(async () => {
|
|
249
|
+
try {
|
|
250
|
+
await client.heartbeat()
|
|
251
|
+
log.debug(TAG, 'SwarmDock token refreshed')
|
|
252
|
+
} catch (err) {
|
|
253
|
+
log.error(TAG, `SwarmDock heartbeat failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
254
|
+
}
|
|
255
|
+
}, 23 * 60 * 60 * 1000)
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
connector,
|
|
259
|
+
|
|
260
|
+
stop: async () => {
|
|
261
|
+
alive = false
|
|
262
|
+
clearInterval(heartbeatInterval)
|
|
263
|
+
client.events.unsubscribe()
|
|
264
|
+
log.info(TAG, 'SwarmDock connector stopped')
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
sendMessage: async (channelId: string, text: string) => {
|
|
268
|
+
// channelId format: "swarmdock-task:{taskId}"
|
|
269
|
+
const swarmdockTaskId = channelId.replace('swarmdock-task:', '')
|
|
270
|
+
if (!swarmdockTaskId) return
|
|
271
|
+
|
|
272
|
+
await submitSwarmdockTaskResult(client, swarmdockTaskId, text)
|
|
273
|
+
log.info(TAG, `Submitted results for SwarmDock task ${swarmdockTaskId}`)
|
|
274
|
+
|
|
275
|
+
if (findBoardTaskBySwarmdockId(swarmdockTaskId)) {
|
|
276
|
+
await updateBoardTaskFromEvent(swarmdockTaskId, 'task.submitted')
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return { messageId: swarmdockTaskId }
|
|
280
|
+
},
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export default swarmdock
|
|
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict'
|
|
|
2
2
|
import { test } from 'node:test'
|
|
3
3
|
|
|
4
4
|
import { buildExecutionBrief, buildExecutionBriefContextBlock, serializeExecutionBriefForDelegation } from './execution-brief'
|
|
5
|
-
import type {
|
|
5
|
+
import type { Session, SessionWorkingState } from '@/types'
|
|
6
6
|
|
|
7
7
|
test('buildExecutionBrief prefers working state and folds in mission and run-context fallback data', () => {
|
|
8
8
|
const session = {
|
|
@@ -31,25 +31,8 @@ test('buildExecutionBrief prefers working state and folds in mission and run-con
|
|
|
31
31
|
},
|
|
32
32
|
} satisfies Partial<Session> as Session
|
|
33
33
|
|
|
34
|
-
const mission = {
|
|
35
|
-
id: 'm1',
|
|
36
|
-
source: 'chat',
|
|
37
|
-
objective: 'Ship the release fix',
|
|
38
|
-
status: 'active',
|
|
39
|
-
phase: 'executing',
|
|
40
|
-
currentStep: 'Verify staging auth',
|
|
41
|
-
successCriteria: ['Restore staging deploys'],
|
|
42
|
-
waitState: {
|
|
43
|
-
kind: 'approval',
|
|
44
|
-
reason: 'Waiting for deploy approval.',
|
|
45
|
-
},
|
|
46
|
-
createdAt: 1,
|
|
47
|
-
updatedAt: 1,
|
|
48
|
-
} satisfies Partial<Mission> as Mission
|
|
49
|
-
|
|
50
34
|
const workingState = {
|
|
51
35
|
sessionId: 's1',
|
|
52
|
-
missionId: 'm1',
|
|
53
36
|
objective: 'Ship the release fix safely',
|
|
54
37
|
summary: 'Auth mismatch isolated to staging.',
|
|
55
38
|
constraints: ['Do not change the API'],
|
|
@@ -79,7 +62,7 @@ test('buildExecutionBrief prefers working state and folds in mission and run-con
|
|
|
79
62
|
updatedAt: 1,
|
|
80
63
|
} satisfies SessionWorkingState
|
|
81
64
|
|
|
82
|
-
const brief = buildExecutionBrief({ session,
|
|
65
|
+
const brief = buildExecutionBrief({ session, workingState })
|
|
83
66
|
|
|
84
67
|
assert.equal(brief.objective, 'Ship the release fix safely')
|
|
85
68
|
assert.equal(brief.summary, 'Auth mismatch isolated to staging.')
|
|
@@ -87,11 +70,9 @@ test('buildExecutionBrief prefers working state and folds in mission and run-con
|
|
|
87
70
|
assert.equal(brief.nextAction, 'Request deploy approval')
|
|
88
71
|
assert.equal(brief.plan[0]?.text, 'Request deploy approval')
|
|
89
72
|
assert.equal(brief.blockers[0], 'Deploy approval is pending. | next: Request deploy approval')
|
|
90
|
-
assert.ok(brief.blockers.some((entry) => /waiting for deploy approval/i.test(entry)))
|
|
91
73
|
assert.ok(brief.facts.some((entry) => /auth mismatch isolated to staging/i.test(entry)))
|
|
92
74
|
assert.ok(brief.artifacts.some((entry) => /deploy\.log/i.test(entry)))
|
|
93
75
|
assert.equal(brief.parentContext, 'Parent asked for a contained fix.')
|
|
94
|
-
assert.equal(brief.missionPhase, 'executing')
|
|
95
76
|
})
|
|
96
77
|
|
|
97
78
|
test('buildExecutionBriefContextBlock renders a single canonical state block', () => {
|
|
@@ -139,7 +120,6 @@ test('buildExecutionBriefContextBlock renders a single canonical state block', (
|
|
|
139
120
|
test('serializeExecutionBriefForDelegation creates a bounded handoff summary', () => {
|
|
140
121
|
const text = serializeExecutionBriefForDelegation({
|
|
141
122
|
sessionId: 's3',
|
|
142
|
-
missionId: 'm3',
|
|
143
123
|
objective: 'Repair the deployment pipeline',
|
|
144
124
|
summary: 'The regression is isolated to the release job.',
|
|
145
125
|
status: 'blocked',
|
|
@@ -153,9 +133,6 @@ test('serializeExecutionBriefForDelegation creates a bounded handoff summary', (
|
|
|
153
133
|
artifacts: ['/tmp/project/release.log'],
|
|
154
134
|
constraints: ['Keep the current release shape.'],
|
|
155
135
|
successCriteria: ['Production deploy completes'],
|
|
156
|
-
missionStatus: 'active',
|
|
157
|
-
missionPhase: 'executing',
|
|
158
|
-
waitState: null,
|
|
159
136
|
evidenceRefs: [],
|
|
160
137
|
parentContext: 'Parent is waiting on a concise status update.',
|
|
161
138
|
})
|
|
@@ -2,7 +2,6 @@ import type {
|
|
|
2
2
|
EvidenceRef,
|
|
3
3
|
ExecutionBrief,
|
|
4
4
|
ExecutionBriefPlanStep,
|
|
5
|
-
Mission,
|
|
6
5
|
Session,
|
|
7
6
|
SessionWorkingState,
|
|
8
7
|
WorkingPlanStep,
|
|
@@ -13,6 +12,7 @@ import { getSession } from '@/lib/server/sessions/session-repository'
|
|
|
13
12
|
import { loadSessionWorkingState } from '@/lib/server/working-state/service'
|
|
14
13
|
import { ensureRunContext } from '@/lib/server/run-context'
|
|
15
14
|
import { cleanText, cleanMultiline } from '@/lib/server/text-normalization'
|
|
15
|
+
import { resolveEffectiveGoal, getGoalChain, formatGoalChainForBrief } from '@/lib/server/goals/goal-service'
|
|
16
16
|
|
|
17
17
|
const MAX_PLAN_ITEMS = 8
|
|
18
18
|
const MAX_FACTS = 8
|
|
@@ -75,14 +75,8 @@ function dedupePlan(steps: ExecutionBriefPlanStep[]): ExecutionBriefPlanStep[] {
|
|
|
75
75
|
return out
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
function inferStatus(
|
|
78
|
+
function inferStatus(workingState: SessionWorkingState | null): WorkingStateStatus {
|
|
79
79
|
if (workingState?.status) return workingState.status
|
|
80
|
-
if (!mission) return 'idle'
|
|
81
|
-
if (mission.status === 'completed') return 'completed'
|
|
82
|
-
if (mission.status === 'waiting') return 'waiting'
|
|
83
|
-
if (mission.status === 'failed' || mission.phase === 'failed') return 'blocked'
|
|
84
|
-
if (mission.waitState) return 'waiting'
|
|
85
|
-
if (mission.phase === 'executing' || mission.phase === 'dispatching' || mission.phase === 'verifying') return 'progress'
|
|
86
80
|
return 'idle'
|
|
87
81
|
}
|
|
88
82
|
|
|
@@ -121,15 +115,14 @@ function buildFacts(workingState: SessionWorkingState | null, session: Session |
|
|
|
121
115
|
return uniqueStrings([...activeFacts, ...runContextFacts], MAX_FACTS, 240)
|
|
122
116
|
}
|
|
123
117
|
|
|
124
|
-
function buildBlockers(workingState: SessionWorkingState | null,
|
|
118
|
+
function buildBlockers(workingState: SessionWorkingState | null, session: Session | null): string[] {
|
|
125
119
|
const activeBlockers = workingState
|
|
126
120
|
? workingState.blockers
|
|
127
121
|
.filter((blocker) => blocker.status === 'active')
|
|
128
122
|
.map((blocker) => blocker.nextAction ? `${blocker.summary} | next: ${blocker.nextAction}` : blocker.summary)
|
|
129
123
|
: []
|
|
130
|
-
const missionBlockers = mission?.waitState?.reason ? [cleanText(mission.waitState.reason, 280)] : []
|
|
131
124
|
const runContextBlockers = session?.runContext ? ensureRunContext(session.runContext).blockers : []
|
|
132
|
-
return uniqueStrings([...activeBlockers, ...
|
|
125
|
+
return uniqueStrings([...activeBlockers, ...runContextBlockers], MAX_BLOCKERS, 280)
|
|
133
126
|
}
|
|
134
127
|
|
|
135
128
|
function buildArtifacts(workingState: SessionWorkingState | null): string[] {
|
|
@@ -147,8 +140,8 @@ function buildConstraints(workingState: SessionWorkingState | null, session: Ses
|
|
|
147
140
|
return uniqueStrings([...(workingConstraints || []), ...(runContext?.constraints || [])], 10, 220)
|
|
148
141
|
}
|
|
149
142
|
|
|
150
|
-
function buildSuccessCriteria(workingState: SessionWorkingState | null
|
|
151
|
-
return uniqueStrings([...(workingState?.successCriteria || [])
|
|
143
|
+
function buildSuccessCriteria(workingState: SessionWorkingState | null): string[] {
|
|
144
|
+
return uniqueStrings([...(workingState?.successCriteria || [])], 10, 220)
|
|
152
145
|
}
|
|
153
146
|
|
|
154
147
|
function buildEvidenceRefs(workingState: SessionWorkingState | null): EvidenceRef[] {
|
|
@@ -161,49 +154,40 @@ function buildEvidenceRefs(workingState: SessionWorkingState | null): EvidenceRe
|
|
|
161
154
|
export function buildExecutionBrief(params: {
|
|
162
155
|
sessionId?: string | null
|
|
163
156
|
session?: Session | null
|
|
164
|
-
mission?:
|
|
157
|
+
mission?: null
|
|
165
158
|
workingState?: SessionWorkingState | null
|
|
166
159
|
}): ExecutionBrief {
|
|
167
160
|
const session = params.session
|
|
168
161
|
|| (params.sessionId ? getSession(params.sessionId) || null : null)
|
|
169
|
-
const mission = params.mission || null
|
|
170
162
|
const workingState = params.workingState
|
|
171
|
-
|| (session?.id ? loadSessionWorkingState(session.id
|
|
163
|
+
|| (session?.id ? loadSessionWorkingState(session.id) : null)
|
|
172
164
|
const runContext = session?.runContext ? ensureRunContext(session.runContext) : null
|
|
173
165
|
const plan = buildPlan(workingState, session)
|
|
174
166
|
const nextAction = cleanText(
|
|
175
167
|
workingState?.nextAction
|
|
176
|
-
|| mission?.currentStep
|
|
177
168
|
|| plan.find((step) => step.status === 'active')?.text,
|
|
178
169
|
240,
|
|
179
170
|
) || null
|
|
180
171
|
|
|
181
172
|
return {
|
|
182
173
|
sessionId: session?.id || params.sessionId || null,
|
|
183
|
-
missionId: mission?.id || workingState?.missionId || null,
|
|
184
174
|
objective: cleanMultiline(
|
|
185
175
|
workingState?.objective
|
|
186
|
-
|| mission?.objective
|
|
187
176
|
|| runContext?.objective,
|
|
188
177
|
900,
|
|
189
178
|
) || null,
|
|
190
179
|
summary: cleanMultiline(
|
|
191
|
-
workingState?.summary
|
|
192
|
-
|| mission?.verifierSummary
|
|
193
|
-
|| mission?.plannerSummary,
|
|
180
|
+
workingState?.summary,
|
|
194
181
|
700,
|
|
195
182
|
) || null,
|
|
196
|
-
status: inferStatus(
|
|
183
|
+
status: inferStatus(workingState),
|
|
197
184
|
nextAction,
|
|
198
185
|
plan,
|
|
199
|
-
blockers: buildBlockers(workingState,
|
|
186
|
+
blockers: buildBlockers(workingState, session),
|
|
200
187
|
facts: buildFacts(workingState, session),
|
|
201
188
|
artifacts: buildArtifacts(workingState),
|
|
202
189
|
constraints: buildConstraints(workingState, session),
|
|
203
|
-
successCriteria: buildSuccessCriteria(workingState
|
|
204
|
-
missionStatus: mission?.status || null,
|
|
205
|
-
missionPhase: mission?.phase || null,
|
|
206
|
-
waitState: mission?.waitState || null,
|
|
190
|
+
successCriteria: buildSuccessCriteria(workingState),
|
|
207
191
|
evidenceRefs: buildEvidenceRefs(workingState),
|
|
208
192
|
parentContext: cleanMultiline(runContext?.parentContext, 900) || null,
|
|
209
193
|
}
|
|
@@ -238,21 +222,32 @@ export function buildExecutionBriefContextBlock(
|
|
|
238
222
|
|| brief.artifacts.length > 0
|
|
239
223
|
|| brief.constraints.length > 0
|
|
240
224
|
|| brief.successCriteria.length > 0
|
|
241
|
-
|| brief.evidenceRefs.length > 0
|
|
242
|
-
|| brief.missionStatus
|
|
243
|
-
|| brief.missionPhase
|
|
244
|
-
|| brief.waitState?.reason,
|
|
225
|
+
|| brief.evidenceRefs.length > 0,
|
|
245
226
|
)
|
|
246
227
|
if (!hasContent && brief.status === 'idle') return ''
|
|
228
|
+
// Resolve goal chain for the session's agent/task/project context
|
|
229
|
+
let goalBlock = ''
|
|
230
|
+
if (brief.sessionId) {
|
|
231
|
+
const session = getSession(brief.sessionId)
|
|
232
|
+
if (session) {
|
|
233
|
+
const goal = resolveEffectiveGoal({
|
|
234
|
+
agentId: session.agentId || null,
|
|
235
|
+
projectId: session.projectId || null,
|
|
236
|
+
})
|
|
237
|
+
if (goal) {
|
|
238
|
+
const chain = getGoalChain(goal.id)
|
|
239
|
+
goalBlock = formatGoalChainForBrief(chain)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
247
244
|
const sections = [
|
|
248
245
|
options?.title || '## Execution Brief',
|
|
246
|
+
goalBlock,
|
|
249
247
|
brief.parentContext ? `Parent context:\n${brief.parentContext}` : '',
|
|
250
248
|
brief.objective ? `Objective: ${brief.objective}` : '',
|
|
251
249
|
brief.summary ? `Summary: ${brief.summary}` : '',
|
|
252
250
|
`Status: ${brief.status}`,
|
|
253
|
-
brief.missionStatus ? `Mission status: ${brief.missionStatus}` : '',
|
|
254
|
-
brief.missionPhase ? `Mission phase: ${brief.missionPhase}` : '',
|
|
255
|
-
brief.waitState?.reason ? `Waiting reason: ${cleanText(brief.waitState.reason, 280)}` : '',
|
|
256
251
|
brief.nextAction ? `Next action: ${brief.nextAction}` : '',
|
|
257
252
|
brief.successCriteria.length > 0 ? `Success criteria: ${brief.successCriteria.join(' | ')}` : '',
|
|
258
253
|
brief.constraints.length > 0 ? `Constraints: ${brief.constraints.join(' | ')}` : '',
|
|
@@ -238,7 +238,6 @@ export function enqueueTaskAttemptExecution(
|
|
|
238
238
|
const run: SessionRunRecord = {
|
|
239
239
|
id: executionId,
|
|
240
240
|
sessionId: input.sessionId,
|
|
241
|
-
missionId: input.task.missionId || null,
|
|
242
241
|
kind: 'task_attempt',
|
|
243
242
|
ownerType: 'task',
|
|
244
243
|
ownerId: input.task.id,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Goal } from '@/types'
|
|
2
|
+
import { loadGoals, loadGoal, upsertGoal, deleteGoalItem } from '@/lib/server/storage'
|
|
3
|
+
import { perf } from '@/lib/server/runtime/perf'
|
|
4
|
+
|
|
5
|
+
export function listGoals(): Record<string, Goal> {
|
|
6
|
+
return perf.measureSync('repository', 'goals.list', () => loadGoals()) as Record<string, Goal>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getGoal(id: string): Goal | null {
|
|
10
|
+
return perf.measureSync('repository', 'goals.get', () => loadGoal(id)) as Goal | null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function saveGoal(id: string, goal: Goal): void {
|
|
14
|
+
perf.measureSync('repository', 'goals.upsert', () => upsertGoal(id, goal), { id })
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function removeGoal(id: string): void {
|
|
18
|
+
perf.measureSync('repository', 'goals.delete', () => deleteGoalItem(id), { id })
|
|
19
|
+
}
|