dsh-hams 0.1.2 → 0.1.4

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.
Files changed (49) hide show
  1. package/README.md +4 -0
  2. package/dist/index.js +11335 -0
  3. package/package.json +9 -5
  4. package/src/domain/check.ts +0 -193
  5. package/src/domain/errors.ts +0 -59
  6. package/src/domain/evidence.ts +0 -134
  7. package/src/domain/ids.ts +0 -31
  8. package/src/host/chat-runtime.ts +0 -489
  9. package/src/host/dsh-adapter.ts +0 -867
  10. package/src/host/session-adapter.ts +0 -276
  11. package/src/http/handler.ts +0 -1132
  12. package/src/index.js +0 -53
  13. package/src/service/audit.ts +0 -92
  14. package/src/service/chat.ts +0 -1100
  15. package/src/service/contracts.ts +0 -193
  16. package/src/service/defaults.ts +0 -182
  17. package/src/service/deliverables.ts +0 -240
  18. package/src/service/hams.ts +0 -3103
  19. package/src/service/knowledge.ts +0 -940
  20. package/src/service/member-config.ts +0 -39
  21. package/src/service/members.ts +0 -506
  22. package/src/service/memory.ts +0 -375
  23. package/src/service/permissions.ts +0 -118
  24. package/src/service/project-lifecycle.ts +0 -233
  25. package/src/service/projects.ts +0 -840
  26. package/src/service/receipts.ts +0 -63
  27. package/src/service/templates.ts +0 -140
  28. package/src/service/tick.ts +0 -751
  29. package/src/service/tools.ts +0 -711
  30. package/src/service/types.ts +0 -219
  31. package/src/service/work-item-create.ts +0 -159
  32. package/src/service/workspace-profile.ts +0 -443
  33. package/src/store/backup.ts +0 -239
  34. package/src/store/bootstrap.ts +0 -225
  35. package/src/store/db.ts +0 -179
  36. package/src/store/lock.ts +0 -194
  37. package/src/store/migrations.ts +0 -211
  38. package/src/store/schema/001_p1.sql +0 -846
  39. package/src/store/schema/001_p1.sql.ts +0 -865
  40. package/src/store/schema/002_knowledge.sql +0 -54
  41. package/src/store/schema/002_knowledge.sql.ts +0 -68
  42. package/src/store/schema/003_projects.sql.ts +0 -3
  43. package/src/store/schema/004_team.sql.ts +0 -50
  44. package/src/store/schema/005_target_check.sql.ts +0 -22
  45. package/src/store/schema/006_target_flow.sql.ts +0 -19
  46. package/src/store/schema/007_project_delete.sql.ts +0 -17
  47. package/src/store/schema/008_memory.sql.ts +0 -47
  48. package/src/store/schema/generate-projects.mjs +0 -16
  49. package/src/store/seed.ts +0 -114
@@ -1,1132 +0,0 @@
1
- /**
2
- * HTTP handler:只做「协议 → 命令上下文 → 领域服务 → 回执」的翻译。
3
- *
4
- * 刻意不依赖 `ctx.webServer`:宿主注册(`webServer.register({kind:'exact'})`)与
5
- * Origin/自定义头能不能读,要等 C2 实测;本文件先把可以确定的部分做成纯函数,
6
- * 宿主接线时只包一层薄适配器。
7
- *
8
- * 进站顺序(与 backend §9 一致):
9
- * 方法/路径 → 验来源与主体(注入的 authenticate)→ 拒绝 bot 走 UI 通道 →
10
- * 读负载(大小上限)→ 解析信封 → 可选修订号校验 → 领域命令 → 回执。
11
- *
12
- * 传输层错误用 HTTP 状态码:405 / 404 / 413 / 400 / 401 / 403;
13
- * 业务成败一律 200 + 回执里的 `ok`,界面不需要解析状态码猜业务结果。
14
- *
15
- * 401 与 403 都表示「没进业务层」:401 = 浏览器会话缺失/过期(界面该重开带
16
- * `?token=` 的地址),403 = 来源不可信或主体明确不允许(重开也没用)。
17
- */
18
- import type { HamsService } from '../service/hams.ts'
19
- import type { CommandContext, CommandReceipt } from '../service/types.ts'
20
- import {
21
- asNumber,
22
- asObject,
23
- asString,
24
- optionalString,
25
- rejectedReceipt,
26
- requireEnvelope,
27
- toCommandContext,
28
- type CommandEnvelope,
29
- type ErrorResult,
30
- type HamsResponseBody,
31
- type QueryResult,
32
- } from '../service/contracts.ts'
33
- import { HamsError } from '../domain/errors.ts'
34
- import type { MetricInput } from '../service/projects.ts'
35
- import { listDeliverables, readDeliverable } from '../service/deliverables.ts'
36
-
37
- export interface HttpRequest {
38
- method: string
39
- path: string
40
- /** 键一律按小写读取;宿主适配器负责把原始头转成这个形状。 */
41
- headers: Record<string, string | undefined>
42
- body: string
43
- }
44
-
45
- export interface HttpResponse {
46
- status: number
47
- headers: Record<string, string>
48
- body: string
49
- }
50
-
51
- export interface AuthSuccess {
52
- ok: true
53
- principalId: string
54
- authSubject?: string
55
- hostPreset?: string
56
- onBehalfOf?: string
57
- }
58
-
59
- export interface AuthFailure {
60
- ok: false
61
- /**
62
- * `UNAUTHORIZED` → 401(浏览器会话缺失/过期,界面应重开带 ?token= 的地址);
63
- * `FORBIDDEN` → 403(来源不可信,或主体明确不允许)。两者都不进业务层。
64
- */
65
- reason: 'UNAUTHORIZED' | 'FORBIDDEN'
66
- message: string
67
- nextStep?: string
68
- }
69
-
70
- export type AuthResult = AuthSuccess | AuthFailure
71
-
72
- export interface HamsHttpDeps {
73
- service: HamsService
74
- /** 对话(单聊/群聊)与宿主模型/preset 目录;未提供时这些路由返回 HOST_CAPABILITY_MISSING。 */
75
- chat?: import('../service/chat.ts').ChatService
76
- /** 宿主适配器提供:验 Origin / 本机令牌 / SSO 头,并解析出主体。 */
77
- authenticate(request: HttpRequest): AuthResult
78
- maxBodyBytes?: number
79
- correlationId?: () => string
80
- /** 第二实例只读模式:写命令一律拒绝并说明原因。 */
81
- readOnly?: boolean
82
- }
83
-
84
- export const API_PREFIX = '/hams/api/v1/'
85
- const DEFAULT_MAX_BODY = 256 * 1024
86
-
87
- export interface HamsHttpExtras {
88
- /** 对话与宿主目录:需要读宿主会话时才有;纯只读实例可以不给。 */
89
- chat?: import('../service/chat.ts').ChatService
90
- /** 项目交付物扫描需要 HAMS 数据目录(bot 工作区在 <dataDir>/bots 下)。 */
91
- deliverables?: { dataDir: string; db: import('node:sqlite').DatabaseSync }
92
- }
93
-
94
- type RouteHandler = (
95
- service: HamsService,
96
- ctx: CommandContext,
97
- envelope: CommandEnvelope,
98
- extras: HamsHttpExtras,
99
- ) => CommandReceipt | QueryResult | Promise<CommandReceipt | QueryResult>
100
-
101
- interface Route {
102
- write: boolean
103
- handle: RouteHandler
104
- /** 对工作项类命令做 expectedRevision 前置校验。 */
105
- revisionCheck?: boolean
106
- }
107
-
108
- function payloadOf(envelope: CommandEnvelope): Record<string, unknown> {
109
- return envelope.payload === undefined ? {} : asObject(envelope.payload, 'payload')
110
- }
111
-
112
- function idOf(envelope: CommandEnvelope, payload: Record<string, unknown>, field = 'objectId'): string {
113
- const fromEnvelope = envelope.objectId
114
- if (fromEnvelope) return fromEnvelope
115
- return asString(payload[field], field)
116
- }
117
-
118
- /** 项目指标列表:建项目与存草稿共用;草稿允许只填一半(后续发布时再严格校验)。 */
119
- function metricListOf(payload: Record<string, unknown>, lenient = false): unknown[] | undefined {
120
- const list = payload.metrics
121
- if (list === undefined) return undefined
122
- if (!Array.isArray(list)) throw new HamsError('STATE_INVALID', 'payload.metrics 必须是数组')
123
- if (!lenient) return list
124
- return list.filter((item) => item && typeof item === 'object' && !Array.isArray(item))
125
- }
126
-
127
- function ok<T>(data: T, correlationId: string): QueryResult<T> {
128
- return { ok: true, data, correlationId }
129
- }
130
-
131
- function stringArray(value: unknown, field: string): string[] | undefined {
132
- if (value === undefined) return undefined
133
- if (!Array.isArray(value)) throw new HamsError('STATE_INVALID', `${field} 必须是字符串数组`)
134
- return value.map((item, index) => asString(item, `${field}[${index}]`))
135
- }
136
-
137
- const ROUTES = new Map<string, Route>([
138
- [
139
- 'session/whoami',
140
- {
141
- write: false,
142
- handle: (service, ctx) => ok(service.whoami(ctx), ctx.correlationId),
143
- },
144
- ],
145
- [
146
- 'ops/health',
147
- {
148
- write: false,
149
- handle: (service, ctx) => ok(service.health(ctx), ctx.correlationId),
150
- },
151
- ],
152
- [
153
- 'overview/get',
154
- {
155
- write: false,
156
- handle: (service, ctx) => ok(service.overview(ctx), ctx.correlationId),
157
- },
158
- ],
159
- // ── 对话:单聊(智能体)/ 群聊(项目)──────────────────────────────────
160
- // 消息正文留在宿主会话里,这里只读;线程指针存在 hams_meta,不新建表。
161
- [
162
- 'chat/open',
163
- {
164
- write: true,
165
- handle: async (service, ctx, envelope, extras) => {
166
- if (!extras.chat) throw new HamsError('HOST_CAPABILITY_MISSING', '当前实例没有接宿主会话服务', { nextStep: '重启 DSH 让插件接上 agents 服务' })
167
- const payload = payloadOf(envelope)
168
- const kind = asString(payload.kind, 'payload.kind')
169
- if (kind !== 'member' && kind !== 'project') throw new HamsError('STATE_INVALID', 'kind 只能是 member 或 project')
170
- return ok(await extras.chat.open(ctx, { kind, targetId: asString(payload.targetId, 'payload.targetId') }), ctx.correlationId)
171
- },
172
- },
173
- ],
174
- [
175
- 'chat/send',
176
- {
177
- write: true,
178
- handle: async (service, ctx, envelope, extras) => {
179
- if (!extras.chat) throw new HamsError('HOST_CAPABILITY_MISSING', '当前实例没有接宿主会话服务', { nextStep: '重启 DSH 让插件接上 agents 服务' })
180
- const payload = payloadOf(envelope)
181
- const kind = asString(payload.kind, 'payload.kind')
182
- if (kind !== 'member' && kind !== 'project') throw new HamsError('STATE_INVALID', 'kind 只能是 member 或 project')
183
- return ok(await extras.chat.send(ctx, { kind, targetId: asString(payload.targetId, 'payload.targetId'), text: asString(payload.text, 'payload.text') }), ctx.correlationId)
184
- },
185
- },
186
- ],
187
- [
188
- 'chat/messages',
189
- {
190
- write: false,
191
- handle: async (service, ctx, envelope, extras) => {
192
- if (!extras.chat) return ok({ thread: null, messages: [], running: false, available: false, reason: '当前实例没有接宿主会话服务' }, ctx.correlationId)
193
- const payload = payloadOf(envelope)
194
- const kind = asString(payload.kind, 'payload.kind')
195
- if (kind !== 'member' && kind !== 'project') throw new HamsError('STATE_INVALID', 'kind 只能是 member 或 project')
196
- return ok(await extras.chat.read(ctx, { kind, targetId: asString(payload.targetId, 'payload.targetId') }), ctx.correlationId)
197
- },
198
- },
199
- ],
200
- [
201
- 'chat/stop',
202
- {
203
- write: true,
204
- handle: async (service, ctx, envelope, extras) => {
205
- if (!extras.chat) throw new HamsError('HOST_CAPABILITY_MISSING', '当前实例没有接宿主会话服务', { nextStep: '重启 DSH 让插件接上 agents 服务' })
206
- const payload = payloadOf(envelope)
207
- const kind = asString(payload.kind, 'payload.kind')
208
- if (kind !== 'member' && kind !== 'project') throw new HamsError('STATE_INVALID', 'kind 只能是 member 或 project')
209
- return ok(await extras.chat.stop(ctx, { kind, targetId: asString(payload.targetId, 'payload.targetId') }), ctx.correlationId)
210
- },
211
- },
212
- ],
213
- [
214
- 'project/deliverables',
215
- {
216
- write: false,
217
- handle: (service, ctx, envelope, extras) => {
218
- if (!extras.deliverables) return ok({ items: [], truncated: false, workItems: 0 }, ctx.correlationId)
219
- const payload = payloadOf(envelope)
220
- return ok(
221
- listDeliverables({
222
- db: extras.deliverables.db,
223
- dataDir: extras.deliverables.dataDir,
224
- projectId: asString(payload.projectId, 'payload.projectId'),
225
- }),
226
- ctx.correlationId,
227
- )
228
- },
229
- },
230
- ],
231
- [
232
- 'project/deliverable',
233
- {
234
- write: false,
235
- handle: (service, ctx, envelope, extras) => {
236
- if (!extras.deliverables) throw new HamsError('HOST_CAPABILITY_MISSING', '当前实例没有交付物目录,无法预览文件')
237
- const payload = payloadOf(envelope)
238
- return ok(
239
- readDeliverable({
240
- db: extras.deliverables.db,
241
- dataDir: extras.deliverables.dataDir,
242
- projectId: asString(payload.projectId, 'payload.projectId'),
243
- relPath: asString(payload.path, 'payload.path'),
244
- }),
245
- ctx.correlationId,
246
- )
247
- },
248
- },
249
- ],
250
- [
251
- 'chat/team',
252
- {
253
- write: false,
254
- handle: async (service, ctx, envelope, extras) => {
255
- if (!extras.chat) return ok({ coordinator: null, members: [] }, ctx.correlationId)
256
- return ok(await extras.chat.team(ctx, { projectId: asString(payloadOf(envelope).projectId, 'payload.projectId') }), ctx.correlationId)
257
- },
258
- },
259
- ],
260
- [
261
- 'chat/team/add',
262
- {
263
- write: true,
264
- handle: async (service, ctx, envelope, extras) => {
265
- if (!extras.chat) throw new HamsError('HOST_CAPABILITY_MISSING', '当前实例没有接宿主会话服务')
266
- const payload = payloadOf(envelope)
267
- return ok(extras.chat.addParticipant(ctx, { projectId: asString(payload.projectId, 'payload.projectId'), memberId: asString(payload.memberId, 'payload.memberId') }), ctx.correlationId)
268
- },
269
- },
270
- ],
271
- [
272
- 'chat/team/remove',
273
- {
274
- write: true,
275
- handle: async (service, ctx, envelope, extras) => {
276
- if (!extras.chat) throw new HamsError('HOST_CAPABILITY_MISSING', '当前实例没有接宿主会话服务')
277
- const payload = payloadOf(envelope)
278
- return ok(extras.chat.removeParticipant(ctx, { projectId: asString(payload.projectId, 'payload.projectId'), memberId: asString(payload.memberId, 'payload.memberId') }), ctx.correlationId)
279
- },
280
- },
281
- ],
282
- [
283
- 'chat/team/spawn',
284
- {
285
- write: true,
286
- handle: async (service, ctx, envelope, extras) => {
287
- if (!extras.chat) throw new HamsError('HOST_CAPABILITY_MISSING', '当前实例没有接宿主会话服务')
288
- const payload = payloadOf(envelope)
289
- return ok(extras.chat.spawnTempBot(ctx, { projectId: asString(payload.projectId, 'payload.projectId'), name: asString(payload.name, 'payload.name'), brief: optionalString(payload.brief, 'payload.brief') }), ctx.correlationId)
290
- },
291
- },
292
- ],
293
- [
294
- 'chat/group/messages',
295
- {
296
- write: false,
297
- handle: async (service, ctx, envelope, extras) => {
298
- if (!extras.chat) return ok({ messages: [], members: [], coordinator: null, running: false, available: false, reason: '当前实例没有接宿主会话服务' }, ctx.correlationId)
299
- return ok(await extras.chat.groupRead(ctx, { projectId: asString(payloadOf(envelope).projectId, 'payload.projectId') }), ctx.correlationId)
300
- },
301
- },
302
- ],
303
- [
304
- 'chat/group/send',
305
- {
306
- write: true,
307
- handle: async (service, ctx, envelope, extras) => {
308
- if (!extras.chat) throw new HamsError('HOST_CAPABILITY_MISSING', '当前实例没有接宿主会话服务')
309
- const payload = payloadOf(envelope)
310
- return ok(await extras.chat.groupSend(ctx, { projectId: asString(payload.projectId, 'payload.projectId'), text: asString(payload.text, 'payload.text') }), ctx.correlationId)
311
- },
312
- },
313
- ],
314
- [
315
- 'host/catalog',
316
- {
317
- write: false,
318
- handle: async (service, ctx, envelope, extras) => ok({
319
- available: Boolean(extras.chat && extras.chat.available),
320
- reason: extras.chat ? extras.chat.reason : '当前实例没有接宿主会话服务',
321
- presets: extras.chat ? await extras.chat.presets() : [],
322
- models: extras.chat ? await extras.chat.models() : [],
323
- }, ctx.correlationId),
324
- },
325
- ],
326
- [
327
- 'changes/since',
328
- {
329
- write: false,
330
- handle: (service, ctx, envelope) => {
331
- const payload = payloadOf(envelope)
332
- return ok(
333
- service.changesSince(ctx, {
334
- cursor: payload.cursor === undefined ? undefined : asNumber(payload.cursor, 'payload.cursor'),
335
- limit: payload.limit === undefined ? undefined : asNumber(payload.limit, 'payload.limit'),
336
- }),
337
- ctx.correlationId,
338
- )
339
- },
340
- },
341
- ],
342
- [
343
- 'proposal/list',
344
- {
345
- write: false,
346
- handle: (service, ctx, envelope) => {
347
- const payload = payloadOf(envelope)
348
- return ok(
349
- service.listProposals(ctx, {
350
- deciderId: optionalString(payload.deciderId, 'payload.deciderId'),
351
- status: optionalString(payload.status, 'payload.status'),
352
- limit: payload.limit === undefined ? undefined : asNumber(payload.limit, 'payload.limit'),
353
- }),
354
- ctx.correlationId,
355
- )
356
- },
357
- },
358
- ],
359
- [
360
- 'workItem/create',
361
- {
362
- write: true,
363
- handle: (service, ctx, envelope) => {
364
- const payload = payloadOf(envelope)
365
- return service.createWorkItem(ctx, {
366
- domain: asString(payload.domain, 'payload.domain'),
367
- category: asString(payload.category, 'payload.category'),
368
- title: asString(payload.title, 'payload.title'),
369
- ownerHumanId: optionalString(payload.ownerHumanId, 'payload.ownerHumanId'),
370
- projectId: optionalString(payload.projectId, 'payload.projectId'),
371
- templateVersionId: optionalString(payload.templateVersionId, 'payload.templateVersionId'),
372
- assetId: optionalString(payload.assetId, 'payload.assetId'),
373
- dedupeKey: optionalString(payload.dedupeKey, 'payload.dedupeKey'),
374
- priority: payload.priority === undefined ? undefined : asNumber(payload.priority, 'payload.priority'),
375
- dueAt: payload.dueAt === undefined ? undefined : asNumber(payload.dueAt, 'payload.dueAt'),
376
- })
377
- },
378
- },
379
- ],
380
- [
381
- 'workItem/list',
382
- {
383
- write: false,
384
- handle: (service, ctx, envelope) => {
385
- const payload = payloadOf(envelope)
386
- return ok(
387
- service.listWorkItems(ctx, {
388
- status: optionalString(payload.status, 'payload.status'),
389
- assigneeId: optionalString(payload.assigneeId, 'payload.assigneeId'),
390
- ownerId: optionalString(payload.ownerId, 'payload.ownerId'),
391
- projectId: optionalString(payload.projectId, 'payload.projectId'),
392
- domain: optionalString(payload.domain, 'payload.domain'),
393
- limit: payload.limit === undefined ? undefined : asNumber(payload.limit, 'payload.limit'),
394
- }),
395
- ctx.correlationId,
396
- )
397
- },
398
- },
399
- ],
400
- [
401
- 'workItem/get',
402
- {
403
- write: false,
404
- handle: (service, ctx, envelope) => ok(service.getWorkItem(ctx, idOf(envelope, payloadOf(envelope))), ctx.correlationId),
405
- },
406
- ],
407
- [
408
- 'check/list',
409
- {
410
- write: false,
411
- handle: (service, ctx, envelope) => {
412
- const payload = payloadOf(envelope)
413
- return ok(service.listChecks(ctx, asString(payload.workItemId, 'payload.workItemId')), ctx.correlationId)
414
- },
415
- },
416
- ],
417
- [
418
- 'template/list',
419
- {
420
- write: false,
421
- handle: (service, ctx, envelope) => {
422
- const payload = payloadOf(envelope)
423
- return ok(
424
- service.listTemplates(ctx, { domain: optionalString(payload.domain, 'payload.domain') }),
425
- ctx.correlationId,
426
- )
427
- },
428
- },
429
- ],
430
- [
431
- 'approval/list',
432
- {
433
- write: false,
434
- handle: (service, ctx, envelope) => {
435
- const payload = payloadOf(envelope)
436
- return ok(
437
- service.listApprovals(ctx, {
438
- approverId: optionalString(payload.approverId, 'payload.approverId'),
439
- status: optionalString(payload.status, 'payload.status'),
440
- }),
441
- ctx.correlationId,
442
- )
443
- },
444
- },
445
- ],
446
- [
447
- 'workItem/assign',
448
- {
449
- write: true,
450
- revisionCheck: true,
451
- handle: (service, ctx, envelope) => {
452
- const payload = payloadOf(envelope)
453
- return service.assignWorkItem(ctx, {
454
- workItemId: idOf(envelope, payload),
455
- memberId: asString(payload.memberId ?? payload.assigneeId, 'payload.memberId'),
456
- })
457
- },
458
- },
459
- ],
460
- [
461
- 'workItem/accept',
462
- {
463
- write: true,
464
- revisionCheck: true,
465
- handle: (service, ctx, envelope) => {
466
- const payload = payloadOf(envelope)
467
- return service.accept(ctx, {
468
- workItemId: idOf(envelope, payload),
469
- note: optionalString(payload.note, 'payload.note'),
470
- })
471
- },
472
- },
473
- ],
474
- ...[
475
- ['workItem/pause', 'pause'],
476
- ['workItem/resume', 'resume'],
477
- ['workItem/cancel', 'cancel'],
478
- ['workItem/reopen', 'reopen'],
479
- ['workItem/retry', 'retry'],
480
- ].map(([path, method]): [string, Route] => [
481
- path,
482
- {
483
- write: true,
484
- revisionCheck: true,
485
- handle: (service, ctx, envelope) => {
486
- const payload = payloadOf(envelope)
487
- const fn = service[method as 'pause' | 'resume' | 'cancel' | 'reopen' | 'retry'].bind(service)
488
- return fn(ctx, {
489
- workItemId: idOf(envelope, payload),
490
- reason: optionalString(payload.reason, 'payload.reason'),
491
- })
492
- },
493
- },
494
- ]),
495
- ['workItem/rotateThread',{write:true,handle:(service,ctx,envelope)=>{
496
- const p=payloadOf(envelope)
497
- return service.rotateThread(ctx,{workItemId:idOf(envelope,p),sessionId:asString(p.sessionId,'sessionId'),reason:optionalString(p.reason,'reason')})
498
- }}],
499
- [
500
- 'workItem/addEvidence',
501
- {
502
- write: true,
503
- revisionCheck: true,
504
- handle: (service, ctx, envelope) => {
505
- const payload = payloadOf(envelope)
506
- return service.appendEvidence(ctx, {
507
- workItemId: idOf(envelope, payload),
508
- kind: asString(payload.kind, 'payload.kind'),
509
- ref: asString(payload.ref, 'payload.ref'),
510
- observedAt: asNumber(payload.observedAt, 'payload.observedAt'),
511
- contentHash: optionalString(payload.contentHash, 'payload.contentHash'),
512
- source: optionalString(payload.source, 'payload.source'),
513
- runId: optionalString(payload.runId, 'payload.runId'),
514
- onBehalfOf: optionalString(payload.onBehalfOf, 'payload.onBehalfOf'),
515
- supersedesId: optionalString(payload.supersedesId, 'payload.supersedesId'),
516
- })
517
- },
518
- },
519
- ],
520
- [
521
- 'workItem/reportBlocker',
522
- {
523
- write: true,
524
- revisionCheck: true,
525
- handle: (service, ctx, envelope) => {
526
- const payload = payloadOf(envelope)
527
- return service.reportBlocker(ctx, {
528
- workItemId: idOf(envelope, payload),
529
- category: asString(payload.category, 'payload.category'),
530
- note: asString(payload.note, 'payload.note', { allowEmpty: true }),
531
- })
532
- },
533
- },
534
- ],
535
- [
536
- 'workItem/unblock',
537
- {
538
- write: true,
539
- revisionCheck: true,
540
- handle: (service, ctx, envelope) => {
541
- const payload = payloadOf(envelope)
542
- const to = optionalString(payload.to, 'payload.to')
543
- if (to !== undefined && to !== 'ready' && to !== 'active') {
544
- throw new HamsError('STATE_INVALID', 'payload.to 只能是 ready 或 active')
545
- }
546
- return service.unblock(ctx, { workItemId: idOf(envelope, payload), to })
547
- },
548
- },
549
- ],
550
- [
551
- 'workItem/requestClosure',
552
- {
553
- write: true,
554
- revisionCheck: true,
555
- handle: (service, ctx, envelope) => {
556
- const payload = payloadOf(envelope)
557
- return service.requestClosure(ctx, {
558
- workItemId: idOf(envelope, payload),
559
- reason: asString(payload.reason, 'payload.reason'),
560
- })
561
- },
562
- },
563
- ],
564
- [
565
- 'approval/decide',
566
- {
567
- write: true,
568
- handle: (service, ctx, envelope) => {
569
- const payload = payloadOf(envelope)
570
- const decision = asString(payload.decision, 'payload.decision')
571
- if (decision !== 'approve' && decision !== 'reject' && decision !== 'return') {
572
- throw new HamsError('STATE_INVALID', 'payload.decision 只能是 approve / reject / return')
573
- }
574
- return service.decideApproval(ctx, {
575
- approvalId: idOf(envelope, payload, 'approvalId'),
576
- decision,
577
- note: optionalString(payload.note, 'payload.note'),
578
- })
579
- },
580
- },
581
- ],
582
- [
583
- 'proposal/decide',
584
- {
585
- write: true,
586
- handle: (service, ctx, envelope) => {
587
- const payload = payloadOf(envelope)
588
- const decision = asString(payload.decision, 'payload.decision')
589
- if (decision !== 'accept' && decision !== 'reject') {
590
- throw new HamsError('STATE_INVALID', 'payload.decision 只能是 accept / reject')
591
- }
592
- const workItemRaw = payload.workItem === undefined ? undefined : asObject(payload.workItem, 'payload.workItem')
593
- return service.decideProposal(ctx, {
594
- proposalId: idOf(envelope, payload, 'proposalId'),
595
- decision,
596
- note: asString(payload.note, 'payload.note', { allowEmpty: true }),
597
- workItem: workItemRaw
598
- ? {
599
- domain: asString(workItemRaw.domain, 'payload.workItem.domain'),
600
- category: asString(workItemRaw.category, 'payload.workItem.category'),
601
- title: asString(workItemRaw.title, 'payload.workItem.title'),
602
- ownerHumanId: asString(workItemRaw.ownerHumanId, 'payload.workItem.ownerHumanId'),
603
- projectId: optionalString(workItemRaw.projectId, 'payload.workItem.projectId'),
604
- templateVersionId: optionalString(workItemRaw.templateVersionId, 'payload.workItem.templateVersionId'),
605
- assetId: optionalString(workItemRaw.assetId, 'payload.workItem.assetId'),
606
- dueAt: workItemRaw.dueAt === undefined ? undefined : asNumber(workItemRaw.dueAt, 'payload.workItem.dueAt'),
607
- }
608
- : undefined,
609
- })
610
- },
611
- },
612
- ],
613
- [
614
- 'check/evaluate',
615
- {
616
- write: true,
617
- handle: (service, ctx, envelope) => {
618
- const payload = payloadOf(envelope)
619
- return service.evaluateCheck(ctx, {
620
- checkId: idOf(envelope, payload, 'checkId'),
621
- })
622
- },
623
- },
624
- ],
625
- // ───────────────────────── 项目(M6) ─────────────────────────
626
- [
627
- 'project/list',
628
- {
629
- write: false,
630
- handle: (service, ctx, envelope) => {
631
- const payload = payloadOf(envelope)
632
- return ok(
633
- service.listProjects(ctx, {
634
- status: optionalString(payload.status, 'payload.status'),
635
- type: optionalString(payload.type, 'payload.type'),
636
- limit: payload.limit === undefined ? undefined : asNumber(payload.limit, 'payload.limit'),
637
- }),
638
- ctx.correlationId,
639
- )
640
- },
641
- },
642
- ],
643
- [
644
- 'project/get',
645
- {
646
- write: false,
647
- handle: (service, ctx, envelope) =>
648
- ok(
649
- service.getProjectDetail(ctx, idOf(envelope, payloadOf(envelope), 'projectId')),
650
- ctx.correlationId,
651
- ),
652
- },
653
- ],
654
- [
655
- 'project/create',
656
- {
657
- write: true,
658
- handle: (service, ctx, envelope) => {
659
- const payload = payloadOf(envelope)
660
- const metricList = payload.metrics === undefined ? undefined : payload.metrics
661
- if (metricList !== undefined && !Array.isArray(metricList)) {
662
- throw new HamsError('STATE_INVALID', 'payload.metrics 必须是数组')
663
- }
664
- const metrics = (metricList as unknown[] | undefined)?.map((item, index) => {
665
- const metric = asObject(item, `payload.metrics[${index}]`)
666
- return {
667
- name: asString(metric.name, `payload.metrics[${index}].name`),
668
- direction: asString(metric.direction, `payload.metrics[${index}].direction`),
669
- targetValue:
670
- metric.targetValue === undefined || metric.targetValue === null
671
- ? null
672
- : asNumber(metric.targetValue, `payload.metrics[${index}].targetValue`),
673
- targetMax:
674
- metric.targetMax === undefined || metric.targetMax === null
675
- ? null
676
- : asNumber(metric.targetMax, `payload.metrics[${index}].targetMax`),
677
- unit: optionalString(metric.unit, `payload.metrics[${index}].unit`),
678
- windowKind: asString(metric.windowKind, `payload.metrics[${index}].windowKind`),
679
- definitionText: optionalString(metric.definitionText, `payload.metrics[${index}].definitionText`),
680
- sourceKind: optionalString(metric.sourceKind, `payload.metrics[${index}].sourceKind`),
681
- calculatorKey: optionalString(metric.calculatorKey, 'calculatorKey'),
682
- calculatorParams: metric.calculatorParams === undefined ? undefined : asObject(metric.calculatorParams,'calculatorParams'),
683
- sourceRef: optionalString(metric.sourceRef,'sourceRef'),
684
- }
685
- })
686
- return service.createProject(ctx, {
687
- name: asString(payload.name, 'payload.name'),
688
- type: asString(payload.type, 'payload.type'),
689
- goalText: asString(payload.goalText, 'payload.goalText', { allowEmpty: true }),
690
- ownerHumanId: optionalString(payload.ownerHumanId, 'payload.ownerHumanId'),
691
- coordinatorId:
692
- payload.coordinatorId === null ? null : optionalString(payload.coordinatorId, 'payload.coordinatorId'),
693
- metrics,
694
- publish: payload.publish === undefined ? undefined : payload.publish === true,
695
- })
696
- },
697
- },
698
- ],
699
- [
700
- 'project/saveDraft',
701
- {
702
- write: true,
703
- handle: (service, ctx, envelope) => {
704
- const payload = payloadOf(envelope)
705
- return service.saveProjectDraft(ctx, {
706
- projectId: optionalString(payload.projectId, 'payload.projectId'),
707
- name: asString(payload.name, 'payload.name'),
708
- type: asString(payload.type, 'payload.type'),
709
- goalText: asString(payload.goalText ?? '', 'payload.goalText', { allowEmpty: true }),
710
- coordinatorId:
711
- payload.coordinatorId === null ? null : optionalString(payload.coordinatorId, 'payload.coordinatorId'),
712
- metrics: metricListOf(payload, true) as MetricInput[] | undefined,
713
- step: payload.step === undefined ? undefined : asNumber(payload.step, 'payload.step'),
714
- })
715
- },
716
- },
717
- ],
718
- [
719
- 'project/delete',
720
- {
721
- write: true,
722
- handle: (service, ctx, envelope) => {
723
- const payload = payloadOf(envelope)
724
- return service.deleteProject(ctx, {
725
- projectId: idOf(envelope, payload, 'projectId'),
726
- reason: optionalString(payload.reason, 'payload.reason'),
727
- })
728
- },
729
- },
730
- ],
731
- [
732
- 'project/status',
733
- {
734
- write: true,
735
- handle: (service, ctx, envelope) => {
736
- const payload = payloadOf(envelope)
737
- return service.updateProjectStatus(ctx, {
738
- projectId: idOf(envelope, payload, 'projectId'),
739
- status: asString(payload.status, 'payload.status'),
740
- reason: optionalString(payload.reason, 'payload.reason'),
741
- })
742
- },
743
- },
744
- ],
745
- [
746
- 'project/memory/list',
747
- {
748
- write: false,
749
- handle: (service, ctx, envelope) => {
750
- const payload = payloadOf(envelope)
751
- return ok(
752
- service.listProjectMemories(ctx, {
753
- projectId: asString(payload.projectId, 'payload.projectId'),
754
- status: optionalString(payload.status, 'payload.status'),
755
- kind: optionalString(payload.kind, 'payload.kind'),
756
- limit: payload.limit === undefined ? undefined : asNumber(payload.limit, 'payload.limit'),
757
- }),
758
- ctx.correlationId,
759
- )
760
- },
761
- },
762
- ],
763
- [
764
- 'project/memory/create',
765
- {
766
- write: true,
767
- handle: (service, ctx, envelope) => {
768
- const payload = payloadOf(envelope)
769
- return service.createProjectMemory(ctx, {
770
- projectId: idOf(envelope, payload, 'projectId'),
771
- kind: asString(payload.kind, 'payload.kind'),
772
- title: asString(payload.title, 'payload.title'),
773
- body: asString(payload.body, 'payload.body'),
774
- memberId: optionalString(payload.memberId, 'payload.memberId'),
775
- })
776
- },
777
- },
778
- ],
779
- [
780
- 'project/memory/update',
781
- {
782
- write: true,
783
- handle: (service, ctx, envelope) => {
784
- const payload = payloadOf(envelope)
785
- return service.updateProjectMemory(ctx, {
786
- memoryId: idOf(envelope, payload, 'memoryId'),
787
- title: optionalString(payload.title, 'payload.title'),
788
- body: optionalString(payload.body, 'payload.body'),
789
- status: optionalString(payload.status, 'payload.status'),
790
- })
791
- },
792
- },
793
- ],
794
- [
795
- 'project/recordMetric',
796
- {
797
- write: true,
798
- handle: (service, ctx, envelope) => {
799
- const payload = payloadOf(envelope)
800
- return service.recordMetric(ctx, {
801
- projectId: idOf(envelope, payload, 'projectId'),
802
- metricId: asString(payload.metricId, 'payload.metricId'),
803
- value:
804
- payload.value === undefined || payload.value === null
805
- ? null
806
- : asNumber(payload.value, 'payload.value'),
807
- observedAt: payload.observedAt === undefined ? undefined : asNumber(payload.observedAt, 'payload.observedAt'),
808
- windowStart: payload.windowStart === undefined ? undefined : asNumber(payload.windowStart, 'payload.windowStart'),
809
- windowEnd: payload.windowEnd === undefined ? undefined : asNumber(payload.windowEnd, 'payload.windowEnd'),
810
- sourceRef: optionalString(payload.sourceRef, 'payload.sourceRef'),
811
- })
812
- },
813
- },
814
- ],
815
-
816
- ...[
817
- ['project/publishTarget','publishProjectTarget'],
818
- ['project/createPhase','createProjectPhase'],
819
- ['project/updatePhase','updateProjectPhase'],
820
- ['project/confirmPhase','confirmProjectPhase'],
821
- ['project/submitTargetCheck','submitProjectTargetCheck'],
822
- ['project/updateTarget','updateProjectTarget'],
823
- ['project/addTarget','addProjectTarget'],
824
- ['project/createReview','createProjectReview'],
825
- ['project/confirmReview','confirmProjectReview'],
826
- ].map(([path,method]):[string,Route]=>[path,{write:true,handle:(service,ctx,envelope)=>{
827
- const payload=payloadOf(envelope)
828
- return (service[method as 'publishProjectTarget'] as Function).call(service,ctx,{...payload,projectId:idOf(envelope,payload,'projectId')})
829
- }}]),
830
- ['project/tryMetric',{write:false,handle:(service,ctx,envelope)=>{
831
- const p=payloadOf(envelope)
832
- return ok(service.tryProjectMetric(ctx,{projectId:optionalString(p.projectId,'projectId'),metric:asObject(p.metric,'metric') as any}),ctx.correlationId)
833
- }}],
834
- ['member/publishConfig',{write:true,handle:(service,ctx,envelope)=>{
835
- const p=payloadOf(envelope)
836
- return service.publishMemberConfig(ctx,{...p,memberId:idOf(envelope,p,'memberId')} as any)
837
- }}],
838
-
839
- // ───────────────────────── 人机成员(M6) ─────────────────────────
840
- [
841
- 'member/list',
842
- {
843
- write: false,
844
- handle: (service, ctx, envelope) => {
845
- const payload = payloadOf(envelope)
846
- return ok(
847
- service.listMembers(ctx, {
848
- kind: optionalString(payload.kind, 'payload.kind'),
849
- status: optionalString(payload.status, 'payload.status'),
850
- projectId: optionalString(payload.projectId, 'payload.projectId'),
851
- limit: payload.limit === undefined ? undefined : asNumber(payload.limit, 'payload.limit'),
852
- }),
853
- ctx.correlationId,
854
- )
855
- },
856
- },
857
- ],
858
- [
859
- 'member/get',
860
- {
861
- write: false,
862
- handle: (service, ctx, envelope) =>
863
- ok(service.getMemberDetail(ctx, idOf(envelope, payloadOf(envelope), 'memberId')), ctx.correlationId),
864
- },
865
- ],
866
- [
867
- 'position/list',
868
- {
869
- write: false,
870
- handle: (service, ctx) => ok(service.listPositions(ctx), ctx.correlationId),
871
- },
872
- ],
873
- [
874
- 'member/create',
875
- {
876
- write: true,
877
- handle: (service, ctx, envelope) => {
878
- const payload = payloadOf(envelope)
879
- return service.createMember(ctx, {
880
- kind: asString(payload.kind, 'payload.kind'),
881
- name: asString(payload.name, 'payload.name'),
882
- role: optionalString(payload.role, 'payload.role'),
883
- domain: optionalString(payload.domain, 'payload.domain'),
884
- responsibility: optionalString(payload.responsibility, 'payload.responsibility'),
885
- positionId: optionalString(payload.positionId, 'payload.positionId'),
886
- ownerHumanId: optionalString(payload.ownerHumanId, 'payload.ownerHumanId'),
887
- agentPreset: optionalString(payload.agentPreset, 'payload.agentPreset'),
888
- model: optionalString(payload.model, 'payload.model'),
889
- capabilities: stringArray(payload.capabilities, 'payload.capabilities'),
890
- scope: optionalString(payload.scope, 'payload.scope'),
891
- })
892
- },
893
- },
894
- ],
895
- [
896
- 'member/status',
897
- {
898
- write: true,
899
- handle: (service, ctx, envelope) => {
900
- const payload = payloadOf(envelope)
901
- return service.updateMemberStatus(ctx, {
902
- memberId: idOf(envelope, payload, 'memberId'),
903
- status: asString(payload.status, 'payload.status'),
904
- })
905
- },
906
- },
907
- ],
908
-
909
- // ───────────────────────── 知识(M6) ─────────────────────────
910
- [
911
- 'knowledge/list',
912
- {
913
- write: false,
914
- handle: (service, ctx, envelope) => {
915
- const payload = payloadOf(envelope)
916
- return ok(
917
- service.listKnowledge(ctx, {
918
- kind: optionalString(payload.kind, 'payload.kind'),
919
- q: optionalString(payload.q, 'payload.q'),
920
- limit: payload.limit === undefined ? undefined : asNumber(payload.limit, 'payload.limit'),
921
- }),
922
- ctx.correlationId,
923
- )
924
- },
925
- },
926
- ],
927
- [
928
- 'knowledge/get',
929
- {
930
- write: false,
931
- handle: (service, ctx, envelope) => {
932
- const payload = payloadOf(envelope)
933
- return ok(
934
- service.getKnowledge(ctx, {
935
- kind: asString(payload.kind, 'payload.kind'),
936
- id: idOf(envelope, payload, 'id'),
937
- }),
938
- ctx.correlationId,
939
- )
940
- },
941
- },
942
- ],
943
- [
944
- 'knowledge/sync',
945
- {
946
- write: true,
947
- handle: (service, ctx) => service.syncKnowledge(ctx),
948
- },
949
- ],
950
- [
951
- 'knowledge/createDoc',
952
- {
953
- write: true,
954
- handle: (service, ctx, envelope) => {
955
- const payload = payloadOf(envelope)
956
- return service.createKnowledgeDoc(ctx, {
957
- title: asString(payload.title, 'payload.title'),
958
- body: asString(payload.body, 'payload.body', { allowEmpty: true }),
959
- status: optionalString(payload.status, 'payload.status'),
960
- })
961
- },
962
- },
963
- ],
964
- [
965
- 'knowledge/updateDoc',
966
- {
967
- write: true,
968
- handle: (service, ctx, envelope) => {
969
- const payload = payloadOf(envelope)
970
- return service.updateKnowledgeDoc(ctx, {
971
- docId: idOf(envelope, payload, 'docId'),
972
- title: optionalString(payload.title, 'payload.title'),
973
- body: asString(payload.body, 'payload.body', { allowEmpty: true }),
974
- expectedRevision:
975
- payload.expectedRevision === undefined
976
- ? undefined
977
- : asNumber(payload.expectedRevision, 'payload.expectedRevision'),
978
- })
979
- },
980
- },
981
- ],
982
- [
983
- 'knowledge/publishDoc',
984
- {
985
- write: true,
986
- handle: (service, ctx, envelope) => {
987
- const payload = payloadOf(envelope)
988
- return service.publishKnowledgeDoc(ctx, { docId: idOf(envelope, payload, 'docId') })
989
- },
990
- },
991
- ],
992
- [
993
- 'knowledge/importDocs',
994
- {
995
- write: true,
996
- handle: (service, ctx, envelope) => {
997
- const payload = payloadOf(envelope)
998
- return service.importKnowledgeDocs(ctx, {
999
- dir: asString(payload.dir, 'payload.dir'),
1000
- recursive: payload.recursive === undefined ? undefined : payload.recursive === true,
1001
- maxFiles: payload.maxFiles === undefined ? undefined : asNumber(payload.maxFiles, 'payload.maxFiles'),
1002
- })
1003
- },
1004
- },
1005
- ],
1006
- ])
1007
-
1008
- export const ROUTE_PATHS: string[] = [...ROUTES.keys()].sort()
1009
-
1010
- function json(status: number, body: HamsResponseBody): HttpResponse {
1011
- return {
1012
- status,
1013
- headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' },
1014
- body: JSON.stringify(body),
1015
- }
1016
- }
1017
-
1018
- function transportError(
1019
- status: number,
1020
- correlationId: string,
1021
- message: string,
1022
- code: 'STATE_INVALID' | 'HOST_CAPABILITY_MISSING' = 'STATE_INVALID',
1023
- ): HttpResponse {
1024
- const body: ErrorResult = {
1025
- ok: false,
1026
- correlationId,
1027
- error: { code, message, nextStep: '检查请求路径与请求头后重试' },
1028
- }
1029
- return json(status, body)
1030
- }
1031
-
1032
- /** 认证失败也用同一份错误信封,界面按码分支;只有 401 才提示「重开带 token 的地址」。 */
1033
- function authError(correlationId: string, auth: AuthFailure): HttpResponse {
1034
- const status = auth.reason === 'UNAUTHORIZED' ? 401 : 403
1035
- return json(status, {
1036
- ok: false,
1037
- correlationId,
1038
- error: {
1039
- code: 'POLICY_DENIED',
1040
- message: auth.message,
1041
- nextStep:
1042
- auth.nextStep ??
1043
- (status === 401
1044
- ? '重新打开 dsh 打印的带 ?token= 的地址(或刷新页面)后再试'
1045
- : '确认请求来自本机面板后重试'),
1046
- },
1047
- })
1048
- }
1049
-
1050
- let counter = 0
1051
-
1052
- export function createHamsHttpHandler(deps: HamsHttpDeps): (request: HttpRequest) => HttpResponse | Promise<HttpResponse> {
1053
- const maxBody = deps.maxBodyBytes ?? DEFAULT_MAX_BODY
1054
- const newCorrelationId = deps.correlationId ?? (() => `http-${Date.now().toString(36)}-${(counter += 1)}`)
1055
-
1056
- // 同步命令一定返回对象(老测试与适配器都按同步读);只有对话这类要等宿主会话的命令返回 Promise。
1057
- const finish = (routeKey: string, result: CommandReceipt | QueryResult, correlationId: string): HttpResponse => {
1058
- if (routeKey === 'ops/health' && result.ok && 'data' in result) {
1059
- return json(200, { ...result, data: { ...result.data as object, readOnly: deps.readOnly === true } })
1060
- }
1061
- return json(200, result)
1062
- }
1063
-
1064
- return function handle(request: HttpRequest): HttpResponse | Promise<HttpResponse> {
1065
- const correlationId = newCorrelationId()
1066
-
1067
- if (request.method.toUpperCase() !== 'POST') {
1068
- return transportError(405, correlationId, '只接受 POST')
1069
- }
1070
- if (!request.path.startsWith(API_PREFIX)) {
1071
- return transportError(404, correlationId, `未知接口:${request.path}`)
1072
- }
1073
- const routeKey = request.path.slice(API_PREFIX.length)
1074
- const route = ROUTES.get(routeKey)
1075
- if (!route) return transportError(404, correlationId, `未知命令:${routeKey}`)
1076
-
1077
- if (Buffer.byteLength(request.body, 'utf8') > maxBody) {
1078
- return transportError(413, correlationId, '请求体过大')
1079
- }
1080
-
1081
- const auth = deps.authenticate(request)
1082
- if (!auth.ok) {
1083
- return authError(correlationId, auth)
1084
- }
1085
-
1086
- let rawBody: unknown
1087
- try {
1088
- rawBody = JSON.parse(request.body || '{}')
1089
- } catch {
1090
- return transportError(400, correlationId, '请求体不是合法 JSON')
1091
- }
1092
-
1093
- let envelope: CommandEnvelope
1094
- try {
1095
- envelope = requireEnvelope(rawBody, { write: route.write, correlationId })
1096
- } catch (error) {
1097
- return json(200, rejectedReceipt(correlationId, error))
1098
- }
1099
-
1100
- const ctx = toCommandContext({
1101
- principalId: auth.principalId,
1102
- channel: 'ui',
1103
- correlationId,
1104
- envelope,
1105
- authSubject: auth.authSubject,
1106
- hostPreset: auth.hostPreset,
1107
- onBehalfOf: auth.onBehalfOf,
1108
- })
1109
-
1110
- try {
1111
- if (deps.readOnly && route.write) {
1112
- throw new HamsError('HOST_CAPABILITY_MISSING', '另一实例持有写锁,当前实例为只读视图', {
1113
- nextStep: '关闭另一实例,或等它释放写锁后重试',
1114
- })
1115
- }
1116
- const principal = deps.service.whoami(ctx).principal
1117
- if (principal.kind === 'bot') {
1118
- // 通道隔离:bot 的动作只能走工具通道,不能借 HTTP 冒充人。
1119
- return json(200, rejectedReceipt(correlationId, new HamsError('POLICY_DENIED', 'bot 只能通过工具通道动作,不能调用 UI 接口')))
1120
- }
1121
- const result = route.handle(deps.service, ctx, envelope, { chat: deps.chat })
1122
- if (result && typeof (result as Promise<unknown>).then === 'function') {
1123
- return (result as Promise<CommandReceipt | QueryResult>)
1124
- .then((settled) => finish(routeKey, settled, correlationId))
1125
- .catch((error) => json(200, rejectedReceipt(correlationId, error)))
1126
- }
1127
- return finish(routeKey, result as CommandReceipt | QueryResult, correlationId)
1128
- } catch (error) {
1129
- return json(200, rejectedReceipt(correlationId, error))
1130
- }
1131
- }
1132
- }