dsh-agora-plugin 0.6.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 +520 -0
- package/client/index.js +492 -0
- package/cordis.patch.yml +14 -0
- package/dsh.plugin.json +7 -0
- package/lib/agora-client.d.ts +73 -0
- package/lib/agora-client.d.ts.map +1 -0
- package/lib/agora-client.js +207 -0
- package/lib/agora-client.js.map +1 -0
- package/lib/client.js +492 -0
- package/lib/command-adapter.d.ts +44 -0
- package/lib/command-adapter.d.ts.map +1 -0
- package/lib/command-adapter.js +70 -0
- package/lib/command-adapter.js.map +1 -0
- package/lib/command.d.ts +53 -0
- package/lib/command.d.ts.map +1 -0
- package/lib/command.js +375 -0
- package/lib/command.js.map +1 -0
- package/lib/context-types.d.ts +51 -0
- package/lib/context-types.d.ts.map +1 -0
- package/lib/context-types.js +2 -0
- package/lib/context-types.js.map +1 -0
- package/lib/contracts.d.ts +402 -0
- package/lib/contracts.d.ts.map +1 -0
- package/lib/contracts.js +2 -0
- package/lib/contracts.js.map +1 -0
- package/lib/extension-sdk.d.ts +74 -0
- package/lib/extension-sdk.d.ts.map +1 -0
- package/lib/extension-sdk.js +160 -0
- package/lib/extension-sdk.js.map +1 -0
- package/lib/harness-runtime.d.ts +29 -0
- package/lib/harness-runtime.d.ts.map +1 -0
- package/lib/harness-runtime.js +422 -0
- package/lib/harness-runtime.js.map +1 -0
- package/lib/http-api.d.ts +10 -0
- package/lib/http-api.d.ts.map +1 -0
- package/lib/http-api.js +282 -0
- package/lib/http-api.js.map +1 -0
- package/lib/im-bridge-v1.d.ts +37 -0
- package/lib/im-bridge-v1.d.ts.map +1 -0
- package/lib/im-bridge-v1.js +43 -0
- package/lib/im-bridge-v1.js.map +1 -0
- package/lib/im-gateway.d.ts +24 -0
- package/lib/im-gateway.d.ts.map +1 -0
- package/lib/im-gateway.js +53 -0
- package/lib/im-gateway.js.map +1 -0
- package/lib/index.d.ts +46 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +210 -0
- package/lib/index.js.map +1 -0
- package/lib/node-worker.d.ts +52 -0
- package/lib/node-worker.d.ts.map +1 -0
- package/lib/node-worker.js +372 -0
- package/lib/node-worker.js.map +1 -0
- package/lib/service.d.ts +51 -0
- package/lib/service.d.ts.map +1 -0
- package/lib/service.js +196 -0
- package/lib/service.js.map +1 -0
- package/lib/tool.d.ts +72 -0
- package/lib/tool.d.ts.map +1 -0
- package/lib/tool.js +206 -0
- package/lib/tool.js.map +1 -0
- package/package.json +74 -0
- package/patches/dsh-im/@xmanrui__dsh-im@2.1.0.patch +920 -0
- package/patches/dsh-im/@xmanrui__dsh-im@2.3.0.patch +984 -0
- package/patches/dsh-im/README.md +82 -0
- package/src/agora-client.ts +333 -0
- package/src/command-adapter.ts +106 -0
- package/src/command.ts +424 -0
- package/src/context-types.ts +54 -0
- package/src/contracts.ts +413 -0
- package/src/extension-sdk.ts +225 -0
- package/src/harness-runtime.ts +518 -0
- package/src/http-api.ts +269 -0
- package/src/im-bridge-v1.ts +73 -0
- package/src/im-gateway.ts +82 -0
- package/src/index.ts +260 -0
- package/src/node-worker.ts +442 -0
- package/src/service.ts +262 -0
- package/src/tool.ts +269 -0
package/src/contracts.ts
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
export type TaskPriority = 'low' | 'normal' | 'high'
|
|
2
|
+
|
|
3
|
+
export interface AgoraTask {
|
|
4
|
+
readonly id: string
|
|
5
|
+
readonly version?: number
|
|
6
|
+
readonly title: string
|
|
7
|
+
readonly description?: string | null
|
|
8
|
+
readonly type: string
|
|
9
|
+
readonly priority: TaskPriority
|
|
10
|
+
readonly creator: string
|
|
11
|
+
readonly locale?: string
|
|
12
|
+
readonly project_id?: string | null
|
|
13
|
+
readonly state: string
|
|
14
|
+
readonly current_stage: string | null
|
|
15
|
+
readonly error_detail?: string | null
|
|
16
|
+
readonly created_at?: string
|
|
17
|
+
readonly updated_at?: string
|
|
18
|
+
readonly [key: string]: unknown
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AgoraTaskStatus {
|
|
22
|
+
readonly task: AgoraTask
|
|
23
|
+
readonly flow_log: readonly Record<string, unknown>[]
|
|
24
|
+
readonly progress_log: readonly Record<string, unknown>[]
|
|
25
|
+
readonly subtasks: readonly Record<string, unknown>[]
|
|
26
|
+
readonly [key: string]: unknown
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AgoraHealth {
|
|
30
|
+
readonly ok?: boolean
|
|
31
|
+
readonly status?: string
|
|
32
|
+
readonly service?: string
|
|
33
|
+
readonly [key: string]: unknown
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const DSH_AGORA_NODE_PROTOCOL = 'dsh-agora.node/v1' as const
|
|
37
|
+
|
|
38
|
+
export interface RuntimeNodeAgent {
|
|
39
|
+
readonly agent_ref: string
|
|
40
|
+
readonly display_name?: string | null
|
|
41
|
+
readonly preset?: string | null
|
|
42
|
+
readonly model?: string | null
|
|
43
|
+
readonly workspace_alias?: string | null
|
|
44
|
+
readonly roles: readonly string[]
|
|
45
|
+
readonly capabilities: readonly string[]
|
|
46
|
+
readonly metadata?: Readonly<Record<string, unknown>> | null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface RuntimeNodeBot {
|
|
50
|
+
readonly provider: string
|
|
51
|
+
readonly bot_ref: string
|
|
52
|
+
readonly platform_id?: string | null
|
|
53
|
+
readonly display_name?: string | null
|
|
54
|
+
readonly agent_ref?: string | null
|
|
55
|
+
readonly connected: boolean
|
|
56
|
+
readonly capabilities: readonly string[]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface RuntimeNodeHeartbeatInput {
|
|
60
|
+
readonly protocol: typeof DSH_AGORA_NODE_PROTOCOL
|
|
61
|
+
readonly instance_id: string
|
|
62
|
+
readonly plugin_version: string
|
|
63
|
+
readonly host_framework: 'deepseek-harness'
|
|
64
|
+
readonly runtime_provider: 'dsh'
|
|
65
|
+
readonly agents: readonly RuntimeNodeAgent[]
|
|
66
|
+
readonly bots: readonly RuntimeNodeBot[]
|
|
67
|
+
readonly capacity: { readonly max_concurrent: number; readonly active: number }
|
|
68
|
+
readonly lease_seconds: number
|
|
69
|
+
readonly metadata?: Readonly<Record<string, unknown>> | null
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface RuntimeNode extends RuntimeNodeHeartbeatInput {
|
|
73
|
+
readonly node_id: string
|
|
74
|
+
readonly presence: 'online' | 'stale'
|
|
75
|
+
readonly registered_at: string
|
|
76
|
+
readonly last_seen_at: string
|
|
77
|
+
readonly expires_at: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface RuntimeTarget {
|
|
81
|
+
readonly runtime_target_ref: string
|
|
82
|
+
readonly runtime_provider: string | null
|
|
83
|
+
readonly runtime_flavor: string | null
|
|
84
|
+
readonly host_framework: string | null
|
|
85
|
+
readonly primary_model: string | null
|
|
86
|
+
readonly channel_providers: readonly string[]
|
|
87
|
+
readonly enabled: boolean
|
|
88
|
+
readonly display_name: string | null
|
|
89
|
+
readonly presentation_mode: 'headless' | 'im_presented'
|
|
90
|
+
readonly presentation_provider: string | null
|
|
91
|
+
readonly presentation_identity_ref: string | null
|
|
92
|
+
readonly [key: string]: unknown
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface CreateRuntimeDispatchInput {
|
|
96
|
+
readonly task_id?: string | null
|
|
97
|
+
readonly participant_binding_id?: string | null
|
|
98
|
+
readonly runtime_target_ref: string
|
|
99
|
+
readonly session_id?: string | null
|
|
100
|
+
readonly workspace_alias?: string | null
|
|
101
|
+
readonly agent_preset?: string | null
|
|
102
|
+
readonly prompt: string
|
|
103
|
+
readonly idempotency_key: string
|
|
104
|
+
readonly metadata?: Readonly<Record<string, unknown>> | null
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface DispatchAgentInput extends CreateRuntimeDispatchInput {
|
|
108
|
+
readonly source_session_id?: string
|
|
109
|
+
readonly wait_timeout_ms?: number
|
|
110
|
+
readonly presentation_mode?: 'source_bot' | 'destination_bot' | 'silent'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface RuntimeDispatch extends CreateRuntimeDispatchInput {
|
|
114
|
+
readonly id: string
|
|
115
|
+
readonly node_id: string
|
|
116
|
+
readonly status: 'pending' | 'claimed' | 'completed' | 'failed' | 'cancelled'
|
|
117
|
+
readonly claimed_by: string | null
|
|
118
|
+
readonly claim_token: string | null
|
|
119
|
+
readonly claim_expires_at: string | null
|
|
120
|
+
readonly attempt: number
|
|
121
|
+
readonly claimed_at: string | null
|
|
122
|
+
readonly claim_renewed_at: string | null
|
|
123
|
+
readonly latest_progress?: RuntimeDispatchProgress | null
|
|
124
|
+
readonly progress_updated_at?: string | null
|
|
125
|
+
readonly result: Readonly<Record<string, unknown>> | null
|
|
126
|
+
readonly result_envelope?: RuntimeResultEnvelope | null
|
|
127
|
+
readonly error: string | null
|
|
128
|
+
readonly created_at: string
|
|
129
|
+
readonly updated_at: string
|
|
130
|
+
readonly completed_at: string | null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface RuntimeDispatchProgressInput {
|
|
134
|
+
readonly phase: string
|
|
135
|
+
readonly message?: string | null
|
|
136
|
+
readonly percent?: number | null
|
|
137
|
+
readonly details?: Readonly<Record<string, unknown>> | null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface RecordRuntimeDispatchProgressInput extends RuntimeDispatchProgressInput {
|
|
141
|
+
readonly instance_id: string
|
|
142
|
+
readonly claim_token: string
|
|
143
|
+
readonly sequence: number
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface RuntimeDispatchProgress extends RuntimeDispatchProgressInput {
|
|
147
|
+
readonly id: string
|
|
148
|
+
readonly dispatch_id: string
|
|
149
|
+
readonly node_id: string
|
|
150
|
+
readonly instance_id: string
|
|
151
|
+
readonly attempt: number
|
|
152
|
+
readonly sequence: number
|
|
153
|
+
readonly created_at: string
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface RuntimeResultEvidence {
|
|
157
|
+
readonly id: string
|
|
158
|
+
readonly kind: 'file' | 'url' | 'commit' | 'measurement' | 'log' | 'command' | 'other'
|
|
159
|
+
readonly label?: string | null
|
|
160
|
+
readonly uri?: string | null
|
|
161
|
+
readonly content_hash?: string | null
|
|
162
|
+
readonly revision?: string | null
|
|
163
|
+
readonly line_start?: number | null
|
|
164
|
+
readonly line_end?: number | null
|
|
165
|
+
readonly metadata?: Readonly<Record<string, unknown>> | null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface RuntimeResultClaim {
|
|
169
|
+
readonly id: string
|
|
170
|
+
readonly statement: string
|
|
171
|
+
readonly evidence_ids: readonly string[]
|
|
172
|
+
readonly confidence?: number | null
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface RuntimeResultEnvelope {
|
|
176
|
+
readonly schema: 'agora.runtime-result/v1'
|
|
177
|
+
readonly answer: string
|
|
178
|
+
readonly claims: readonly RuntimeResultClaim[]
|
|
179
|
+
readonly evidence: readonly RuntimeResultEvidence[]
|
|
180
|
+
readonly confidence?: number | null
|
|
181
|
+
readonly environment?: {
|
|
182
|
+
readonly runtime_provider: string
|
|
183
|
+
readonly agent_ref?: string | null
|
|
184
|
+
readonly model?: string | null
|
|
185
|
+
readonly workspace_alias?: string | null
|
|
186
|
+
readonly revision?: string | null
|
|
187
|
+
readonly metadata?: Readonly<Record<string, unknown>> | null
|
|
188
|
+
} | null
|
|
189
|
+
readonly usage?: RuntimeUsage | null
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface RuntimeUsage {
|
|
193
|
+
readonly input_tokens: number | null
|
|
194
|
+
readonly output_tokens: number | null
|
|
195
|
+
readonly total_tokens: number | null
|
|
196
|
+
readonly tool_calls: number | null
|
|
197
|
+
readonly cost_usd: number | null
|
|
198
|
+
readonly duration_ms: number | null
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export type CoordinationMode = 'single' | 'fanout' | 'review' | 'debate' | 'council'
|
|
202
|
+
export type CoordinationRunStatus = 'running' | 'verifying' | 'completed' | 'partial' | 'failed' | 'cancelled' | 'budget_exhausted'
|
|
203
|
+
|
|
204
|
+
export interface CoordinationBudget {
|
|
205
|
+
readonly max_agents: number
|
|
206
|
+
readonly max_dispatches: number
|
|
207
|
+
readonly max_wall_clock_seconds: number
|
|
208
|
+
readonly max_tokens: number | null
|
|
209
|
+
readonly max_tool_calls: number | null
|
|
210
|
+
readonly max_cost_usd: number | null
|
|
211
|
+
readonly min_information_gain: number
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface CreateCoordinationRunInput {
|
|
215
|
+
readonly task_id?: string | null
|
|
216
|
+
readonly task_type?: string
|
|
217
|
+
readonly prompt: string
|
|
218
|
+
readonly mode: CoordinationMode
|
|
219
|
+
readonly candidates: readonly {
|
|
220
|
+
readonly runtime_target_ref: string
|
|
221
|
+
readonly capabilities?: readonly string[]
|
|
222
|
+
readonly priority?: number
|
|
223
|
+
}[]
|
|
224
|
+
readonly verifier_target_ref?: string | null
|
|
225
|
+
readonly budget?: Partial<CoordinationBudget>
|
|
226
|
+
readonly memory_scopes?: readonly ('task' | 'agent_private' | 'project_shared' | 'decision' | 'episodic')[]
|
|
227
|
+
readonly idempotency_key: string
|
|
228
|
+
readonly metadata?: Readonly<Record<string, unknown>> | null
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface CoordinationRun {
|
|
232
|
+
readonly id: string
|
|
233
|
+
readonly task_id: string | null
|
|
234
|
+
readonly task_type: string
|
|
235
|
+
readonly prompt: string
|
|
236
|
+
readonly mode: CoordinationMode
|
|
237
|
+
readonly status: CoordinationRunStatus
|
|
238
|
+
readonly budget: CoordinationBudget
|
|
239
|
+
readonly usage: RuntimeUsage
|
|
240
|
+
readonly synthesis: {
|
|
241
|
+
readonly answer: string
|
|
242
|
+
readonly agreements: readonly string[]
|
|
243
|
+
readonly conflicts: readonly {
|
|
244
|
+
readonly id: string
|
|
245
|
+
readonly kind: 'claim_conflict' | 'environment_drift' | 'unsupported_claim'
|
|
246
|
+
readonly key: string
|
|
247
|
+
readonly member_ids: readonly string[]
|
|
248
|
+
readonly statements: readonly string[]
|
|
249
|
+
readonly detail: string
|
|
250
|
+
readonly resolved: boolean
|
|
251
|
+
}[]
|
|
252
|
+
readonly evidence_ids: readonly string[]
|
|
253
|
+
readonly verified: boolean
|
|
254
|
+
readonly information_gain: number
|
|
255
|
+
readonly result_envelope: RuntimeResultEnvelope | null
|
|
256
|
+
} | null
|
|
257
|
+
readonly stop_reason: string | null
|
|
258
|
+
readonly deadline_at: string
|
|
259
|
+
readonly created_at: string
|
|
260
|
+
readonly updated_at: string
|
|
261
|
+
readonly completed_at: string | null
|
|
262
|
+
readonly members: readonly {
|
|
263
|
+
readonly id: string
|
|
264
|
+
readonly dispatch_id: string
|
|
265
|
+
readonly runtime_target_ref: string
|
|
266
|
+
readonly role: 'primary' | 'investigator' | 'reviewer' | 'verifier' | 'arbitrator'
|
|
267
|
+
readonly round: number
|
|
268
|
+
readonly status: 'dispatched' | 'claimed' | 'completed' | 'failed' | 'cancelled'
|
|
269
|
+
readonly selection_score: number
|
|
270
|
+
readonly selection_reason: readonly string[]
|
|
271
|
+
readonly result_envelope: RuntimeResultEnvelope | null
|
|
272
|
+
readonly usage: RuntimeUsage | null
|
|
273
|
+
}[]
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export interface AgentScorecard {
|
|
277
|
+
readonly runtime_target_ref: string
|
|
278
|
+
readonly task_type: string
|
|
279
|
+
readonly observations: number
|
|
280
|
+
readonly success_rate: number | null
|
|
281
|
+
readonly failure_rate: number | null
|
|
282
|
+
readonly cancellation_rate: number | null
|
|
283
|
+
readonly retry_rate: number | null
|
|
284
|
+
readonly timeout_rate: number | null
|
|
285
|
+
readonly median_duration_ms: number | null
|
|
286
|
+
readonly p95_duration_ms: number | null
|
|
287
|
+
readonly evidence_yield: number | null
|
|
288
|
+
readonly verifier_acceptance_rate: number | null
|
|
289
|
+
readonly agreement_rate: number | null
|
|
290
|
+
readonly unique_information_rate: number | null
|
|
291
|
+
readonly environment_drift_rate: number | null
|
|
292
|
+
readonly total_tokens: number | null
|
|
293
|
+
readonly total_cost_usd: number | null
|
|
294
|
+
readonly score: number
|
|
295
|
+
readonly updated_at: string | null
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export interface RuntimeDelivery {
|
|
299
|
+
readonly id: string
|
|
300
|
+
readonly dispatch_id: string
|
|
301
|
+
readonly node_id: string
|
|
302
|
+
readonly payload: Readonly<Record<string, unknown>>
|
|
303
|
+
readonly status: 'pending' | 'claimed' | 'delivered' | 'failed'
|
|
304
|
+
readonly attempt: number
|
|
305
|
+
readonly claimed_by: string | null
|
|
306
|
+
readonly claim_token: string | null
|
|
307
|
+
readonly claim_expires_at: string | null
|
|
308
|
+
readonly next_attempt_at: string
|
|
309
|
+
readonly receipt: Readonly<Record<string, unknown>> | null
|
|
310
|
+
readonly error: string | null
|
|
311
|
+
readonly created_at: string
|
|
312
|
+
readonly updated_at: string
|
|
313
|
+
readonly delivered_at: string | null
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export interface RuntimeSessionBinding {
|
|
317
|
+
readonly id: string
|
|
318
|
+
readonly participant_binding_id: string
|
|
319
|
+
readonly runtime_provider: 'dsh'
|
|
320
|
+
readonly runtime_session_ref: string
|
|
321
|
+
readonly runtime_actor_ref: string | null
|
|
322
|
+
readonly continuity_ref: string | null
|
|
323
|
+
readonly presence_state: 'active' | 'idle' | 'closed'
|
|
324
|
+
readonly last_seen_at: string
|
|
325
|
+
readonly [key: string]: unknown
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export interface AgoraImTarget {
|
|
329
|
+
readonly provider?: string
|
|
330
|
+
readonly conversation_ref?: string
|
|
331
|
+
readonly thread_ref?: string
|
|
332
|
+
readonly visibility?: 'public' | 'private'
|
|
333
|
+
readonly participant_refs?: readonly string[]
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export interface CreateAgoraTaskInput {
|
|
337
|
+
readonly title: string
|
|
338
|
+
readonly type?: string
|
|
339
|
+
readonly creator?: string
|
|
340
|
+
readonly description?: string
|
|
341
|
+
readonly priority?: TaskPriority
|
|
342
|
+
readonly locale?: 'zh-CN' | 'en-US'
|
|
343
|
+
readonly projectId?: string
|
|
344
|
+
readonly imTarget?: AgoraImTarget
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface AgoraRequestContext {
|
|
348
|
+
readonly actorId?: string
|
|
349
|
+
readonly provider?: string
|
|
350
|
+
readonly conversationRef?: string
|
|
351
|
+
readonly threadRef?: string
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export type AgoraCommandResult =
|
|
355
|
+
| { readonly kind: 'success'; readonly text?: string }
|
|
356
|
+
| { readonly kind: 'error'; readonly text: string }
|
|
357
|
+
|
|
358
|
+
export type DshAgoraImStatus =
|
|
359
|
+
| { readonly state: 'connected'; readonly service: string; readonly protocol: string }
|
|
360
|
+
| { readonly state: 'unavailable'; readonly reason: string }
|
|
361
|
+
| { readonly state: 'incompatible'; readonly service: string; readonly reason: string }
|
|
362
|
+
|
|
363
|
+
export interface DshAgoraSnapshot {
|
|
364
|
+
readonly serverUrl: string
|
|
365
|
+
readonly command: string
|
|
366
|
+
readonly im: DshAgoraImStatus
|
|
367
|
+
readonly imBridge: DshAgoraImStatus
|
|
368
|
+
readonly commandAdapter: {
|
|
369
|
+
readonly state: 'ready'
|
|
370
|
+
readonly protocol: 'dsh-agora.command-adapter/v1'
|
|
371
|
+
}
|
|
372
|
+
readonly node: DshAgoraNodeStatus
|
|
373
|
+
readonly extensions: readonly DshAgoraExtensionSummary[]
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export type DshAgoraNodeStatus =
|
|
377
|
+
| { readonly state: 'disabled'; readonly nodeId: string }
|
|
378
|
+
| { readonly state: 'connecting'; readonly nodeId: string }
|
|
379
|
+
| { readonly state: 'online'; readonly nodeId: string; readonly lastHeartbeatAt: string }
|
|
380
|
+
| { readonly state: 'error'; readonly nodeId: string; readonly error: string; readonly lastHeartbeatAt?: string }
|
|
381
|
+
|
|
382
|
+
export interface DshAgoraExtensionSummary {
|
|
383
|
+
readonly id: string
|
|
384
|
+
readonly protocol: string
|
|
385
|
+
readonly kind: string
|
|
386
|
+
readonly capabilities: readonly string[]
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export interface DshAgoraServiceApi {
|
|
390
|
+
readonly serverUrl: string
|
|
391
|
+
health(signal?: AbortSignal): Promise<AgoraHealth>
|
|
392
|
+
listTasks(state?: string, projectId?: string, signal?: AbortSignal): Promise<AgoraTask[]>
|
|
393
|
+
getTask(taskId: string, signal?: AbortSignal): Promise<AgoraTask>
|
|
394
|
+
taskStatus(taskId: string, signal?: AbortSignal): Promise<AgoraTaskStatus>
|
|
395
|
+
createTask(input: CreateAgoraTaskInput, signal?: AbortSignal): Promise<AgoraTask>
|
|
396
|
+
listRuntimeNodes(signal?: AbortSignal): Promise<RuntimeNode[]>
|
|
397
|
+
listRuntimeTargets(signal?: AbortSignal): Promise<RuntimeTarget[]>
|
|
398
|
+
createRuntimeDispatch(nodeId: string, input: CreateRuntimeDispatchInput, signal?: AbortSignal): Promise<RuntimeDispatch>
|
|
399
|
+
getRuntimeDispatch(dispatchId: string, signal?: AbortSignal): Promise<RuntimeDispatch>
|
|
400
|
+
listRuntimeDispatchProgress(dispatchId: string, signal?: AbortSignal): Promise<RuntimeDispatchProgress[]>
|
|
401
|
+
createCoordinationRun(input: CreateCoordinationRunInput, signal?: AbortSignal): Promise<CoordinationRun>
|
|
402
|
+
getCoordinationRun(runId: string, signal?: AbortSignal): Promise<CoordinationRun>
|
|
403
|
+
listCoordinationRuns(status?: CoordinationRunStatus, signal?: AbortSignal): Promise<CoordinationRun[]>
|
|
404
|
+
listAgentScorecards(taskType?: string, signal?: AbortSignal): Promise<AgentScorecard[]>
|
|
405
|
+
dispatchAgent(input: DispatchAgentInput, signal?: AbortSignal): Promise<RuntimeDispatch>
|
|
406
|
+
bindRuntimeSession(taskId: string, participantBindingId: string, sessionId: string, agentRef?: string, signal?: AbortSignal): Promise<RuntimeSessionBinding>
|
|
407
|
+
executeCommand(rawInput: string, context?: AgoraRequestContext, signal?: AbortSignal): Promise<AgoraCommandResult>
|
|
408
|
+
executeCommandEvent(
|
|
409
|
+
event: import('./command-adapter.js').DshAgoraCommandEventV1,
|
|
410
|
+
signal?: AbortSignal,
|
|
411
|
+
): Promise<import('./command-adapter.js').DshAgoraCommandEventResultV1>
|
|
412
|
+
snapshot(): DshAgoraSnapshot
|
|
413
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { createHash, createPublicKey, verify as verifySignature } from 'node:crypto'
|
|
2
|
+
import type {
|
|
3
|
+
RuntimeDispatch,
|
|
4
|
+
RuntimeDispatchProgressInput,
|
|
5
|
+
RuntimeNodeAgent,
|
|
6
|
+
RuntimeResultEnvelope,
|
|
7
|
+
} from './contracts.js'
|
|
8
|
+
|
|
9
|
+
export const DSH_AGORA_EXTENSION_PROTOCOL = 'dsh-agora.extension/v1' as const
|
|
10
|
+
export const DSH_AGORA_RUNTIME_PROTOCOL = 'dsh-agora.runtime/v1' as const
|
|
11
|
+
export const DSH_AGORA_EXTENSION_MANIFEST_PROTOCOL = 'dsh-agora.extension-manifest/v1' as const
|
|
12
|
+
|
|
13
|
+
export interface RuntimeExecutionResult {
|
|
14
|
+
readonly sessionId: string
|
|
15
|
+
readonly answer: string
|
|
16
|
+
readonly reason?: string | null
|
|
17
|
+
readonly metadata?: Readonly<Record<string, unknown>>
|
|
18
|
+
readonly resultEnvelope?: RuntimeResultEnvelope
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RuntimeExecutionContext {
|
|
22
|
+
reportProgress(event: RuntimeDispatchProgressInput): Promise<void>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DshAgoraRuntimeAdapterV1 {
|
|
26
|
+
readonly protocol: typeof DSH_AGORA_RUNTIME_PROTOCOL
|
|
27
|
+
supportsTarget?(runtimeTargetRef: string): boolean
|
|
28
|
+
describeAgents(): readonly RuntimeNodeAgent[] | Promise<readonly RuntimeNodeAgent[]>
|
|
29
|
+
execute(dispatch: RuntimeDispatch, signal: AbortSignal, context?: RuntimeExecutionContext): Promise<RuntimeExecutionResult>
|
|
30
|
+
cancel?(sessionId: string, signal: AbortSignal): Promise<boolean>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface DshAgoraExtensionV1 {
|
|
34
|
+
readonly protocol: typeof DSH_AGORA_EXTENSION_PROTOCOL
|
|
35
|
+
readonly id: string
|
|
36
|
+
readonly kind: 'runtime' | 'transport' | 'context' | 'workflow' | 'artifact' | 'event-sink' | 'policy'
|
|
37
|
+
readonly capabilities: readonly string[]
|
|
38
|
+
readonly runtime?: DshAgoraRuntimeAdapterV1
|
|
39
|
+
readonly metadata?: Readonly<Record<string, unknown>>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface DshAgoraExtensionManifestV1 {
|
|
43
|
+
readonly protocol: typeof DSH_AGORA_EXTENSION_MANIFEST_PROTOCOL
|
|
44
|
+
readonly id: string
|
|
45
|
+
readonly version: string
|
|
46
|
+
readonly kind: DshAgoraExtensionV1['kind']
|
|
47
|
+
readonly integrity_sha256: string
|
|
48
|
+
readonly capabilities: readonly string[]
|
|
49
|
+
readonly permissions: readonly {
|
|
50
|
+
readonly capability: string
|
|
51
|
+
readonly resources: readonly string[]
|
|
52
|
+
}[]
|
|
53
|
+
readonly publisher: {
|
|
54
|
+
readonly id: string
|
|
55
|
+
readonly key_id: string
|
|
56
|
+
}
|
|
57
|
+
readonly signature?: {
|
|
58
|
+
readonly algorithm: 'Ed25519'
|
|
59
|
+
readonly value: string
|
|
60
|
+
} | null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface DshAgoraExtensionTrustPolicy {
|
|
64
|
+
readonly requireSignedThirdParty?: boolean
|
|
65
|
+
readonly trustedPublicKeys?: Readonly<Record<string, string>>
|
|
66
|
+
readonly builtInExtensionIds?: readonly string[]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface DshAgoraExtensionRegistryApi {
|
|
70
|
+
registerExtension(extension: DshAgoraExtensionV1, manifest?: DshAgoraExtensionManifestV1, packageBytes?: Uint8Array): () => void
|
|
71
|
+
listExtensions(): readonly DshAgoraExtensionV1[]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class DshAgoraExtensionRegistry implements DshAgoraExtensionRegistryApi {
|
|
75
|
+
private readonly extensions = new Map<string, DshAgoraExtensionV1>()
|
|
76
|
+
private readonly manifests = new Map<string, DshAgoraExtensionManifestV1>()
|
|
77
|
+
|
|
78
|
+
constructor(private readonly trustPolicy: DshAgoraExtensionTrustPolicy = {}) {}
|
|
79
|
+
|
|
80
|
+
registerExtension(extension: DshAgoraExtensionV1, manifest?: DshAgoraExtensionManifestV1, packageBytes?: Uint8Array): () => void {
|
|
81
|
+
validateExtension(extension)
|
|
82
|
+
const builtIn = new Set(this.trustPolicy.builtInExtensionIds ?? ['dsh-runtime']).has(extension.id)
|
|
83
|
+
if (manifest) {
|
|
84
|
+
validateExtensionManifest(manifest, extension)
|
|
85
|
+
if (this.trustPolicy.requireSignedThirdParty && !builtIn && packageBytes === undefined) {
|
|
86
|
+
throw new Error(`extension "${extension.id}" requires package bytes for integrity verification`)
|
|
87
|
+
}
|
|
88
|
+
verifyExtensionManifest(manifest, this.trustPolicy, packageBytes)
|
|
89
|
+
} else if (this.trustPolicy.requireSignedThirdParty && !builtIn) {
|
|
90
|
+
throw new Error(`extension "${extension.id}" requires a signed manifest`)
|
|
91
|
+
}
|
|
92
|
+
if (this.extensions.has(extension.id)) throw new Error(`dsh-agora extension "${extension.id}" is already registered`)
|
|
93
|
+
const frozen = Object.freeze({
|
|
94
|
+
...extension,
|
|
95
|
+
capabilities: Object.freeze(unique(extension.capabilities)),
|
|
96
|
+
})
|
|
97
|
+
this.extensions.set(frozen.id, frozen)
|
|
98
|
+
if (manifest) this.manifests.set(frozen.id, Object.freeze({ ...manifest }))
|
|
99
|
+
return () => {
|
|
100
|
+
if (this.extensions.get(frozen.id) === frozen) {
|
|
101
|
+
this.extensions.delete(frozen.id)
|
|
102
|
+
this.manifests.delete(frozen.id)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
manifestFor(id: string): DshAgoraExtensionManifestV1 | null {
|
|
108
|
+
return this.manifests.get(id) ?? null
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
listExtensions(): readonly DshAgoraExtensionV1[] {
|
|
112
|
+
return [...this.extensions.values()].sort((left, right) => left.id.localeCompare(right.id))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
runtimeForTarget(runtimeTargetRef: string): DshAgoraRuntimeAdapterV1 | null {
|
|
116
|
+
const runtimes = [...this.extensions.values()].filter(extension => (
|
|
117
|
+
extension.runtime && extension.capabilities.includes('runtime.execute')
|
|
118
|
+
))
|
|
119
|
+
const explicit = runtimes.find(extension => extension.runtime!.supportsTarget?.(runtimeTargetRef) === true)
|
|
120
|
+
if (explicit?.runtime) return explicit.runtime
|
|
121
|
+
if (runtimeTargetRef.startsWith('dsh:')) {
|
|
122
|
+
const builtIn = runtimes.find(extension => extension.id === 'dsh-runtime')
|
|
123
|
+
if (builtIn?.runtime) return builtIn.runtime
|
|
124
|
+
}
|
|
125
|
+
const legacy = runtimes.filter(extension => extension.runtime!.supportsTarget === undefined)
|
|
126
|
+
return legacy.length === 1 ? legacy[0]!.runtime! : null
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function verifyExtensionManifest(
|
|
131
|
+
manifest: DshAgoraExtensionManifestV1,
|
|
132
|
+
policy: DshAgoraExtensionTrustPolicy,
|
|
133
|
+
packageBytes?: Uint8Array,
|
|
134
|
+
): boolean {
|
|
135
|
+
if (packageBytes && createHash('sha256').update(packageBytes).digest('hex') !== manifest.integrity_sha256) {
|
|
136
|
+
throw new Error(`extension manifest integrity mismatch for ${manifest.id}`)
|
|
137
|
+
}
|
|
138
|
+
if (!manifest.signature) {
|
|
139
|
+
if (policy.requireSignedThirdParty) throw new Error(`extension manifest ${manifest.id} is unsigned`)
|
|
140
|
+
return false
|
|
141
|
+
}
|
|
142
|
+
const keyRef = `${manifest.publisher.id}:${manifest.publisher.key_id}`
|
|
143
|
+
const publicKey = policy.trustedPublicKeys?.[keyRef]
|
|
144
|
+
if (!publicKey) throw new Error(`extension publisher key ${keyRef} is not trusted`)
|
|
145
|
+
const valid = verifySignature(
|
|
146
|
+
null,
|
|
147
|
+
Buffer.from(canonicalManifest(manifest)),
|
|
148
|
+
createPublicKey(publicKey),
|
|
149
|
+
Buffer.from(manifest.signature.value, 'base64url'),
|
|
150
|
+
)
|
|
151
|
+
if (!valid) throw new Error(`extension manifest signature is invalid for ${manifest.id}`)
|
|
152
|
+
return true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function runExtensionConformance(
|
|
156
|
+
extension: DshAgoraExtensionV1,
|
|
157
|
+
manifest?: DshAgoraExtensionManifestV1,
|
|
158
|
+
): Promise<{ readonly ok: true; readonly checks: readonly string[] }> {
|
|
159
|
+
validateExtension(extension)
|
|
160
|
+
if (manifest) validateExtensionManifest(manifest, extension)
|
|
161
|
+
const checks = ['extension-shape', 'capability-uniqueness']
|
|
162
|
+
if (extension.runtime) {
|
|
163
|
+
const first = normalizeAgents(await extension.runtime.describeAgents())
|
|
164
|
+
const second = normalizeAgents(await extension.runtime.describeAgents())
|
|
165
|
+
if (JSON.stringify(first) !== JSON.stringify(second)) throw new Error('runtime describeAgents must be deterministic')
|
|
166
|
+
if (new Set(first.map(agent => agent.agent_ref)).size !== first.length) throw new Error('runtime describeAgents returned duplicate agent_ref values')
|
|
167
|
+
checks.push('runtime-protocol', 'runtime-deterministic-agents', 'runtime-unique-agent-refs')
|
|
168
|
+
}
|
|
169
|
+
if (manifest) checks.push('manifest-capability-permissions')
|
|
170
|
+
return { ok: true, checks }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function validateExtension(extension: DshAgoraExtensionV1): void {
|
|
174
|
+
if (extension.protocol !== DSH_AGORA_EXTENSION_PROTOCOL) {
|
|
175
|
+
throw new TypeError(`extension protocol must be ${DSH_AGORA_EXTENSION_PROTOCOL}`)
|
|
176
|
+
}
|
|
177
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,127}$/u.test(extension.id)) throw new TypeError('extension id is invalid')
|
|
178
|
+
if (!Array.isArray(extension.capabilities) || extension.capabilities.some(value => typeof value !== 'string' || value.trim() === '')) {
|
|
179
|
+
throw new TypeError('extension capabilities must be non-empty strings')
|
|
180
|
+
}
|
|
181
|
+
if (unique(extension.capabilities).length !== extension.capabilities.length) throw new TypeError('extension capabilities must be unique')
|
|
182
|
+
if (extension.runtime !== undefined && extension.runtime.protocol !== DSH_AGORA_RUNTIME_PROTOCOL) {
|
|
183
|
+
throw new TypeError(`runtime adapter protocol must be ${DSH_AGORA_RUNTIME_PROTOCOL}`)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function validateExtensionManifest(manifest: DshAgoraExtensionManifestV1, extension: DshAgoraExtensionV1): void {
|
|
188
|
+
if (manifest.protocol !== DSH_AGORA_EXTENSION_MANIFEST_PROTOCOL) throw new TypeError(`manifest protocol must be ${DSH_AGORA_EXTENSION_MANIFEST_PROTOCOL}`)
|
|
189
|
+
if (manifest.id !== extension.id || manifest.kind !== extension.kind) throw new TypeError('manifest identity does not match extension')
|
|
190
|
+
if (!/^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/u.test(manifest.version)) throw new TypeError('manifest version must be semantic')
|
|
191
|
+
if (!/^[a-f0-9]{64}$/u.test(manifest.integrity_sha256)) throw new TypeError('manifest integrity_sha256 is invalid')
|
|
192
|
+
const capabilities = unique(extension.capabilities)
|
|
193
|
+
if (JSON.stringify(unique(manifest.capabilities)) !== JSON.stringify(capabilities)) throw new TypeError('manifest capabilities do not match extension capabilities')
|
|
194
|
+
const granted = new Set(manifest.permissions.map(permission => permission.capability))
|
|
195
|
+
if (granted.size !== manifest.permissions.length) throw new TypeError('manifest permissions contain duplicate capabilities')
|
|
196
|
+
for (const capability of capabilities) if (!granted.has(capability)) throw new TypeError(`manifest permission missing for capability ${capability}`)
|
|
197
|
+
for (const permission of manifest.permissions) {
|
|
198
|
+
if (!capabilities.includes(permission.capability)) throw new TypeError(`manifest permission exceeds declared capability ${permission.capability}`)
|
|
199
|
+
if (permission.resources.length === 0 || permission.resources.some(resource => !resource.trim())) {
|
|
200
|
+
throw new TypeError(`manifest permission ${permission.capability} requires explicit resources`)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,127}$/u.test(manifest.publisher.id) || !manifest.publisher.key_id.trim()) throw new TypeError('manifest publisher is invalid')
|
|
204
|
+
if (manifest.signature && manifest.signature.algorithm !== 'Ed25519') throw new TypeError('only Ed25519 extension signatures are supported')
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function canonicalManifest(manifest: DshAgoraExtensionManifestV1): string {
|
|
208
|
+
const { signature: _signature, ...unsigned } = manifest
|
|
209
|
+
return stableJson(unsigned)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function stableJson(value: unknown): string {
|
|
213
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(',')}]`
|
|
214
|
+
if (value && typeof value === 'object') return `{${Object.entries(value as Record<string, unknown>).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(',')}}`
|
|
215
|
+
return JSON.stringify(value) ?? 'null'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function normalizeAgents(agents: readonly RuntimeNodeAgent[]): RuntimeNodeAgent[] {
|
|
219
|
+
return agents.map(agent => ({ ...agent, roles: unique(agent.roles), capabilities: unique(agent.capabilities) }))
|
|
220
|
+
.sort((left, right) => left.agent_ref.localeCompare(right.agent_ref))
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function unique(values: readonly string[]): string[] {
|
|
224
|
+
return [...new Set(values.map(value => value.trim()).filter(Boolean))].sort()
|
|
225
|
+
}
|