@namzu/sdk 0.4.3 → 0.4.5

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 (108) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/dist/bridge/tools/connector/adapter.d.ts +2 -2
  3. package/dist/bus/index.d.ts +3 -1
  4. package/dist/bus/index.d.ts.map +1 -1
  5. package/dist/bus/index.js +18 -11
  6. package/dist/bus/index.js.map +1 -1
  7. package/dist/config/runtime.d.ts +28 -28
  8. package/dist/probe/context.d.ts +8 -0
  9. package/dist/probe/context.d.ts.map +1 -0
  10. package/dist/probe/context.js +7 -0
  11. package/dist/probe/context.js.map +1 -0
  12. package/dist/probe/errors.d.ts +12 -0
  13. package/dist/probe/errors.d.ts.map +1 -0
  14. package/dist/probe/errors.js +21 -0
  15. package/dist/probe/errors.js.map +1 -0
  16. package/dist/probe/index.d.ts +5 -0
  17. package/dist/probe/index.d.ts.map +1 -0
  18. package/dist/probe/index.js +4 -0
  19. package/dist/probe/index.js.map +1 -0
  20. package/dist/probe/registry.d.ts +24 -0
  21. package/dist/probe/registry.d.ts.map +1 -0
  22. package/dist/probe/registry.js +228 -0
  23. package/dist/probe/registry.js.map +1 -0
  24. package/dist/probe/registry.test.d.ts +7 -0
  25. package/dist/probe/registry.test.d.ts.map +1 -0
  26. package/dist/probe/registry.test.js +310 -0
  27. package/dist/probe/registry.test.js.map +1 -0
  28. package/dist/provider/instrumentation.d.ts +9 -0
  29. package/dist/provider/instrumentation.d.ts.map +1 -0
  30. package/dist/provider/instrumentation.js +104 -0
  31. package/dist/provider/instrumentation.js.map +1 -0
  32. package/dist/provider/instrumentation.test.d.ts +2 -0
  33. package/dist/provider/instrumentation.test.d.ts.map +1 -0
  34. package/dist/provider/instrumentation.test.js +152 -0
  35. package/dist/provider/instrumentation.test.js.map +1 -0
  36. package/dist/public-runtime.d.ts +5 -0
  37. package/dist/public-runtime.d.ts.map +1 -1
  38. package/dist/public-runtime.js +8 -0
  39. package/dist/public-runtime.js.map +1 -1
  40. package/dist/public-types.d.ts +3 -0
  41. package/dist/public-types.d.ts.map +1 -1
  42. package/dist/runtime/query/events.d.ts +3 -1
  43. package/dist/runtime/query/events.d.ts.map +1 -1
  44. package/dist/runtime/query/events.js +6 -1
  45. package/dist/runtime/query/events.js.map +1 -1
  46. package/dist/runtime/query/executor.d.ts +3 -1
  47. package/dist/runtime/query/executor.d.ts.map +1 -1
  48. package/dist/runtime/query/executor.js +30 -1
  49. package/dist/runtime/query/executor.js.map +1 -1
  50. package/dist/types/bus/index.d.ts +46 -2
  51. package/dist/types/bus/index.d.ts.map +1 -1
  52. package/dist/types/doctor/check.d.ts +41 -0
  53. package/dist/types/doctor/check.d.ts.map +1 -0
  54. package/dist/types/doctor/check.js +2 -0
  55. package/dist/types/doctor/check.js.map +1 -0
  56. package/dist/types/doctor/index.d.ts +2 -0
  57. package/dist/types/doctor/index.d.ts.map +1 -0
  58. package/dist/types/doctor/index.js +2 -0
  59. package/dist/types/doctor/index.js.map +1 -0
  60. package/dist/types/probe/event-kind.d.ts +6 -0
  61. package/dist/types/probe/event-kind.d.ts.map +1 -0
  62. package/dist/types/probe/event-kind.js +2 -0
  63. package/dist/types/probe/event-kind.js.map +1 -0
  64. package/dist/types/probe/event-of.d.ts +5 -0
  65. package/dist/types/probe/event-of.d.ts.map +1 -0
  66. package/dist/types/probe/event-of.js +2 -0
  67. package/dist/types/probe/event-of.js.map +1 -0
  68. package/dist/types/probe/index.d.ts +4 -0
  69. package/dist/types/probe/index.d.ts.map +1 -0
  70. package/dist/types/probe/index.js +2 -0
  71. package/dist/types/probe/index.js.map +1 -0
  72. package/dist/types/probe/registry.d.ts +27 -0
  73. package/dist/types/probe/registry.d.ts.map +1 -0
  74. package/dist/types/probe/registry.js +2 -0
  75. package/dist/types/probe/registry.js.map +1 -0
  76. package/dist/types/provider/interface.d.ts +10 -0
  77. package/dist/types/provider/interface.d.ts.map +1 -1
  78. package/dist/vault/instrumentation.d.ts +11 -0
  79. package/dist/vault/instrumentation.d.ts.map +1 -0
  80. package/dist/vault/instrumentation.js +32 -0
  81. package/dist/vault/instrumentation.js.map +1 -0
  82. package/dist/vault/instrumentation.test.d.ts +2 -0
  83. package/dist/vault/instrumentation.test.d.ts.map +1 -0
  84. package/dist/vault/instrumentation.test.js +80 -0
  85. package/dist/vault/instrumentation.test.js.map +1 -0
  86. package/package.json +1 -1
  87. package/src/bus/index.ts +21 -10
  88. package/src/probe/context.ts +14 -0
  89. package/src/probe/errors.ts +27 -0
  90. package/src/probe/index.ts +4 -0
  91. package/src/probe/registry.test.ts +480 -0
  92. package/src/probe/registry.ts +276 -0
  93. package/src/provider/instrumentation.test.ts +192 -0
  94. package/src/provider/instrumentation.ts +139 -0
  95. package/src/public-runtime.ts +22 -0
  96. package/src/public-types.ts +3 -0
  97. package/src/runtime/query/events.ts +6 -1
  98. package/src/runtime/query/executor.ts +34 -0
  99. package/src/types/bus/index.ts +54 -2
  100. package/src/types/doctor/check.ts +53 -0
  101. package/src/types/doctor/index.ts +9 -0
  102. package/src/types/probe/event-kind.ts +8 -0
  103. package/src/types/probe/event-of.ts +3 -0
  104. package/src/types/probe/index.ts +11 -0
  105. package/src/types/probe/registry.ts +36 -0
  106. package/src/types/provider/interface.ts +12 -0
  107. package/src/vault/instrumentation.test.ts +98 -0
  108. package/src/vault/instrumentation.ts +56 -0
@@ -1,6 +1,9 @@
1
1
  import { extractFromToolCall, extractFromToolResult } from '../../compaction/extractor.js'
2
2
  import type { WorkingStateManager } from '../../compaction/manager.js'
3
3
  import type { PluginLifecycleManager } from '../../plugin/lifecycle.js'
4
+ import { buildProbeContext } from '../../probe/context.js'
5
+ import { ProbeVetoError } from '../../probe/errors.js'
6
+ import { type ProbeRegistry, probe as defaultProbeRegistry } from '../../probe/registry.js'
4
7
  import type { ActivityStore } from '../../store/activity/memory.js'
5
8
  import type { RunId } from '../../types/ids/index.js'
6
9
  import type { InvocationState } from '../../types/invocation/index.js'
@@ -44,17 +47,20 @@ export class ToolExecutor {
44
47
  private emitEvent: EmitEvent
45
48
  private log: Logger
46
49
  private workingStateManager?: WorkingStateManager
50
+ private probes: ProbeRegistry
47
51
 
48
52
  constructor(
49
53
  config: ToolExecutorConfig,
50
54
  activityStore: ActivityStore,
51
55
  emitEvent: EmitEvent,
52
56
  log: Logger,
57
+ probes: ProbeRegistry = defaultProbeRegistry,
53
58
  ) {
54
59
  this.config = config
55
60
  this.activityStore = activityStore
56
61
  this.emitEvent = emitEvent
57
62
  this.log = log
63
+ this.probes = probes
58
64
  }
59
65
 
60
66
  setWorkingStateManager(manager: WorkingStateManager): void {
@@ -150,6 +156,34 @@ export class ToolExecutor {
150
156
  input,
151
157
  })
152
158
 
159
+ const vetoOutcome = this.probes.queryVeto(
160
+ {
161
+ type: 'tool_executing',
162
+ runId: this.config.runId,
163
+ toolName,
164
+ input,
165
+ },
166
+ buildProbeContext({ runId: this.config.runId }),
167
+ )
168
+ if (vetoOutcome.action === 'deny') {
169
+ const probeName = vetoOutcome.probeName ?? 'unnamed'
170
+ const reason = vetoOutcome.reason ?? 'no reason provided'
171
+ const veto = new ProbeVetoError(probeName, reason, 'tool_executing')
172
+ this.log.warn('Tool call denied by probe', {
173
+ runId: this.config.runId,
174
+ tool: toolName,
175
+ probeName,
176
+ reason,
177
+ })
178
+ if (activity) {
179
+ this.activityStore.fail(activity.id, veto.message)
180
+ }
181
+ return {
182
+ toolCallId: toolCall.id,
183
+ output: `Error: ${veto.message}`,
184
+ }
185
+ }
186
+
153
187
  if (this.workingStateManager) {
154
188
  extractFromToolCall(this.workingStateManager, toolName, JSON.stringify(input))
155
189
  }
@@ -1,6 +1,8 @@
1
- import type { RunId } from '../ids/index.js'
1
+ import type { CredentialId, LockId, RunId, SandboxId, TenantId } from '../ids/index.js'
2
2
 
3
- export type LockId = `lock_${string}`
3
+ export type { LockId } from '../ids/index.js'
4
+
5
+ export type ProviderCallId = `pcall_${string}`
4
6
 
5
7
  export type CircuitBreakerState = 'closed' | 'open' | 'half_open'
6
8
 
@@ -35,6 +37,15 @@ export type OwnershipClaimResult =
35
37
  | { claimed: true; ownership: FileOwnership }
36
38
  | { claimed: false; currentOwner: RunId; filePath: string }
37
39
 
40
+ export interface ProviderCallUsage {
41
+ readonly inputTokens?: number
42
+ readonly outputTokens?: number
43
+ readonly totalTokens?: number
44
+ readonly costUsd?: number
45
+ }
46
+
47
+ export type SandboxDecisionAction = 'allow' | 'deny'
48
+
38
49
  export type AgentBusEvent =
39
50
  | { type: 'lock_acquired'; lockId: LockId; filePath: string; owner: RunId }
40
51
  | { type: 'lock_released'; lockId: LockId; filePath: string; owner: RunId }
@@ -49,5 +60,46 @@ export type AgentBusEvent =
49
60
  | { type: 'breaker_half_open'; agentRunId: RunId }
50
61
  | { type: 'breaker_probe_success'; agentRunId: RunId }
51
62
  | { type: 'breaker_probe_failure'; agentRunId: RunId }
63
+ | {
64
+ type: 'provider_call_start'
65
+ providerId: string
66
+ model: string
67
+ callId: ProviderCallId
68
+ runId?: RunId
69
+ }
70
+ | {
71
+ type: 'provider_call_completed'
72
+ providerId: string
73
+ model: string
74
+ callId: ProviderCallId
75
+ runId?: RunId
76
+ durationMs: number
77
+ usage?: ProviderCallUsage
78
+ }
79
+ | {
80
+ type: 'provider_call_failed'
81
+ providerId: string
82
+ model: string
83
+ callId: ProviderCallId
84
+ runId?: RunId
85
+ durationMs: number
86
+ error: string
87
+ }
88
+ | {
89
+ type: 'vault_lookup'
90
+ vaultId: string
91
+ credentialId?: CredentialId
92
+ tenantId?: TenantId
93
+ found: boolean
94
+ runId?: RunId
95
+ }
96
+ | {
97
+ type: 'sandbox_decision'
98
+ sandboxId: SandboxId
99
+ action: SandboxDecisionAction
100
+ resource: string
101
+ ruleId?: string
102
+ runId?: RunId
103
+ }
52
104
 
53
105
  export type AgentBusEventListener = (event: AgentBusEvent) => void
@@ -0,0 +1,53 @@
1
+ export type DoctorStatus = 'pass' | 'fail' | 'inconclusive' | 'warn'
2
+
3
+ export type DoctorCategory =
4
+ | 'sandbox'
5
+ | 'providers'
6
+ | 'vault'
7
+ | 'telemetry'
8
+ | 'runtime'
9
+ | 'plugins'
10
+ | 'custom'
11
+
12
+ export interface DoctorCheckContext {
13
+ readonly cwd: string
14
+ readonly env: Readonly<Record<string, string | undefined>>
15
+ readonly projectRoot: string | null
16
+ }
17
+
18
+ export interface DoctorCheckResult {
19
+ readonly status: DoctorStatus
20
+ readonly message?: string
21
+ readonly remediation?: string
22
+ readonly durationMs?: number
23
+ }
24
+
25
+ export interface DoctorCheck {
26
+ readonly id: string
27
+ readonly category: DoctorCategory
28
+ readonly run: (ctx: DoctorCheckContext) => Promise<DoctorCheckResult>
29
+ readonly fix?: (ctx: DoctorCheckContext) => Promise<DoctorCheckResult>
30
+ }
31
+
32
+ export interface DoctorCheckRecord {
33
+ readonly id: string
34
+ readonly category: DoctorCategory
35
+ readonly status: DoctorStatus
36
+ readonly message?: string
37
+ readonly remediation?: string
38
+ readonly durationMs: number
39
+ }
40
+
41
+ export interface DoctorReport {
42
+ readonly version: string
43
+ readonly timestamp: string
44
+ readonly checks: readonly DoctorCheckRecord[]
45
+ readonly summary: {
46
+ readonly pass: number
47
+ readonly fail: number
48
+ readonly inconclusive: number
49
+ readonly warn: number
50
+ readonly total: number
51
+ }
52
+ readonly exit: 0 | 1 | 2 | 70
53
+ }
@@ -0,0 +1,9 @@
1
+ export type {
2
+ DoctorCategory,
3
+ DoctorCheck,
4
+ DoctorCheckContext,
5
+ DoctorCheckRecord,
6
+ DoctorCheckResult,
7
+ DoctorReport,
8
+ DoctorStatus,
9
+ } from './check.js'
@@ -0,0 +1,8 @@
1
+ import type { AgentBusEvent } from '../bus/index.js'
2
+ import type { RunEvent } from '../run/events.js'
3
+
4
+ export type ProbeEventKind = RunEvent['type'] | AgentBusEvent['type']
5
+
6
+ export type VetoableEventKind = 'tool_executing'
7
+
8
+ export type ProbeEvent = RunEvent | AgentBusEvent
@@ -0,0 +1,3 @@
1
+ import type { ProbeEvent, ProbeEventKind } from './event-kind.js'
2
+
3
+ export type ProbeEventOf<K extends ProbeEventKind> = Extract<ProbeEvent, { type: K }>
@@ -0,0 +1,11 @@
1
+ export type { ProbeEvent, ProbeEventKind, VetoableEventKind } from './event-kind.js'
2
+ export type { ProbeEventOf } from './event-of.js'
3
+ export type {
4
+ ProbeContext,
5
+ ProbeHandler,
6
+ ProbeOptions,
7
+ Unsubscribe,
8
+ VetoDecision,
9
+ VetoHandler,
10
+ VetoOutcome,
11
+ } from './registry.js'
@@ -0,0 +1,36 @@
1
+ import type { RunId } from '../ids/index.js'
2
+ import type { ProbeEventKind, VetoableEventKind } from './event-kind.js'
3
+ import type { ProbeEventOf } from './event-of.js'
4
+
5
+ export type Unsubscribe = () => void
6
+
7
+ export interface ProbeContext {
8
+ readonly runId?: RunId
9
+ readonly isReplay: boolean
10
+ }
11
+
12
+ export type ProbeHandler<K extends ProbeEventKind> = (
13
+ event: ProbeEventOf<K>,
14
+ ctx: ProbeContext,
15
+ ) => void
16
+
17
+ export type VetoDecision = 'allow' | 'deny' | { readonly action: 'deny'; readonly reason: string }
18
+
19
+ export type VetoHandler<K extends VetoableEventKind> = (
20
+ event: ProbeEventOf<K>,
21
+ ctx: ProbeContext,
22
+ ) => VetoDecision
23
+
24
+ export interface VetoOutcome {
25
+ readonly action: 'allow' | 'deny'
26
+ readonly probeName?: string
27
+ readonly reason?: string
28
+ }
29
+
30
+ export interface ProbeOptions<K extends ProbeEventKind = ProbeEventKind> {
31
+ readonly where?: (event: ProbeEventOf<K>) => boolean
32
+ readonly priority?: number
33
+ readonly name?: string
34
+ readonly otel?: boolean
35
+ readonly override?: boolean
36
+ }
@@ -1,3 +1,5 @@
1
+ import type { DoctorCheckResult } from '../doctor/index.js'
2
+
1
3
  import type { ChatCompletionParams, ChatCompletionResponse } from './chat.js'
2
4
  import type { ModelInfo } from './model.js'
3
5
  import type { StreamChunk } from './stream.js'
@@ -13,4 +15,14 @@ export interface LLMProvider {
13
15
  listModels?(): Promise<ModelInfo[]>
14
16
 
15
17
  healthCheck?(): Promise<boolean>
18
+
19
+ /**
20
+ * Optional structured health probe used by `runDoctor()`.
21
+ *
22
+ * Returns a `DoctorCheckResult` with provider-specific detail
23
+ * (latency, model availability, auth status, …). Providers that
24
+ * cannot be cheaply probed should return `{ status: 'inconclusive' }`
25
+ * so the doctor doesn't mark them as failing — see ses_007 Q6.4.
26
+ */
27
+ doctorCheck?(): Promise<DoctorCheckResult>
16
28
  }
@@ -0,0 +1,98 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { createProbeRegistry } from '../probe/registry.js'
4
+ import type { AgentBusEvent } from '../types/bus/index.js'
5
+ import type { CredentialId, TenantId } from '../types/ids/index.js'
6
+
7
+ import { InMemoryCredentialVault } from './InMemoryCredentialVault.js'
8
+ import { wrapVaultWithProbes } from './instrumentation.js'
9
+
10
+ const tenant = 'tnt_acme' as TenantId
11
+ const connector = 'conn_x' as never
12
+
13
+ describe('wrapVaultWithProbes', () => {
14
+ it('emits vault_lookup with found:true when retrieve hits', async () => {
15
+ const reg = createProbeRegistry()
16
+ const seen: AgentBusEvent[] = []
17
+ reg.onAny((event) => seen.push(event as AgentBusEvent))
18
+
19
+ const inner = new InMemoryCredentialVault()
20
+ const ref = await inner.store(tenant, connector, 'k', { type: 'apiKey', apiKey: 's' } as never)
21
+ const wrapped = wrapVaultWithProbes(inner, { probes: reg, vaultId: 'in-memory' })
22
+
23
+ const result = await wrapped.retrieve(ref.id)
24
+ expect(result).toBeDefined()
25
+ expect(seen.length).toBe(1)
26
+ const event = seen[0] as AgentBusEvent & { type: 'vault_lookup' }
27
+ expect(event.type).toBe('vault_lookup')
28
+ expect(event.found).toBe(true)
29
+ expect(event.vaultId).toBe('in-memory')
30
+ expect(event.credentialId).toBe(ref.id)
31
+ })
32
+
33
+ it('emits vault_lookup with found:false when retrieve misses', async () => {
34
+ const reg = createProbeRegistry()
35
+ const seen: AgentBusEvent[] = []
36
+ reg.onAny((event) => seen.push(event as AgentBusEvent))
37
+
38
+ const wrapped = wrapVaultWithProbes(new InMemoryCredentialVault(), {
39
+ probes: reg,
40
+ vaultId: 'in-memory',
41
+ })
42
+ const missing = 'cred_missing' as CredentialId
43
+ const result = await wrapped.retrieve(missing)
44
+
45
+ expect(result).toBeUndefined()
46
+ expect(seen.length).toBe(1)
47
+ const event = seen[0] as AgentBusEvent & { type: 'vault_lookup' }
48
+ expect(event.found).toBe(false)
49
+ expect(event.credentialId).toBe(missing)
50
+ })
51
+
52
+ it('does not emit on store/revoke/list — retrieve is the audit point', async () => {
53
+ const reg = createProbeRegistry()
54
+ const seen: AgentBusEvent[] = []
55
+ reg.onAny((event) => seen.push(event as AgentBusEvent))
56
+
57
+ const wrapped = wrapVaultWithProbes(new InMemoryCredentialVault(), { probes: reg })
58
+ const ref = await wrapped.store(tenant, connector, 'k', {
59
+ type: 'apiKey',
60
+ apiKey: 's',
61
+ } as never)
62
+ await wrapped.list(tenant)
63
+ await wrapped.revoke(ref.id)
64
+ expect(seen.length).toBe(0)
65
+ })
66
+
67
+ it('does not leak the secret value in the emitted event', async () => {
68
+ const reg = createProbeRegistry()
69
+ const seen: AgentBusEvent[] = []
70
+ reg.onAny((event) => seen.push(event as AgentBusEvent))
71
+
72
+ const inner = new InMemoryCredentialVault()
73
+ const ref = await inner.store(tenant, connector, 'k', {
74
+ type: 'apiKey',
75
+ apiKey: 'super-secret-value',
76
+ } as never)
77
+ const wrapped = wrapVaultWithProbes(inner, { probes: reg })
78
+ await wrapped.retrieve(ref.id)
79
+
80
+ const event = seen[0]
81
+ const serialized = JSON.stringify(event)
82
+ expect(serialized).not.toContain('super-secret-value')
83
+ })
84
+
85
+ it('falls back to constructor.name as vaultId when not specified', async () => {
86
+ const reg = createProbeRegistry()
87
+ const seen: AgentBusEvent[] = []
88
+ reg.onAny((event) => seen.push(event as AgentBusEvent))
89
+
90
+ const inner = new InMemoryCredentialVault()
91
+ const ref = await inner.store(tenant, connector, 'k', { type: 'apiKey', apiKey: 's' } as never)
92
+ const wrapped = wrapVaultWithProbes(inner, { probes: reg })
93
+ await wrapped.retrieve(ref.id)
94
+
95
+ const event = seen[0] as AgentBusEvent & { type: 'vault_lookup' }
96
+ expect(event.vaultId).toBe('InMemoryCredentialVault')
97
+ })
98
+ })
@@ -0,0 +1,56 @@
1
+ import { buildProbeContext } from '../probe/context.js'
2
+ import { type ProbeRegistry, probe as defaultProbeRegistry } from '../probe/registry.js'
3
+ import type { AuthConfig, CredentialRef, CredentialVault } from '../types/connector/index.js'
4
+ import type { ConnectorId, CredentialId, RunId, TenantId } from '../types/ids/index.js'
5
+
6
+ export interface VaultInstrumentationOptions {
7
+ readonly probes?: ProbeRegistry
8
+ readonly runId?: RunId
9
+ readonly vaultId?: string
10
+ readonly tenantId?: TenantId
11
+ }
12
+
13
+ export function wrapVaultWithProbes(
14
+ vault: CredentialVault,
15
+ opts: VaultInstrumentationOptions = {},
16
+ ): CredentialVault {
17
+ const probes = opts.probes ?? defaultProbeRegistry
18
+ const runId = opts.runId
19
+ const vaultId = opts.vaultId ?? vault.constructor.name
20
+ const tenantIdHint = opts.tenantId
21
+
22
+ return {
23
+ store(
24
+ tenantId: TenantId,
25
+ connectorId: ConnectorId,
26
+ label: string,
27
+ auth: AuthConfig,
28
+ ): Promise<CredentialRef> {
29
+ return vault.store(tenantId, connectorId, label, auth)
30
+ },
31
+
32
+ async retrieve(credentialId: CredentialId): Promise<AuthConfig | undefined> {
33
+ const result = await vault.retrieve(credentialId)
34
+ probes.dispatch(
35
+ {
36
+ type: 'vault_lookup',
37
+ vaultId,
38
+ credentialId,
39
+ tenantId: tenantIdHint,
40
+ found: result !== undefined,
41
+ runId,
42
+ },
43
+ buildProbeContext({ runId }),
44
+ )
45
+ return result
46
+ },
47
+
48
+ revoke(credentialId: CredentialId): Promise<boolean> {
49
+ return vault.revoke(credentialId)
50
+ },
51
+
52
+ list(tenantId: TenantId, connectorId?: ConnectorId): Promise<CredentialRef[]> {
53
+ return vault.list(tenantId, connectorId)
54
+ },
55
+ }
56
+ }