@the-ai-company/cbio-node-runtime 1.56.0 → 1.58.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.
Files changed (133) hide show
  1. package/README.md +27 -28
  2. package/dist/clients/agent/client.d.ts +2 -2
  3. package/dist/clients/agent/contracts.d.ts +3 -2
  4. package/dist/clients/owner/client.d.ts +8 -15
  5. package/dist/clients/owner/client.js +18 -51
  6. package/dist/clients/owner/client.js.map +1 -1
  7. package/dist/clients/owner/contracts.d.ts +11 -23
  8. package/dist/clients/owner/index.d.ts +1 -1
  9. package/dist/runtime/index.d.ts +1 -1
  10. package/dist/runtime/index.js.map +1 -1
  11. package/dist/vault-core/contracts.d.ts +61 -57
  12. package/dist/vault-core/contracts.js +0 -1
  13. package/dist/vault-core/contracts.js.map +1 -1
  14. package/dist/vault-core/core.d.ts +15 -22
  15. package/dist/vault-core/core.js +222 -211
  16. package/dist/vault-core/core.js.map +1 -1
  17. package/dist/vault-core/defaults.d.ts +8 -22
  18. package/dist/vault-core/defaults.js +18 -97
  19. package/dist/vault-core/defaults.js.map +1 -1
  20. package/dist/vault-core/index.d.ts +3 -3
  21. package/dist/vault-core/index.js +1 -1
  22. package/dist/vault-core/index.js.map +1 -1
  23. package/dist/vault-core/persistence.d.ts +8 -6
  24. package/dist/vault-core/persistence.js +16 -8
  25. package/dist/vault-core/persistence.js.map +1 -1
  26. package/dist/vault-core/ports.d.ts +8 -21
  27. package/dist/vault-ingress/defaults.d.ts +2 -2
  28. package/dist/vault-ingress/index.d.ts +14 -34
  29. package/dist/vault-ingress/index.js +20 -42
  30. package/dist/vault-ingress/index.js.map +1 -1
  31. package/dist/vault-ingress/remote-transport.d.ts +2 -2
  32. package/dist/vault-ingress/remote-transport.js.map +1 -1
  33. package/docs/MIGRATION-1.51.md +1 -1
  34. package/docs/REFERENCE.md +26 -28
  35. package/docs/api/README.md +2 -4
  36. package/docs/api/classes/IdentityError.md +1 -1
  37. package/docs/api/classes/OwnerClientError.md +1 -1
  38. package/docs/api/classes/VaultCore.md +29 -105
  39. package/docs/api/classes/VaultCoreError.md +1 -1
  40. package/docs/api/enumerations/IdentityErrorCode.md +1 -1
  41. package/docs/api/enumerations/OwnerClientErrorCode.md +1 -1
  42. package/docs/api/functions/createAgentClient.md +1 -1
  43. package/docs/api/functions/createIdentity.md +1 -1
  44. package/docs/api/functions/createOwnerHttpFlowBoundary.md +1 -1
  45. package/docs/api/functions/createOwnerSession.md +1 -1
  46. package/docs/api/functions/createPersistentVaultCoreDependencies.md +1 -1
  47. package/docs/api/functions/createStandardAcquireBoundary.md +1 -1
  48. package/docs/api/functions/createStandardDispatchBoundary.md +1 -1
  49. package/docs/api/functions/createVault.md +1 -1
  50. package/docs/api/functions/createVaultClient.md +1 -1
  51. package/docs/api/functions/createVaultCore.md +1 -1
  52. package/docs/api/functions/createVaultCoreDependencies.md +1 -1
  53. package/docs/api/functions/createVaultService.md +1 -1
  54. package/docs/api/functions/createWorkspaceStorage.md +1 -1
  55. package/docs/api/functions/deriveIdentityId.md +1 -1
  56. package/docs/api/functions/deriveVaultWorkingKeyFromPassword.md +1 -1
  57. package/docs/api/functions/getDefaultWorkspaceDir.md +1 -1
  58. package/docs/api/functions/handleVaultAgentControlHttp.md +1 -1
  59. package/docs/api/functions/handleVaultHttpDispatch.md +1 -1
  60. package/docs/api/functions/initializeVaultCustody.md +1 -1
  61. package/docs/api/functions/listVaults.md +1 -1
  62. package/docs/api/functions/readVaultProfile.md +1 -1
  63. package/docs/api/functions/recoverVault.md +1 -1
  64. package/docs/api/functions/recoverVaultWorkingKey.md +1 -1
  65. package/docs/api/functions/restoreIdentity.md +1 -1
  66. package/docs/api/functions/updateVaultMetadata.md +1 -1
  67. package/docs/api/functions/wrapVaultCoreAsVaultService.md +1 -1
  68. package/docs/api/functions/writeVaultProfile.md +1 -1
  69. package/docs/api/interfaces/AgentClient.md +5 -5
  70. package/docs/api/interfaces/AgentDispatchIntent.md +1 -1
  71. package/docs/api/interfaces/AgentDispatchTransport.md +5 -5
  72. package/docs/api/interfaces/AgentIdentity.md +1 -1
  73. package/docs/api/interfaces/AgentSigner.md +1 -1
  74. package/docs/api/interfaces/AgentSubmitCapabilityRequestInput.md +1 -1
  75. package/docs/api/interfaces/CbioRuntime.md +1 -1
  76. package/docs/api/interfaces/CreateAgentClientOptions.md +1 -1
  77. package/docs/api/interfaces/CreateIdentityOptions.md +1 -1
  78. package/docs/api/interfaces/CreateOwnerSessionOptions.md +1 -1
  79. package/docs/api/interfaces/CreatePersistentVaultCoreDependenciesOptions.md +1 -1
  80. package/docs/api/interfaces/CreateVaultClientOptions.md +1 -1
  81. package/docs/api/interfaces/CreateVaultOptions.md +1 -1
  82. package/docs/api/interfaces/CreatedVault.md +1 -1
  83. package/docs/api/interfaces/DefaultPolicyEngineOptions.md +1 -1
  84. package/docs/api/interfaces/IStorageProvider.md +1 -1
  85. package/docs/api/interfaces/InitializeVaultCustodyOptions.md +1 -1
  86. package/docs/api/interfaces/InitializedVaultCustody.md +1 -1
  87. package/docs/api/interfaces/OwnerAgentProvisionResult.md +1 -1
  88. package/docs/api/interfaces/OwnerSensitiveActionConfirmation.md +1 -1
  89. package/docs/api/interfaces/OwnerSensitiveActionContext.md +1 -1
  90. package/docs/api/interfaces/OwnerSession.md +1 -1
  91. package/docs/api/interfaces/OwnerStoreSecretInput.md +1 -1
  92. package/docs/api/interfaces/OwnerWriteSecretInput.md +1 -7
  93. package/docs/api/interfaces/RecoverVaultOptions.md +1 -1
  94. package/docs/api/interfaces/RecoveredVault.md +1 -1
  95. package/docs/api/interfaces/RestoreIdentityOptions.md +1 -1
  96. package/docs/api/interfaces/Signer.md +1 -1
  97. package/docs/api/interfaces/VaultApproveCapabilityRequestInput.md +1 -1
  98. package/docs/api/interfaces/VaultApproveDispatchInput.md +1 -1
  99. package/docs/api/interfaces/VaultAuditQueryInput.md +1 -1
  100. package/docs/api/interfaces/VaultClient.md +34 -88
  101. package/docs/api/interfaces/VaultCoreDependenciesOptions.md +1 -1
  102. package/docs/api/interfaces/VaultCreateAgentInput.md +1 -1
  103. package/docs/api/interfaces/VaultDeleteSecretInput.md +1 -1
  104. package/docs/api/interfaces/VaultExportSecretInput.md +1 -1
  105. package/docs/api/interfaces/VaultGrantCapabilityInput.md +1 -1
  106. package/docs/api/interfaces/VaultGrantCapabilityRequest.md +1 -1
  107. package/docs/api/interfaces/VaultIdentity.md +1 -1
  108. package/docs/api/interfaces/VaultImportAgentInput.md +1 -1
  109. package/docs/api/interfaces/VaultIssueSessionTokenInput.md +1 -1
  110. package/docs/api/interfaces/VaultListAgentsInput.md +1 -1
  111. package/docs/api/interfaces/VaultListCapabilitiesInput.md +1 -1
  112. package/docs/api/interfaces/VaultListSecretsInput.md +1 -1
  113. package/docs/api/interfaces/VaultMetadata.md +1 -1
  114. package/docs/api/interfaces/VaultObject.md +1 -1
  115. package/docs/api/interfaces/VaultProfile.md +1 -1
  116. package/docs/api/interfaces/VaultReadAgentPrivateKeyInput.md +1 -1
  117. package/docs/api/interfaces/VaultReadSecretPlaintextInput.md +1 -1
  118. package/docs/api/interfaces/VaultRegisterFlowInput.md +1 -1
  119. package/docs/api/interfaces/VaultRevokeCapabilityInput.md +1 -1
  120. package/docs/api/interfaces/VaultRevokeSessionTokenInput.md +1 -1
  121. package/docs/api/interfaces/VaultSigner.md +1 -1
  122. package/docs/api/interfaces/VaultSubmitCapabilityRequestInput.md +1 -1
  123. package/docs/api/interfaces/VaultUpdateAgentInput.md +1 -1
  124. package/docs/api/type-aliases/AgentCapabilityEnvelope.md +1 -1
  125. package/docs/api/type-aliases/AgentVisibleSecretRecord.md +1 -1
  126. package/docs/api/type-aliases/CbioRuntimeModule.md +1 -1
  127. package/docs/api/type-aliases/OwnerGrantCapabilityInput.md +1 -1
  128. package/docs/api/variables/DEFAULT_VAULT_KEY_CUSTODY_BLOB_KEY.md +1 -1
  129. package/docs/zh/README.md +20 -14
  130. package/examples/process-isolation.ts +1 -1
  131. package/package.json +1 -1
  132. package/docs/api/interfaces/OwnerDefineSecretTargetsInput.md +0 -23
  133. package/docs/api/interfaces/OwnerSecretTargetBinding.md +0 -35
@@ -23,15 +23,16 @@ function toAuditEntry(deps, actor, action, outcome, detail, options) {
23
23
  }
24
24
  function buildSecretRecord(deps, command) {
25
25
  const now = deps.clock.nowIso();
26
+ const source = command.source?.kind === "request" && command.source.requestId
27
+ ? { kind: "request", requestId: command.source.requestId }
28
+ : { kind: "manual" };
26
29
  return {
27
30
  vaultId: deps.vaultId,
28
31
  secretId: deps.ids.newSecretId(),
29
32
  alias: { value: command.alias },
30
33
  version: deps.ids.newVersion(),
31
34
  issuerId: command.kind === "issuer.write_secret" ? command.issuerSiteId : null,
32
- targetBindings: command.kind === "issuer.write_secret"
33
- ? [...(command.targetBindings ?? [{ kind: "site", targetId: command.issuerSiteId }])]
34
- : [...(command.targetBindings ?? [])],
35
+ source,
35
36
  createdAt: now,
36
37
  updatedAt: now,
37
38
  };
@@ -83,8 +84,7 @@ function createAgentControlBinding(requestId, requestedAt, agentId, action, payl
83
84
  */
84
85
  export class VaultCore {
85
86
  _deps;
86
- _pendingObservers = new Set();
87
- _pendingCapabilityObservers = new Set();
87
+ _capabilityStateObservers = new Set();
88
88
  constructor(_deps) {
89
89
  this._deps = _deps;
90
90
  }
@@ -93,6 +93,128 @@ export class VaultCore {
93
93
  throw new VaultCoreError("owner access denied", code);
94
94
  }
95
95
  }
96
+ _stateToGrantedCapability(state) {
97
+ return {
98
+ vaultId: state.vaultId,
99
+ capabilityId: state.capabilityId ?? "",
100
+ agentId: state.agentId,
101
+ secretIds: state.secretIds ? [...state.secretIds] : undefined,
102
+ secretAliases: state.secretAliases ? [...state.secretAliases] : undefined,
103
+ operation: state.operation,
104
+ customFlowId: state.customFlowId,
105
+ scope: state.scope,
106
+ methods: [...state.methods],
107
+ issuedAt: state.issuedAt ?? state.requestedAt,
108
+ expiresAt: state.expiresAt,
109
+ rateLimit: state.rateLimit,
110
+ skipAudit: state.skipAudit,
111
+ };
112
+ }
113
+ async _buildAgentCapabilityStates(agentId) {
114
+ return (await this._deps.capabilityStates.list(this._deps.vaultId, agentId)).map((state) => ({
115
+ status: state.status,
116
+ source: state.source,
117
+ agentId: state.agentId,
118
+ requestId: state.requestId,
119
+ capabilityId: state.capabilityId,
120
+ operation: state.operation,
121
+ secretIds: state.secretIds ? [...state.secretIds] : undefined,
122
+ secretAliases: state.secretAliases ? [...state.secretAliases] : undefined,
123
+ customFlowId: state.customFlowId,
124
+ scope: state.scope,
125
+ methods: [...state.methods],
126
+ issuedAt: state.issuedAt,
127
+ requestedAt: state.requestedAt,
128
+ expiresAt: state.expiresAt,
129
+ rateLimit: state.rateLimit,
130
+ skipAudit: state.skipAudit,
131
+ justification: state.justification,
132
+ secretAlias: state.secretAlias,
133
+ targetUrl: state.targetUrl,
134
+ }));
135
+ }
136
+ _isExecutablePendingState(state) {
137
+ return !!(state.requestId && state.targetUrl && state.secretAlias && state.proof);
138
+ }
139
+ async _executePendingCapabilityState(command, mode) {
140
+ if (command.vaultId.value !== this._deps.vaultId.value) {
141
+ throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
142
+ }
143
+ const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
144
+ if (!pending || pending.status !== "PENDING") {
145
+ throw new VaultCoreError("pending capability state not found", "VAULT_REQUEST_NOT_FOUND");
146
+ }
147
+ const issuedAt = this._deps.clock.nowIso();
148
+ const capability = {
149
+ vaultId: this._deps.vaultId,
150
+ agentId: pending.agentId,
151
+ capabilityId: pending.capabilityId ?? this._deps.ids.newCapabilityId(),
152
+ secretIds: pending.secretIds ? [...pending.secretIds] : undefined,
153
+ secretAliases: pending.secretAliases ? [...pending.secretAliases] : (pending.secretAlias ? [pending.secretAlias] : []),
154
+ operation: pending.operation,
155
+ customFlowId: pending.customFlowId,
156
+ scope: pending.targetUrl ?? pending.scope,
157
+ methods: [...pending.methods],
158
+ issuedAt,
159
+ expiresAt: pending.expiresAt,
160
+ rateLimit: pending.rateLimit,
161
+ skipAudit: pending.skipAudit,
162
+ };
163
+ let result;
164
+ if (this._isExecutablePendingState(pending)) {
165
+ result = await this.agentDispatchSecret({
166
+ vaultId: this._deps.vaultId,
167
+ agent: { kind: "agent", id: pending.agentId },
168
+ capability,
169
+ secretAlias: pending.secretAlias === "unknown" ? undefined : pending.secretAlias,
170
+ targetUrl: pending.targetUrl,
171
+ method: pending.methods[0] ?? "POST",
172
+ headers: pending.headers,
173
+ body: pending.body,
174
+ proof: pending.proof,
175
+ requestId: pending.requestId,
176
+ requestedAt: pending.requestedAt,
177
+ });
178
+ }
179
+ else if (mode === "grant") {
180
+ result = {
181
+ vaultId: this._deps.vaultId,
182
+ requestId: pending.requestId ?? command.requestId,
183
+ status: DispatchStatus.SUCCEEDED,
184
+ targetUrl: pending.scope,
185
+ method: pending.methods[0] ?? "POST",
186
+ };
187
+ }
188
+ else {
189
+ throw new VaultCoreError("pending capability state is not executable", "VAULT_WRITE_DENIED");
190
+ }
191
+ if (mode === "grant") {
192
+ await this._deps.capabilityStates.upsert({
193
+ ...pending,
194
+ capabilityId: capability.capabilityId,
195
+ status: "GRANTED",
196
+ source: "owner_grant",
197
+ issuedAt,
198
+ decidedAt: issuedAt,
199
+ });
200
+ await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `executed and granted capability state ${command.requestId}`, {
201
+ requestId: command.requestId,
202
+ agentId: pending.agentId,
203
+ capabilityId: capability.capabilityId,
204
+ operation: capability.operation,
205
+ }));
206
+ }
207
+ else {
208
+ await this._deps.capabilityStates.deleteByRequestId(command.vaultId, command.requestId);
209
+ await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `executed once and deleted capability state ${command.requestId}`, {
210
+ requestId: command.requestId,
211
+ agentId: pending.agentId,
212
+ capabilityId: capability.capabilityId,
213
+ operation: capability.operation,
214
+ }));
215
+ }
216
+ return result;
217
+ }
96
218
  get vaultId() {
97
219
  return this._deps.vaultId;
98
220
  }
@@ -142,7 +264,9 @@ export class VaultCore {
142
264
  }
143
265
  }
144
266
  async _listVisibleSecretsForAgent(agentId) {
145
- const capabilities = await this._deps.capabilities.list(this._deps.vaultId, agentId);
267
+ const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
268
+ .filter((state) => state.status === "GRANTED")
269
+ .map((state) => this._stateToGrantedCapability(state));
146
270
  const capabilityMap = new Map();
147
271
  for (const capability of capabilities) {
148
272
  for (const alias of capability.secretAliases ?? []) {
@@ -163,7 +287,7 @@ export class VaultCore {
163
287
  secretId: record.secretId,
164
288
  alias: record.alias,
165
289
  issuerId: record.issuerId,
166
- targetBindings: [...record.targetBindings],
290
+ source: record.source,
167
291
  createdAt: record.createdAt,
168
292
  updatedAt: record.updatedAt,
169
293
  isAuthorizedForAgent: authorizedCapabilities.length > 0,
@@ -171,16 +295,10 @@ export class VaultCore {
171
295
  };
172
296
  });
173
297
  }
174
- ownerOnPendingDispatch(callback) {
175
- this._pendingObservers.add(callback);
298
+ ownerOnCapabilityState(callback) {
299
+ this._capabilityStateObservers.add(callback);
176
300
  return () => {
177
- this._pendingObservers.delete(callback);
178
- };
179
- }
180
- ownerOnPendingCapabilityRequest(callback) {
181
- this._pendingCapabilityObservers.add(callback);
182
- return () => {
183
- this._pendingCapabilityObservers.delete(callback);
301
+ this._capabilityStateObservers.delete(callback);
184
302
  };
185
303
  }
186
304
  async ownerRegisterAgentIdentity(command) {
@@ -245,7 +363,13 @@ export class VaultCore {
245
363
  throw new VaultCoreError("capability id required", "VAULT_IDENTITY_DENIED");
246
364
  }
247
365
  try {
248
- await this._deps.capabilities.register(command.capability);
366
+ await this._deps.capabilityStates.upsert({
367
+ ...command.capability,
368
+ status: "GRANTED",
369
+ source: "owner_grant",
370
+ requestId: undefined,
371
+ requestedAt: command.capability.issuedAt,
372
+ });
249
373
  await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CAPABILITY, AuditOutcome.SUCCEEDED, `capability registered: ${command.capability.capabilityId}`, {
250
374
  capabilityId: command.capability.capabilityId,
251
375
  operation: command.capability.operation,
@@ -275,28 +399,27 @@ export class VaultCore {
275
399
  }
276
400
  const pendingRecord = {
277
401
  vaultId: this._deps.vaultId,
402
+ status: "PENDING",
403
+ source: "explicit_request",
278
404
  requestId: command.requestId,
279
- requester: command.requester,
280
405
  agentId: command.agentId,
281
- scope: {
282
- operation: command.scope.operation,
283
- secretAliases: command.scope.secretAliases ? [...command.scope.secretAliases] : [],
284
- scope: command.scope.scope,
285
- methods: [...command.scope.methods],
286
- rateLimit: command.scope.rateLimit,
287
- skipAudit: command.scope.skipAudit,
288
- expiresAt: command.scope.expiresAt,
289
- },
406
+ operation: command.scope.operation,
407
+ secretAliases: command.scope.secretAliases ? [...command.scope.secretAliases] : [],
408
+ scope: command.scope.scope,
409
+ methods: [...command.scope.methods],
410
+ rateLimit: command.scope.rateLimit,
411
+ skipAudit: command.scope.skipAudit,
412
+ expiresAt: command.scope.expiresAt,
290
413
  justification: command.justification,
291
414
  requestedAt: command.requestedAt,
292
415
  };
293
- await this._deps.pendingCapabilityRequests.save(pendingRecord);
294
- for (const observer of this._pendingCapabilityObservers) {
416
+ await this._deps.capabilityStates.upsert(pendingRecord);
417
+ for (const observer of this._capabilityStateObservers) {
295
418
  try {
296
419
  observer(pendingRecord);
297
420
  }
298
421
  catch (error) {
299
- console.error("VaultCore: error in pending capability observer:", error);
422
+ console.error("VaultCore: error in capability state observer:", error);
300
423
  }
301
424
  }
302
425
  await this._appendAudit(toAuditEntry(this._deps, command.requester, AuditAction.SUBMIT_CAPABILITY_REQUEST, AuditOutcome.PENDING, `capability request submitted for agent: ${command.agentId}`, {
@@ -310,7 +433,8 @@ export class VaultCore {
310
433
  if (vaultId.value !== this._deps.vaultId.value) {
311
434
  throw new VaultCoreError("capability lookup vault mismatch", "VAULT_IDENTITY_DENIED");
312
435
  }
313
- return this._deps.capabilities.get(vaultId, agentId, capabilityId);
436
+ const state = await this._deps.capabilityStates.getByCapabilityId(vaultId, agentId, capabilityId);
437
+ return state && state.status === "GRANTED" ? this._stateToGrantedCapability(state) : null;
314
438
  }
315
439
  async ownerRegisterCustomFlow(command) {
316
440
  if (command.vaultId.value !== this._deps.vaultId.value) {
@@ -344,13 +468,7 @@ export class VaultCore {
344
468
  }
345
469
  async _storeCustomFlowSecret(flow, alias, plaintext) {
346
470
  const actor = { kind: "owner", id: flow.ownerId };
347
- const targetBindings = [{
348
- kind: "site",
349
- targetId: flow.flowId,
350
- targetUrl: flow.targetUrl,
351
- methods: [flow.method],
352
- paths: [new URL(flow.targetUrl).pathname || "/"],
353
- }];
471
+ const requestId = this._deps.ids.newRequestId("custom_flow_store");
354
472
  const existing = await this._deps.secrets.getByAlias({ value: alias });
355
473
  if (existing) {
356
474
  await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.REASSIGN_ALIAS, AuditOutcome.DENIED, "alias already bound to existing secret; explicit alias lifecycle required", {
@@ -362,11 +480,14 @@ export class VaultCore {
362
480
  const record = buildSecretRecord(this._deps, {
363
481
  kind: "owner.write_secret",
364
482
  vaultId: this._deps.vaultId,
365
- requestId: this._deps.ids.newRequestId("custom_flow_store"),
483
+ requestId,
366
484
  owner: actor,
367
485
  alias,
368
486
  plaintext,
369
- targetBindings,
487
+ source: {
488
+ kind: "request",
489
+ requestId,
490
+ },
370
491
  requestedAt: this._deps.clock.nowIso(),
371
492
  });
372
493
  try {
@@ -443,41 +564,6 @@ export class VaultCore {
443
564
  secretId: record.secretId.value,
444
565
  }));
445
566
  }
446
- async ownerDefineSecretTargets(command) {
447
- if (command.vaultId.value !== this._deps.vaultId.value) {
448
- throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
449
- }
450
- try {
451
- await this._deps.policy.authorizeDefineSecretTargets(command);
452
- }
453
- catch (error) {
454
- const detail = error instanceof Error ? error.message : String(error);
455
- await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.DEFINE_SECRET_TARGETS, AuditOutcome.DENIED, detail, {
456
- secretAlias: command.alias,
457
- }));
458
- throw error;
459
- }
460
- const existing = await this._deps.secrets.getByAlias({ value: command.alias });
461
- if (!existing) {
462
- const error = new VaultCoreError("secret not found", "VAULT_SECRET_NOT_FOUND");
463
- await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.DEFINE_SECRET_TARGETS, AuditOutcome.DENIED, error.message, {
464
- secretAlias: command.alias,
465
- }));
466
- throw error;
467
- }
468
- const nextRecord = {
469
- ...existing,
470
- targetBindings: [...command.targetBindings],
471
- updatedAt: this._deps.clock.nowIso(),
472
- };
473
- await this._deps.secrets.save(nextRecord);
474
- await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.DEFINE_SECRET_TARGETS, AuditOutcome.SUCCEEDED, "secret targets defined", {
475
- requestId: command.requestId,
476
- secretAlias: nextRecord.alias.value,
477
- secretId: nextRecord.secretId.value,
478
- }));
479
- return nextRecord;
480
- }
481
567
  async agentAuthorizeDispatch(request) {
482
568
  if (request.vaultId.value !== this._deps.vaultId.value) {
483
569
  throw new VaultCoreError("request vault mismatch", "VAULT_DISPATCH_DENIED");
@@ -492,7 +578,6 @@ export class VaultCore {
492
578
  decision: "deny",
493
579
  reason: "secret not found",
494
580
  secretId: null,
495
- executorTarget: null,
496
581
  };
497
582
  }
498
583
  try {
@@ -511,41 +596,44 @@ export class VaultCore {
511
596
  // DISCOVERY LOGIC: Find best matching capability
512
597
  const agentRecord = await this._deps.agentIdentities.get(this._deps.vaultId, request.agent.id);
513
598
  if (!agentRecord) {
514
- return { vaultId: this._deps.vaultId, decision: "deny", reason: "agent not found", secretId: null, executorTarget: null };
599
+ return { vaultId: this._deps.vaultId, decision: "deny", reason: "agent not found", secretId: null };
515
600
  }
516
- const capabilities = await this._deps.capabilities.list(this._deps.vaultId, request.agent.id);
601
+ const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, request.agent.id))
602
+ .filter((state) => state.status === "GRANTED")
603
+ .map((state) => this._stateToGrantedCapability(state));
517
604
  const requestedCapabilityId = request.capability?.capabilityId;
518
605
  const candidateCapabilities = requestedCapabilityId
519
606
  ? capabilities.filter((cap) => cap.capabilityId === requestedCapabilityId)
520
607
  : capabilities;
521
608
  const capability = candidateCapabilities.find((cap) => this.isCapabilityMatch(cap, request, record?.secretId.value));
522
- const executorTarget = record
523
- ? record.targetBindings.find((binding) => binding.targetUrl === request.targetUrl)
524
- ?? record.targetBindings.find((binding) => binding.targetId === request.targetUrl)
525
- ?? null
526
- : null;
527
609
  if (!capability) {
528
610
  // It's a discovery case if the agent and secret exist but no capability matches
529
611
  const pendingRecord = {
612
+ vaultId: this._deps.vaultId,
613
+ status: "PENDING",
614
+ source: "dispatch_discovery",
530
615
  requestId: request.requestId,
531
616
  agentId: request.agent.id,
532
617
  capabilityId: undefined,
618
+ operation: "dispatch_http",
619
+ secretAliases: request.secretAlias ? [request.secretAlias] : [],
620
+ scope: request.targetUrl,
621
+ methods: [request.method],
622
+ requestedAt: request.requestedAt,
533
623
  secretAlias: request.secretAlias ?? "unknown",
534
624
  targetUrl: request.targetUrl,
535
- method: request.method,
536
625
  headers: request.headers,
537
626
  body: request.body,
538
- requestedAt: request.requestedAt,
539
627
  proof: request.proof,
540
628
  };
541
- await this._deps.pendingRequests.save(pendingRecord);
629
+ await this._deps.capabilityStates.upsert(pendingRecord);
542
630
  // Notify observers
543
- for (const observer of this._pendingObservers) {
631
+ for (const observer of this._capabilityStateObservers) {
544
632
  try {
545
633
  observer(pendingRecord);
546
634
  }
547
635
  catch (error) {
548
- console.error("VaultCore: error in pending observer:", error);
636
+ console.error("VaultCore: error in capability state observer:", error);
549
637
  }
550
638
  }
551
639
  await this._appendDecisionAudit(request, AuditOutcome.PENDING, "dispatch stalled for manual discovery approval", {
@@ -557,7 +645,6 @@ export class VaultCore {
557
645
  decision: "pending",
558
646
  reason: "no matching capability found (discovery needed)",
559
647
  secretId: record?.secretId ?? null,
560
- executorTarget,
561
648
  };
562
649
  }
563
650
  try {
@@ -577,7 +664,6 @@ export class VaultCore {
577
664
  decision: "deny",
578
665
  reason: detail,
579
666
  secretId: record?.secretId ?? null,
580
- executorTarget,
581
667
  };
582
668
  }
583
669
  // Capability found, proceed
@@ -592,7 +678,6 @@ export class VaultCore {
592
678
  decision: "allow",
593
679
  reason: null,
594
680
  secretId: record?.secretId ?? null,
595
- executorTarget,
596
681
  capability, // Expose the found capability for subsequent steps
597
682
  };
598
683
  }
@@ -705,7 +790,9 @@ export class VaultCore {
705
790
  return identities;
706
791
  }
707
792
  async ownerListCapabilities(actor, agentId, request) {
708
- const capabilities = await this._deps.capabilities.list(this._deps.vaultId, agentId);
793
+ const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
794
+ .filter((state) => state.status === "GRANTED")
795
+ .map((state) => this._stateToGrantedCapability(state));
709
796
  await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.LIST_CAPABILITIES, AuditOutcome.ALLOWED, "capabilities listed", {
710
797
  requestId: request?.requestId,
711
798
  agentId,
@@ -722,7 +809,7 @@ export class VaultCore {
722
809
  secretId: record.secretId,
723
810
  alias: record.alias,
724
811
  issuerId: record.issuerId,
725
- targetBindings: [...record.targetBindings],
812
+ source: record.source,
726
813
  createdAt: record.createdAt,
727
814
  updatedAt: record.updatedAt,
728
815
  }));
@@ -732,7 +819,7 @@ export class VaultCore {
732
819
  throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
733
820
  }
734
821
  await this._verifyAgentControlProof(request, "list_capabilities");
735
- return this._deps.capabilities.list(this._deps.vaultId, request.agent.id);
822
+ return this._buildAgentCapabilityStates(request.agent.id);
736
823
  }
737
824
  async agentListSecrets(request) {
738
825
  if (request.vaultId.value !== this._deps.vaultId.value) {
@@ -745,13 +832,25 @@ export class VaultCore {
745
832
  if (command.vaultId.value !== this._deps.vaultId.value) {
746
833
  throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
747
834
  }
748
- const capabilities = await this._deps.capabilities.list(this._deps.vaultId, command.agent.id);
835
+ await this._verifyAgentControlProof(command, "get_manifest");
836
+ const agentRecord = await this._deps.agentIdentities.get(this._deps.vaultId, command.agent.id);
837
+ if (!agentRecord) {
838
+ throw new VaultCoreError("agent identity not registered", "VAULT_DISPATCH_DENIED");
839
+ }
840
+ const capabilities = await this._buildAgentCapabilityStates(command.agent.id);
749
841
  const vaultNickname = "CBIO Vault"; // TODO: Pull from profile if available
750
842
  return {
751
843
  agentId: command.agent.id,
752
844
  vaultId: this._deps.vaultId.value,
753
845
  vaultNickname,
754
846
  issuedAt: this._deps.clock.nowIso(),
847
+ agent: {
848
+ agentId: agentRecord.agentId,
849
+ identityId: agentRecord.identityId,
850
+ publicKey: agentRecord.publicKey,
851
+ nickname: agentRecord.nickname,
852
+ metadata: agentRecord.metadata,
853
+ },
755
854
  capabilities,
756
855
  tools: getAgentToolbox(),
757
856
  };
@@ -778,7 +877,15 @@ export class VaultCore {
778
877
  });
779
878
  }
780
879
  async ownerRevokeCapability(command) {
781
- await this._deps.policy.revokeCapability(command.vaultId, command.agentId, command.capabilityId);
880
+ const existing = await this._deps.capabilityStates.getByCapabilityId(command.vaultId, command.agentId, command.capabilityId);
881
+ if (!existing) {
882
+ throw new VaultCoreError("capability not found", "VAULT_CAPABILITY_NOT_FOUND");
883
+ }
884
+ await this._deps.capabilityStates.upsert({
885
+ ...existing,
886
+ status: "REJECTED",
887
+ decidedAt: this._deps.clock.nowIso(),
888
+ });
782
889
  await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REVOKE_CAPABILITY, AuditOutcome.SUCCEEDED, "capability revoked", {
783
890
  requestId: command.requestId,
784
891
  agentId: command.agentId,
@@ -824,135 +931,39 @@ export class VaultCore {
824
931
  await this._deps.sessionTokens.revoke(request.token);
825
932
  await this._appendAudit(toAuditEntry(this._deps, request.actor, AuditAction.REVOKE_SESSION_TOKEN, AuditOutcome.SUCCEEDED, "session token revoked"));
826
933
  }
827
- async ownerListPendingDispatches(command) {
934
+ async ownerListCapabilityStates(command) {
828
935
  if (command.vaultId.value !== this._deps.vaultId.value) {
829
936
  throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
830
937
  }
831
- return this._deps.pendingRequests.list(command.vaultId);
938
+ return (await this._deps.capabilityStates.list(command.vaultId, command.agentId))
939
+ .filter((state) => !command.status || state.status === command.status);
832
940
  }
833
- async ownerListPendingCapabilityRequests(command) {
834
- if (command.vaultId.value !== this._deps.vaultId.value) {
835
- throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
836
- }
837
- return this._deps.pendingCapabilityRequests.list(command.vaultId);
941
+ async ownerExecuteCapabilityStateOnce(command) {
942
+ return this._executePendingCapabilityState(command, "once");
838
943
  }
839
- async ownerApproveCapabilityRequest(command) {
840
- if (command.vaultId.value !== this._deps.vaultId.value) {
841
- throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
842
- }
843
- const pending = await this._deps.pendingCapabilityRequests.get(command.requestId);
844
- if (!pending) {
845
- throw new VaultCoreError("pending capability request not found", "VAULT_REQUEST_NOT_FOUND");
846
- }
847
- const capability = {
848
- vaultId: this._deps.vaultId,
849
- agentId: pending.agentId,
850
- capabilityId: command.capabilityId ?? this._deps.ids.newCapabilityId(),
851
- operation: pending.scope.operation,
852
- secretAliases: pending.scope.secretAliases ? [...pending.scope.secretAliases] : [],
853
- scope: pending.scope.scope,
854
- methods: [...pending.scope.methods],
855
- rateLimit: pending.scope.rateLimit,
856
- skipAudit: pending.scope.skipAudit,
857
- expiresAt: pending.scope.expiresAt,
858
- issuedAt: this._deps.clock.nowIso(),
859
- };
860
- await this._deps.capabilities.register(capability);
861
- await this._deps.pendingCapabilityRequests.delete(command.requestId);
862
- await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `approved capability request ${command.requestId}`, {
863
- requestId: command.requestId,
864
- agentId: pending.agentId,
865
- capabilityId: capability.capabilityId,
866
- operation: capability.operation,
867
- }));
868
- return capability;
944
+ async ownerExecuteCapabilityStateAndGrant(command) {
945
+ return this._executePendingCapabilityState(command, "grant");
869
946
  }
870
- async ownerRejectCapabilityRequest(command) {
947
+ async ownerRejectCapabilityState(command) {
871
948
  if (command.vaultId.value !== this._deps.vaultId.value) {
872
949
  throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
873
950
  }
874
- const pending = await this._deps.pendingCapabilityRequests.get(command.requestId);
875
- if (!pending) {
876
- throw new VaultCoreError("pending capability request not found", "VAULT_REQUEST_NOT_FOUND");
951
+ const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
952
+ if (!pending || pending.status !== "PENDING") {
953
+ throw new VaultCoreError("pending capability state not found", "VAULT_REQUEST_NOT_FOUND");
877
954
  }
878
- await this._deps.pendingCapabilityRequests.delete(command.requestId);
955
+ const rejectedState = {
956
+ ...pending,
957
+ status: "REJECTED",
958
+ decidedAt: this._deps.clock.nowIso(),
959
+ };
960
+ await this._deps.capabilityStates.upsert(rejectedState);
879
961
  await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REJECT_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `rejected capability request ${command.requestId}`, {
880
962
  requestId: command.requestId,
881
963
  agentId: pending.agentId,
882
- operation: pending.scope.operation,
883
- }));
884
- }
885
- async ownerApproveDispatch(command) {
886
- if (command.vaultId.value !== this._deps.vaultId.value) {
887
- throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
888
- }
889
- const pending = await this._deps.pendingRequests.get(command.requestId);
890
- if (!pending) {
891
- throw new VaultCoreError("pending request not found", "VAULT_REQUEST_NOT_FOUND");
892
- }
893
- const agentRecord = await this._deps.agentIdentities.get(this._deps.vaultId, pending.agentId);
894
- if (!agentRecord) {
895
- throw new VaultCoreError("agent identity not found", "VAULT_AGENT_NOT_FOUND");
896
- }
897
- let capability;
898
- if (pending.capabilityId) {
899
- const existing = await this._deps.capabilities.get(this._deps.vaultId, pending.agentId, pending.capabilityId);
900
- if (!existing) {
901
- throw new VaultCoreError("capability not found", "VAULT_CAPABILITY_NOT_FOUND");
902
- }
903
- capability = existing;
904
- }
905
- else {
906
- // Discovery case: derive from request
907
- const capabilityId = this._deps.ids.newCapabilityId();
908
- capability = {
909
- vaultId: this._deps.vaultId,
910
- agentId: pending.agentId,
911
- capabilityId,
912
- secretAliases: [pending.secretAlias],
913
- methods: [pending.method],
914
- scope: pending.targetUrl,
915
- operation: "dispatch_http",
916
- issuedAt: this._deps.clock.nowIso(),
917
- skipAudit: command.skipAudit ?? false,
918
- };
919
- if (command.permanent) {
920
- await this._deps.capabilities.register(capability);
921
- }
922
- }
923
- const result = await this.agentDispatchSecret({
924
- vaultId: this._deps.vaultId,
925
- agent: { kind: "agent", id: pending.agentId },
926
- capability: capability,
927
- secretAlias: pending.secretAlias === "unknown" ? undefined : pending.secretAlias,
928
- targetUrl: pending.targetUrl,
929
- method: pending.method,
930
- headers: pending.headers,
931
- body: pending.body,
932
- proof: pending.proof,
933
- requestId: pending.requestId,
934
- requestedAt: pending.requestedAt,
935
- });
936
- await this._deps.pendingRequests.delete(command.requestId);
937
- await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_DISPATCH, AuditOutcome.SUCCEEDED, `approved dispatch ${command.requestId}${command.permanent ? " and granted permanent capability" : ""}`, {
938
- requestId: command.requestId,
939
- agentId: pending.agentId,
940
- capabilityId: capability.capabilityId,
941
- }));
942
- return result;
943
- }
944
- async ownerRejectDispatch(command) {
945
- if (command.vaultId.value !== this._deps.vaultId.value) {
946
- throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
947
- }
948
- const pending = await this._deps.pendingRequests.get(command.requestId);
949
- if (!pending) {
950
- throw new VaultCoreError("pending request not found", "VAULT_REQUEST_NOT_FOUND");
951
- }
952
- await this._deps.pendingRequests.delete(command.requestId);
953
- await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REJECT_DISPATCH, AuditOutcome.SUCCEEDED, `rejected dispatch ${command.requestId}`, {
954
- requestId: command.requestId,
964
+ operation: pending.operation,
955
965
  }));
966
+ return rejectedState;
956
967
  }
957
968
  }
958
969
  export function createVaultCore(deps) {