agent-relay-server 0.32.4 → 0.33.1
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/package.json +2 -2
- package/public/assets/{activity-DT1JGHnp.js → activity-B0_uE6Yh.js} +2 -2
- package/public/assets/{activity-DT1JGHnp.js.map → activity-B0_uE6Yh.js.map} +1 -1
- package/public/assets/{agent-profiles-CrMemMkZ.js → agent-profiles-Rwxrcf9F.js} +2 -2
- package/public/assets/{agent-profiles-CrMemMkZ.js.map → agent-profiles-Rwxrcf9F.js.map} +1 -1
- package/public/assets/{agents-Bl-rrgOy.js → agents-Dp1EXJc8.js} +2 -2
- package/public/assets/{agents-Bl-rrgOy.js.map → agents-Dp1EXJc8.js.map} +1 -1
- package/public/assets/{analytics-a663ak56.js → analytics-D5OT5ajj.js} +2 -2
- package/public/assets/{analytics-a663ak56.js.map → analytics-D5OT5ajj.js.map} +1 -1
- package/public/assets/automation-Dm6rXNxK.js +2 -0
- package/public/assets/{automation-CiaLThdO.js.map → automation-Dm6rXNxK.js.map} +1 -1
- package/public/assets/{branch-state-badge-D4ur3m3_.js → branch-state-badge-FX5Yww2s.js} +2 -2
- package/public/assets/{branch-state-badge-D4ur3m3_.js.map → branch-state-badge-FX5Yww2s.js.map} +1 -1
- package/public/assets/{channels-o9KLTHoK.js → channels--rdAiX17.js} +2 -2
- package/public/assets/{channels-o9KLTHoK.js.map → channels--rdAiX17.js.map} +1 -1
- package/public/assets/chat-JZAEDGfX.js +2 -0
- package/public/assets/chat-JZAEDGfX.js.map +1 -0
- package/public/assets/{connectors-CdC806mA.js → connectors-Bx4gzvNf.js} +2 -2
- package/public/assets/{connectors-CdC806mA.js.map → connectors-Bx4gzvNf.js.map} +1 -1
- package/public/assets/display-Bebqs1qu.js +3 -0
- package/public/assets/display-Bebqs1qu.js.map +1 -0
- package/public/assets/{formatted-body-impl-Ca74OAEH.js → formatted-body-impl-CVq4qHix.js} +2 -2
- package/public/assets/{formatted-body-impl-Ca74OAEH.js.map → formatted-body-impl-CVq4qHix.js.map} +1 -1
- package/public/assets/{index-C_33ymaw.js → index-BHRtR4q7.js} +8 -8
- package/public/assets/{index-C_33ymaw.js.map → index-BHRtR4q7.js.map} +1 -1
- package/public/assets/{insights-ClI68s39.js → insights-yJFgCa3o.js} +2 -2
- package/public/assets/{insights-ClI68s39.js.map → insights-yJFgCa3o.js.map} +1 -1
- package/public/assets/{integrations-1nxMizDY.js → integrations-k1HIONjo.js} +2 -2
- package/public/assets/{integrations-1nxMizDY.js.map → integrations-k1HIONjo.js.map} +1 -1
- package/public/assets/maintenance-CsoOFBXx.js +2 -0
- package/public/assets/{maintenance-DiFNzNPN.js.map → maintenance-CsoOFBXx.js.map} +1 -1
- package/public/assets/{managed-agents-Do3dKvfj.js → managed-agents-Q3HuVjGg.js} +2 -2
- package/public/assets/{managed-agents-Do3dKvfj.js.map → managed-agents-Q3HuVjGg.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-CLA0J255.js → markdown-preview-impl-CnsMjrnu.js} +2 -2
- package/public/assets/{markdown-preview-impl-CLA0J255.js.map → markdown-preview-impl-CnsMjrnu.js.map} +1 -1
- package/public/assets/{memory-IjwqFzBd.js → memory-D3-K5eJS.js} +2 -2
- package/public/assets/{memory-IjwqFzBd.js.map → memory-D3-K5eJS.js.map} +1 -1
- package/public/assets/{messages-DjvWqHyn.js → messages-B4lCP5rS.js} +2 -2
- package/public/assets/{messages-DjvWqHyn.js.map → messages-B4lCP5rS.js.map} +1 -1
- package/public/assets/{orchestrators-D2IqDxDT.js → orchestrators-CRoZtLeQ.js} +2 -2
- package/public/assets/{orchestrators-D2IqDxDT.js.map → orchestrators-CRoZtLeQ.js.map} +1 -1
- package/public/assets/{overview-DKC3TbAh.js → overview-CxCU2fOF.js} +2 -2
- package/public/assets/{overview-DKC3TbAh.js.map → overview-CxCU2fOF.js.map} +1 -1
- package/public/assets/pairs-unqjPlmq.js +2 -0
- package/public/assets/{pairs-WpKCPE1n.js.map → pairs-unqjPlmq.js.map} +1 -1
- package/public/assets/{security-BF7ZtPQe.js → security-B7HhSYNy.js} +2 -2
- package/public/assets/{security-BF7ZtPQe.js.map → security-B7HhSYNy.js.map} +1 -1
- package/public/assets/{settings-CQnjrTa-.js → settings-B9NDhsAb.js} +2 -2
- package/public/assets/{settings-CQnjrTa-.js.map → settings-B9NDhsAb.js.map} +1 -1
- package/public/assets/store-DiSzYHj9.js +9 -0
- package/public/assets/{store-C9VcSo05.js.map → store-DiSzYHj9.js.map} +1 -1
- package/public/assets/{tasks-CbN_GSSb.js → tasks-CIQolvNm.js} +2 -2
- package/public/assets/{tasks-CbN_GSSb.js.map → tasks-CIQolvNm.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-BJRohThT.js → terminal-viewer-impl-DCifVqFR.js} +2 -2
- package/public/assets/{terminal-viewer-impl-BJRohThT.js.map → terminal-viewer-impl-DCifVqFR.js.map} +1 -1
- package/public/assets/{work-queue-C5xLBLmm.js → work-queue-Dr3c1V6O.js} +2 -2
- package/public/assets/{work-queue-C5xLBLmm.js.map → work-queue-Dr3c1V6O.js.map} +1 -1
- package/public/assets/{workspaces-D91H3wDX.js → workspaces-B1Jxop7h.js} +3 -3
- package/public/assets/{workspaces-D91H3wDX.js.map → workspaces-B1Jxop7h.js.map} +1 -1
- package/public/index.html +3 -3
- package/runner/src/adapter.ts +1 -1
- package/src/agent-lifecycle-events.ts +137 -0
- package/src/artifact-storage.ts +3 -5
- package/src/channel-target.ts +24 -0
- package/src/cli/_shared.ts +80 -0
- package/src/cli/agent-detect.ts +188 -0
- package/src/cli/agent-meta.ts +95 -0
- package/src/cli/context-probe.ts +88 -0
- package/src/cli/daemon.ts +111 -0
- package/src/cli/dev.ts +173 -0
- package/src/cli/index.ts +361 -0
- package/src/cli/introspect.ts +73 -0
- package/src/cli/memory.ts +37 -0
- package/src/cli/message.ts +201 -0
- package/src/cli/orchestrator.ts +227 -0
- package/src/cli/pair.ts +125 -0
- package/src/cli/provider.ts +209 -0
- package/src/cli/recipe.ts +110 -0
- package/src/cli/reply.ts +141 -0
- package/src/cli/setup.ts +57 -0
- package/src/cli/steward.ts +59 -0
- package/src/cli/token.ts +81 -0
- package/src/cli/upgrade.ts +193 -0
- package/src/cli/workspace.ts +215 -0
- package/src/cli.ts +4 -2718
- package/src/config-store.ts +10 -6
- package/src/db/activity.ts +194 -0
- package/src/db/agent-search.ts +174 -0
- package/src/db/agents.ts +551 -0
- package/src/db/artifacts.ts +342 -0
- package/src/db/channels.ts +576 -0
- package/src/db/connection.ts +71 -0
- package/src/db/delivery.ts +395 -0
- package/src/db/inbox.ts +249 -0
- package/src/db/index.ts +23 -0
- package/src/db/integrations.ts +339 -0
- package/src/db/mappers.ts +397 -0
- package/src/db/merge-lease.ts +160 -0
- package/src/db/message-reads.ts +304 -0
- package/src/db/messages.ts +434 -0
- package/src/db/migrations.ts +431 -0
- package/src/db/orchestrators.ts +358 -0
- package/src/db/pairs.ts +324 -0
- package/src/db/schema.ts +758 -0
- package/src/db/stats.ts +337 -0
- package/src/db/tasks.ts +407 -0
- package/src/db/workspaces.ts +440 -0
- package/src/db.ts +4 -5721
- package/src/maintenance.ts +4 -0
- package/src/mcp-errors.ts +7 -0
- package/src/mcp.ts +32 -34
- package/src/routes/agents-spawn.ts +9 -1
- package/src/routes/agents.ts +5 -0
- package/src/routes/commands.ts +15 -0
- package/src/routes/integrations.ts +6 -8
- package/src/spawn-targets.ts +159 -0
- package/src/utils.ts +16 -1
- package/public/assets/automation-CiaLThdO.js +0 -2
- package/public/assets/chat-5hvHZcAe.js +0 -2
- package/public/assets/chat-5hvHZcAe.js.map +0 -1
- package/public/assets/display-JI19Vc7L.js +0 -3
- package/public/assets/display-JI19Vc7L.js.map +0 -1
- package/public/assets/maintenance-DiFNzNPN.js +0 -2
- package/public/assets/pairs-WpKCPE1n.js +0 -2
- package/public/assets/store-C9VcSo05.js +0 -9
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { isRecord, stringValue, isMechanicalMessageKind } from "agent-relay-sdk";
|
|
4
|
+
import { ORCHESTRATOR_PROTOCOL_VERSION, VERSION } from "../config.ts";
|
|
5
|
+
import { parseJson } from "../utils";
|
|
6
|
+
import { isLiveIsolatedWorkspace } from "../workspace-phase";
|
|
7
|
+
import {
|
|
8
|
+
CONTRACT_REQUIREMENTS,
|
|
9
|
+
contractCompatibility,
|
|
10
|
+
parseRuntimeCapabilities,
|
|
11
|
+
parseRuntimeContracts,
|
|
12
|
+
parseRuntimePackage,
|
|
13
|
+
type RuntimeContracts,
|
|
14
|
+
} from "../contracts";
|
|
15
|
+
import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
|
|
16
|
+
import { matchAgents } from "../agent-ref";
|
|
17
|
+
import { parseStringArray } from "./agents.ts";
|
|
18
|
+
import { artifactId } from "./artifacts.ts";
|
|
19
|
+
import type {
|
|
20
|
+
AgentCard,
|
|
21
|
+
ActivityEvent,
|
|
22
|
+
ActivityEventInput,
|
|
23
|
+
AgentKind,
|
|
24
|
+
AgentSessionGuard,
|
|
25
|
+
Artifact,
|
|
26
|
+
ArtifactBlob,
|
|
27
|
+
ArtifactKind,
|
|
28
|
+
ArtifactLink,
|
|
29
|
+
ArtifactSensitivity,
|
|
30
|
+
ArtifactVisibility,
|
|
31
|
+
AttachmentRef,
|
|
32
|
+
ChannelBinding,
|
|
33
|
+
ChannelBindingMode,
|
|
34
|
+
ChannelRouteTarget,
|
|
35
|
+
ChatHistoryImport,
|
|
36
|
+
ChatHistoryImportEntry,
|
|
37
|
+
ChannelSummary,
|
|
38
|
+
ChannelTargetHealth,
|
|
39
|
+
CreatePairInput,
|
|
40
|
+
HealthCheck,
|
|
41
|
+
HealthReport,
|
|
42
|
+
ManagedAgent,
|
|
43
|
+
ManagedSessionExitDiagnostics,
|
|
44
|
+
Message,
|
|
45
|
+
MessageDeliveryAttempt,
|
|
46
|
+
MessageDeliveryStatus,
|
|
47
|
+
Orchestrator,
|
|
48
|
+
OrchestratorHealth,
|
|
49
|
+
OrchestratorRuntimeInput,
|
|
50
|
+
OrchestratorStatus,
|
|
51
|
+
OrchestratorUpgradeState,
|
|
52
|
+
PairActionInput,
|
|
53
|
+
PairMessageInput,
|
|
54
|
+
PairSession,
|
|
55
|
+
PairStatus,
|
|
56
|
+
RegisterAgentInput,
|
|
57
|
+
ReplyObligation,
|
|
58
|
+
RegisterOrchestratorInput,
|
|
59
|
+
SendMessageInput,
|
|
60
|
+
PollQuery,
|
|
61
|
+
SpawnApprovalMode,
|
|
62
|
+
SpawnProvider,
|
|
63
|
+
Task,
|
|
64
|
+
TaskEvent,
|
|
65
|
+
TaskSeverity,
|
|
66
|
+
TaskStatus,
|
|
67
|
+
IntegrationEventInput,
|
|
68
|
+
IntegrationSummary,
|
|
69
|
+
IntegrationTaskStats,
|
|
70
|
+
InboxDraft,
|
|
71
|
+
InboxState,
|
|
72
|
+
InboxThreadState,
|
|
73
|
+
ContextSnapshot,
|
|
74
|
+
ContextState,
|
|
75
|
+
ProviderCapabilities,
|
|
76
|
+
TaskStatusInput,
|
|
77
|
+
WorkspaceMetadata,
|
|
78
|
+
WorkspaceRecord,
|
|
79
|
+
WorkspaceStatus,
|
|
80
|
+
} from "../types";
|
|
81
|
+
|
|
82
|
+
export const REACTION_EMOJI_ALIASES: Record<string, string> = {
|
|
83
|
+
"+1": "👍",
|
|
84
|
+
":+1:": "👍",
|
|
85
|
+
"thumbsup": "👍",
|
|
86
|
+
":thumbsup:": "👍",
|
|
87
|
+
"thumbs-up": "👍",
|
|
88
|
+
":thumbs-up:": "👍",
|
|
89
|
+
"thumbs_up": "👍",
|
|
90
|
+
":thumbs_up:": "👍",
|
|
91
|
+
"thumb_up": "👍",
|
|
92
|
+
":thumb_up:": "👍",
|
|
93
|
+
"like": "👍",
|
|
94
|
+
":like:": "👍",
|
|
95
|
+
"heart": "❤️",
|
|
96
|
+
":heart:": "❤️",
|
|
97
|
+
"redheart": "❤️",
|
|
98
|
+
"red_heart": "❤️",
|
|
99
|
+
":red_heart:": "❤️",
|
|
100
|
+
"check": "✅",
|
|
101
|
+
":check:": "✅",
|
|
102
|
+
"checkmark": "✅",
|
|
103
|
+
":checkmark:": "✅",
|
|
104
|
+
"white_check_mark": "✅",
|
|
105
|
+
":white_check_mark:": "✅",
|
|
106
|
+
"eyes": "👀",
|
|
107
|
+
":eyes:": "👀",
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export function normalizeReactionEmoji(value: string): string {
|
|
111
|
+
const trimmed = value.trim();
|
|
112
|
+
return REACTION_EMOJI_ALIASES[trimmed.toLowerCase()] ?? trimmed;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function rowToAgent(row: any): AgentCard {
|
|
116
|
+
return {
|
|
117
|
+
id: row.id,
|
|
118
|
+
name: row.name,
|
|
119
|
+
kind: row.kind ?? "provider",
|
|
120
|
+
label: row.label ?? undefined,
|
|
121
|
+
tags: parseStringArray(row.tags),
|
|
122
|
+
machine: row.machine ?? undefined,
|
|
123
|
+
rig: row.rig ?? undefined,
|
|
124
|
+
capabilities: parseStringArray(row.capabilities),
|
|
125
|
+
ready: row.ready === 1,
|
|
126
|
+
status: row.status,
|
|
127
|
+
instanceId: row.instance_id ?? undefined,
|
|
128
|
+
epoch: typeof row.epoch === "number" ? row.epoch : 0,
|
|
129
|
+
providerCapabilities: parseJson<ProviderCapabilities | undefined>(row.provider_capabilities, undefined),
|
|
130
|
+
context: parseJson<ContextState | undefined>(row.context_state, undefined),
|
|
131
|
+
meta: parseJson(row.meta, {}),
|
|
132
|
+
spawnedBy: row.spawned_by ?? undefined,
|
|
133
|
+
lastSeen: row.last_seen,
|
|
134
|
+
createdAt: row.created_at,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function rowToContextSnapshot(row: any): ContextSnapshot {
|
|
139
|
+
const context = parseJson<ContextState>(row.context_state, {
|
|
140
|
+
utilization: row.utilization,
|
|
141
|
+
lifecycleState: row.lifecycle_state,
|
|
142
|
+
warmTopics: [],
|
|
143
|
+
activeMemories: [],
|
|
144
|
+
tasksSinceCompact: 0,
|
|
145
|
+
lastUpdatedAt: row.captured_at,
|
|
146
|
+
source: row.source,
|
|
147
|
+
confidence: row.confidence,
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
id: row.id,
|
|
151
|
+
agentId: row.agent_id,
|
|
152
|
+
context,
|
|
153
|
+
utilization: row.utilization,
|
|
154
|
+
lifecycleState: row.lifecycle_state,
|
|
155
|
+
tokensUsed: row.tokens_used ?? undefined,
|
|
156
|
+
tokensMax: row.tokens_max ?? undefined,
|
|
157
|
+
source: row.source,
|
|
158
|
+
confidence: row.confidence,
|
|
159
|
+
capturedAt: row.captured_at,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function rowToMessage(row: any): Message {
|
|
164
|
+
return {
|
|
165
|
+
id: row.id,
|
|
166
|
+
from: row.from_agent,
|
|
167
|
+
to: row.to_target,
|
|
168
|
+
kind: row.kind ?? "chat",
|
|
169
|
+
channel: row.channel ?? undefined,
|
|
170
|
+
subject: row.subject ?? undefined,
|
|
171
|
+
body: row.body,
|
|
172
|
+
threadId: row.thread_id ?? undefined,
|
|
173
|
+
replyTo: row.reply_to ?? undefined,
|
|
174
|
+
// Default (true) stays absent to match the `claimable` idiom and keep notification-free
|
|
175
|
+
// messages byte-identical on the wire; only an explicit notification surfaces false (#283).
|
|
176
|
+
replyExpected: row.reply_expected === 0 ? false : undefined,
|
|
177
|
+
claimable: row.claimable === 1 ? true : undefined,
|
|
178
|
+
claimedBy: row.claimed_by ?? undefined,
|
|
179
|
+
claimedAt: row.claimed_at ?? undefined,
|
|
180
|
+
claimExpiresAt: row.claim_expires_at ?? undefined,
|
|
181
|
+
idempotencyKey: row.idempotency_key ?? undefined,
|
|
182
|
+
deliveryStatus: row.delivery_status ?? "pending",
|
|
183
|
+
deliveryAttempts: row.delivery_attempts ?? 0,
|
|
184
|
+
deliveryLastError: row.delivery_last_error ?? undefined,
|
|
185
|
+
deliveryNextRetryAt: row.delivery_next_retry_at ?? undefined,
|
|
186
|
+
deliveryPoisonReason: row.delivery_poison_reason ?? undefined,
|
|
187
|
+
deliveryUpdatedAt: row.delivery_updated_at ?? undefined,
|
|
188
|
+
queuedAt: row.queued_at ?? undefined,
|
|
189
|
+
maxAgeSeconds: row.max_age_seconds ?? undefined,
|
|
190
|
+
resolvedToAgent: row.resolved_to_agent ?? undefined,
|
|
191
|
+
payload: parseJson(row.payload ?? "{}", {}),
|
|
192
|
+
meta: parseJson(row.meta, {}),
|
|
193
|
+
reactions: parseJson(row.reactions_json ?? "[]", []),
|
|
194
|
+
readBy: parseJson(row.read_by_agents ?? "[]", []),
|
|
195
|
+
createdAt: row.created_at,
|
|
196
|
+
occurredAt: row.occurred_at ?? undefined,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function rowToArtifactBlob(row: any): ArtifactBlob {
|
|
201
|
+
return {
|
|
202
|
+
digest: row.digest,
|
|
203
|
+
storageUri: row.storage_uri,
|
|
204
|
+
mediaType: row.media_type,
|
|
205
|
+
size: row.size,
|
|
206
|
+
createdAt: row.created_at,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function rowToArtifactLink(row: any): ArtifactLink {
|
|
211
|
+
return {
|
|
212
|
+
id: row.id,
|
|
213
|
+
artifactId: row.artifact_id,
|
|
214
|
+
entityType: row.entity_type,
|
|
215
|
+
entityId: row.entity_id,
|
|
216
|
+
role: row.role ?? undefined,
|
|
217
|
+
title: row.title ?? undefined,
|
|
218
|
+
createdBy: row.created_by,
|
|
219
|
+
createdAt: row.created_at,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function rowToArtifact(row: any, links?: ArtifactLink[]): Artifact {
|
|
224
|
+
return {
|
|
225
|
+
id: row.id,
|
|
226
|
+
blobDigest: row.blob_digest,
|
|
227
|
+
mediaType: row.media_type,
|
|
228
|
+
kind: row.kind ?? "other",
|
|
229
|
+
filename: row.filename ?? undefined,
|
|
230
|
+
size: row.size,
|
|
231
|
+
digest: row.blob_digest,
|
|
232
|
+
visibility: row.visibility ?? "project",
|
|
233
|
+
sensitivity: row.sensitivity ?? "normal",
|
|
234
|
+
createdBy: row.created_by,
|
|
235
|
+
createdAt: row.created_at,
|
|
236
|
+
expiresAt: row.expires_at ?? undefined,
|
|
237
|
+
metadata: parseJson(row.metadata, {}),
|
|
238
|
+
...(links ? { links } : {}),
|
|
239
|
+
url: `/api/artifacts/${encodeURIComponent(row.id)}/content`,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function rowToMessageDeliveryAttempt(row: any): MessageDeliveryAttempt {
|
|
244
|
+
return {
|
|
245
|
+
id: row.id,
|
|
246
|
+
messageId: row.message_id,
|
|
247
|
+
agentId: row.agent_id ?? undefined,
|
|
248
|
+
action: row.action,
|
|
249
|
+
status: row.status,
|
|
250
|
+
error: row.error ?? undefined,
|
|
251
|
+
nextRetryAt: row.next_retry_at ?? undefined,
|
|
252
|
+
poisonReason: row.poison_reason ?? undefined,
|
|
253
|
+
createdAt: row.created_at,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function rowToTask(row: any): Task {
|
|
258
|
+
return {
|
|
259
|
+
id: row.id,
|
|
260
|
+
source: row.source,
|
|
261
|
+
title: row.title,
|
|
262
|
+
body: row.body,
|
|
263
|
+
severity: row.severity as TaskSeverity,
|
|
264
|
+
status: row.status as TaskStatus,
|
|
265
|
+
target: row.target,
|
|
266
|
+
channel: row.channel ?? undefined,
|
|
267
|
+
dedupeKey: row.dedupe_key ?? undefined,
|
|
268
|
+
externalUrl: row.external_url ?? undefined,
|
|
269
|
+
occurrenceCount: row.occurrence_count,
|
|
270
|
+
claimedBy: row.claimed_by ?? undefined,
|
|
271
|
+
claimedAt: row.claimed_at ?? undefined,
|
|
272
|
+
claimExpiresAt: row.claim_expires_at ?? undefined,
|
|
273
|
+
messageId: row.message_id ?? undefined,
|
|
274
|
+
result: row.result ?? undefined,
|
|
275
|
+
metadata: parseJson(row.metadata, {}),
|
|
276
|
+
createdAt: row.created_at,
|
|
277
|
+
updatedAt: row.updated_at,
|
|
278
|
+
lastSeenAt: row.last_seen_at,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function rowToTaskEvent(row: any): TaskEvent {
|
|
283
|
+
return {
|
|
284
|
+
id: row.id,
|
|
285
|
+
taskId: row.task_id,
|
|
286
|
+
source: row.source,
|
|
287
|
+
type: row.type,
|
|
288
|
+
severity: row.severity as TaskSeverity,
|
|
289
|
+
title: row.title,
|
|
290
|
+
body: row.body,
|
|
291
|
+
metadata: parseJson(row.metadata, {}),
|
|
292
|
+
createdAt: row.created_at,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function rowToPair(row: any): PairSession {
|
|
297
|
+
return {
|
|
298
|
+
id: row.id,
|
|
299
|
+
requesterId: row.requester_id,
|
|
300
|
+
targetId: row.target_id,
|
|
301
|
+
status: row.status as PairStatus,
|
|
302
|
+
objective: row.objective ?? undefined,
|
|
303
|
+
createdAt: row.created_at,
|
|
304
|
+
updatedAt: row.updated_at,
|
|
305
|
+
expiresAt: row.expires_at,
|
|
306
|
+
acceptedAt: row.accepted_at ?? undefined,
|
|
307
|
+
endedAt: row.ended_at ?? undefined,
|
|
308
|
+
endedBy: row.ended_by ?? undefined,
|
|
309
|
+
lastMessageAt: row.last_message_at ?? undefined,
|
|
310
|
+
meta: parseJson(row.meta, {}),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function rowToInboxThreadState(row: any): InboxThreadState {
|
|
315
|
+
return {
|
|
316
|
+
operatorId: row.operator_id,
|
|
317
|
+
peerId: row.peer_id,
|
|
318
|
+
readCursorMessageId: row.read_cursor_message_id ?? undefined,
|
|
319
|
+
archivedAtMessageId: row.archived_at_message_id ?? undefined,
|
|
320
|
+
updatedAt: row.updated_at,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function rowToInboxDraft(row: any): InboxDraft {
|
|
325
|
+
return {
|
|
326
|
+
operatorId: row.operator_id,
|
|
327
|
+
peerId: row.peer_id,
|
|
328
|
+
body: row.body,
|
|
329
|
+
subject: row.subject ?? undefined,
|
|
330
|
+
channel: row.channel ?? undefined,
|
|
331
|
+
updatedAt: row.updated_at,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function rowToChatHistoryImport(row: any, entries: ChatHistoryImportEntry[]): ChatHistoryImport {
|
|
336
|
+
return {
|
|
337
|
+
id: row.id,
|
|
338
|
+
targetAgentId: row.target_agent_id ?? undefined,
|
|
339
|
+
targetSpawnRequestId: row.target_spawn_request_id ?? undefined,
|
|
340
|
+
sourcePeerId: row.source_peer_id,
|
|
341
|
+
sourceAgentId: row.source_agent_id ?? undefined,
|
|
342
|
+
sourceThreadId: row.source_thread_id ?? undefined,
|
|
343
|
+
sourceAgentLabel: row.source_agent_label ?? undefined,
|
|
344
|
+
importedBy: row.imported_by,
|
|
345
|
+
importedAt: row.imported_at,
|
|
346
|
+
entries,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function rowToChatHistoryImportEntry(row: any): ChatHistoryImportEntry {
|
|
351
|
+
const message = parseJson<Message>(row.message_snapshot, {} as Message);
|
|
352
|
+
return {
|
|
353
|
+
position: row.position,
|
|
354
|
+
originalMessageId: row.original_message_id,
|
|
355
|
+
originalFrom: row.original_from,
|
|
356
|
+
originalTo: row.original_to,
|
|
357
|
+
originalCreatedAt: row.original_created_at,
|
|
358
|
+
message,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function rowToActivityEvent(row: any): ActivityEvent {
|
|
363
|
+
return {
|
|
364
|
+
id: row.id,
|
|
365
|
+
operatorId: row.operator_id ?? undefined,
|
|
366
|
+
clientId: row.client_id ?? undefined,
|
|
367
|
+
kind: row.kind,
|
|
368
|
+
title: row.title,
|
|
369
|
+
body: row.body ?? undefined,
|
|
370
|
+
meta: row.meta_text ?? undefined,
|
|
371
|
+
icon: row.icon ?? undefined,
|
|
372
|
+
view: row.view ?? undefined,
|
|
373
|
+
peer: row.peer_id ?? undefined,
|
|
374
|
+
messageId: row.message_id ?? undefined,
|
|
375
|
+
pairId: row.pair_id ?? undefined,
|
|
376
|
+
taskId: row.task_id ?? undefined,
|
|
377
|
+
agentId: row.agent_id ?? undefined,
|
|
378
|
+
metadata: parseJson(row.metadata, {}),
|
|
379
|
+
createdAt: row.created_at,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
export const MSG_SELECT = `SELECT m.*, (
|
|
385
|
+
SELECT json_group_array(agent_id) FROM message_reads WHERE message_id = m.id
|
|
386
|
+
) AS read_by_agents, (
|
|
387
|
+
SELECT json_group_array(json_object(
|
|
388
|
+
'messageId', message_id,
|
|
389
|
+
'actorId', actor_id,
|
|
390
|
+
'emoji', emoji,
|
|
391
|
+
'createdAt', created_at,
|
|
392
|
+
'updatedAt', updated_at
|
|
393
|
+
)) FROM message_reactions WHERE message_id = m.id
|
|
394
|
+
) AS reactions_json FROM messages m`;
|
|
395
|
+
|
|
396
|
+
// --- Agents ---
|
|
397
|
+
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { isRecord, stringValue, isMechanicalMessageKind } from "agent-relay-sdk";
|
|
4
|
+
import { ORCHESTRATOR_PROTOCOL_VERSION, VERSION } from "../config.ts";
|
|
5
|
+
import { parseJson } from "../utils";
|
|
6
|
+
import { isLiveIsolatedWorkspace } from "../workspace-phase";
|
|
7
|
+
import {
|
|
8
|
+
CONTRACT_REQUIREMENTS,
|
|
9
|
+
contractCompatibility,
|
|
10
|
+
parseRuntimeCapabilities,
|
|
11
|
+
parseRuntimeContracts,
|
|
12
|
+
parseRuntimePackage,
|
|
13
|
+
type RuntimeContracts,
|
|
14
|
+
} from "../contracts";
|
|
15
|
+
import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
|
|
16
|
+
import { matchAgents } from "../agent-ref";
|
|
17
|
+
import { getDb } from "./connection.ts";
|
|
18
|
+
import type {
|
|
19
|
+
AgentCard,
|
|
20
|
+
ActivityEvent,
|
|
21
|
+
ActivityEventInput,
|
|
22
|
+
AgentKind,
|
|
23
|
+
AgentSessionGuard,
|
|
24
|
+
Artifact,
|
|
25
|
+
ArtifactBlob,
|
|
26
|
+
ArtifactKind,
|
|
27
|
+
ArtifactLink,
|
|
28
|
+
ArtifactSensitivity,
|
|
29
|
+
ArtifactVisibility,
|
|
30
|
+
AttachmentRef,
|
|
31
|
+
ChannelBinding,
|
|
32
|
+
ChannelBindingMode,
|
|
33
|
+
ChannelRouteTarget,
|
|
34
|
+
ChatHistoryImport,
|
|
35
|
+
ChatHistoryImportEntry,
|
|
36
|
+
ChannelSummary,
|
|
37
|
+
ChannelTargetHealth,
|
|
38
|
+
CreatePairInput,
|
|
39
|
+
HealthCheck,
|
|
40
|
+
HealthReport,
|
|
41
|
+
ManagedAgent,
|
|
42
|
+
ManagedSessionExitDiagnostics,
|
|
43
|
+
Message,
|
|
44
|
+
MessageDeliveryAttempt,
|
|
45
|
+
MessageDeliveryStatus,
|
|
46
|
+
Orchestrator,
|
|
47
|
+
OrchestratorHealth,
|
|
48
|
+
OrchestratorRuntimeInput,
|
|
49
|
+
OrchestratorStatus,
|
|
50
|
+
OrchestratorUpgradeState,
|
|
51
|
+
PairActionInput,
|
|
52
|
+
PairMessageInput,
|
|
53
|
+
PairSession,
|
|
54
|
+
PairStatus,
|
|
55
|
+
RegisterAgentInput,
|
|
56
|
+
ReplyObligation,
|
|
57
|
+
RegisterOrchestratorInput,
|
|
58
|
+
SendMessageInput,
|
|
59
|
+
PollQuery,
|
|
60
|
+
SpawnApprovalMode,
|
|
61
|
+
SpawnProvider,
|
|
62
|
+
Task,
|
|
63
|
+
TaskEvent,
|
|
64
|
+
TaskSeverity,
|
|
65
|
+
TaskStatus,
|
|
66
|
+
IntegrationEventInput,
|
|
67
|
+
IntegrationSummary,
|
|
68
|
+
IntegrationTaskStats,
|
|
69
|
+
InboxDraft,
|
|
70
|
+
InboxState,
|
|
71
|
+
InboxThreadState,
|
|
72
|
+
ContextSnapshot,
|
|
73
|
+
ContextState,
|
|
74
|
+
ProviderCapabilities,
|
|
75
|
+
TaskStatusInput,
|
|
76
|
+
WorkspaceMetadata,
|
|
77
|
+
WorkspaceRecord,
|
|
78
|
+
WorkspaceStatus,
|
|
79
|
+
} from "../types";
|
|
80
|
+
|
|
81
|
+
// --- Per-repo merge serialization lease (issue #157) -----------------------
|
|
82
|
+
|
|
83
|
+
export interface MergeLeaseRecord {
|
|
84
|
+
repoRoot: string;
|
|
85
|
+
workspaceId: string;
|
|
86
|
+
commandId?: string;
|
|
87
|
+
holder?: string;
|
|
88
|
+
acquiredAt: number;
|
|
89
|
+
expiresAt: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function rowToMergeLease(row: any): MergeLeaseRecord {
|
|
93
|
+
return {
|
|
94
|
+
repoRoot: row.repo_root,
|
|
95
|
+
workspaceId: row.workspace_id,
|
|
96
|
+
commandId: row.command_id ?? undefined,
|
|
97
|
+
holder: row.holder ?? undefined,
|
|
98
|
+
acquiredAt: row.acquired_at,
|
|
99
|
+
expiresAt: row.expires_at,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getMergeLease(repoRoot: string): MergeLeaseRecord | null {
|
|
104
|
+
const row = getDb().query("SELECT * FROM workspace_merge_leases WHERE repo_root = ?").get(repoRoot) as any;
|
|
105
|
+
return row ? rowToMergeLease(row) : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function listMergeLeases(): MergeLeaseRecord[] {
|
|
109
|
+
return (getDb().query("SELECT * FROM workspace_merge_leases ORDER BY acquired_at DESC").all() as any[]).map(rowToMergeLease);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function releaseExpiredMergeLeases(now: number = Date.now()): string[] {
|
|
113
|
+
const expired = getDb().query("SELECT repo_root FROM workspace_merge_leases WHERE expires_at <= ?").all(now) as Array<{ repo_root: string }>;
|
|
114
|
+
if (!expired.length) return [];
|
|
115
|
+
getDb().query("DELETE FROM workspace_merge_leases WHERE expires_at <= ?").run(now);
|
|
116
|
+
return expired.map((r) => r.repo_root);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Atomically acquire the per-repo merge lease. Succeeds if no live lease is held
|
|
120
|
+
// for the repo (or the existing one has expired). Serialized via getDb().transaction
|
|
121
|
+
// so two concurrent merge requests for the same repo can't both win.
|
|
122
|
+
export function acquireMergeLease(
|
|
123
|
+
repoRoot: string,
|
|
124
|
+
workspaceId: string,
|
|
125
|
+
holder?: string,
|
|
126
|
+
): { ok: true; lease: MergeLeaseRecord } | { ok: false; lease: MergeLeaseRecord } {
|
|
127
|
+
return getDb().transaction(() => {
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
const existing = getMergeLease(repoRoot);
|
|
130
|
+
if (existing && existing.expiresAt > now) return { ok: false as const, lease: existing };
|
|
131
|
+
const expiresAt = now + WORKSPACE_MERGE_LEASE_MS;
|
|
132
|
+
getDb().query(`
|
|
133
|
+
INSERT INTO workspace_merge_leases (repo_root, workspace_id, command_id, holder, acquired_at, expires_at)
|
|
134
|
+
VALUES (?, ?, NULL, ?, ?, ?)
|
|
135
|
+
ON CONFLICT(repo_root) DO UPDATE SET
|
|
136
|
+
workspace_id = excluded.workspace_id, command_id = NULL, holder = excluded.holder,
|
|
137
|
+
acquired_at = excluded.acquired_at, expires_at = excluded.expires_at
|
|
138
|
+
`).run(repoRoot, workspaceId, holder ?? null, now, expiresAt);
|
|
139
|
+
return { ok: true as const, lease: getMergeLease(repoRoot)! };
|
|
140
|
+
})();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Attach the dispatched command id to a held lease so it can be released by
|
|
144
|
+
// command id when the merge settles.
|
|
145
|
+
export function setMergeLeaseCommand(repoRoot: string, commandId: string): void {
|
|
146
|
+
getDb().query("UPDATE workspace_merge_leases SET command_id = ? WHERE repo_root = ?").run(commandId, repoRoot);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Release a merge lease. Guard by commandId/workspaceId when known so a stale
|
|
150
|
+
// release can't drop a newer lease for the same repo.
|
|
151
|
+
export function releaseMergeLease(opts: { repoRoot?: string; commandId?: string; workspaceId?: string }): boolean {
|
|
152
|
+
const where: string[] = [];
|
|
153
|
+
const params: string[] = [];
|
|
154
|
+
if (opts.repoRoot) { where.push("repo_root = ?"); params.push(opts.repoRoot); }
|
|
155
|
+
if (opts.commandId) { where.push("command_id = ?"); params.push(opts.commandId); }
|
|
156
|
+
if (opts.workspaceId) { where.push("workspace_id = ?"); params.push(opts.workspaceId); }
|
|
157
|
+
if (!where.length) return false;
|
|
158
|
+
return getDb().query(`DELETE FROM workspace_merge_leases WHERE ${where.join(" AND ")}`).run(...params).changes > 0;
|
|
159
|
+
}
|
|
160
|
+
|