@the-ai-company/cbio-node-runtime 1.58.0 → 1.59.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +51 -22
  2. package/dist/clients/agent/client.d.ts +3 -1
  3. package/dist/clients/agent/client.js +41 -11
  4. package/dist/clients/agent/client.js.map +1 -1
  5. package/dist/clients/agent/contracts.d.ts +5 -2
  6. package/dist/clients/owner/client.d.ts +6 -4
  7. package/dist/clients/owner/client.js +43 -19
  8. package/dist/clients/owner/client.js.map +1 -1
  9. package/dist/clients/owner/contracts.d.ts +11 -11
  10. package/dist/vault-core/contracts.d.ts +120 -24
  11. package/dist/vault-core/contracts.js +4 -2
  12. package/dist/vault-core/contracts.js.map +1 -1
  13. package/dist/vault-core/core.d.ts +10 -4
  14. package/dist/vault-core/core.js +302 -101
  15. package/dist/vault-core/core.js.map +1 -1
  16. package/dist/vault-core/defaults.d.ts +8 -2
  17. package/dist/vault-core/defaults.js +33 -10
  18. package/dist/vault-core/defaults.js.map +1 -1
  19. package/dist/vault-core/index.d.ts +1 -1
  20. package/dist/vault-core/index.js.map +1 -1
  21. package/dist/vault-core/persistence.d.ts +11 -2
  22. package/dist/vault-core/persistence.js +37 -1
  23. package/dist/vault-core/persistence.js.map +1 -1
  24. package/dist/vault-core/ports.d.ts +7 -1
  25. package/dist/vault-core/tool-metadata.js +25 -8
  26. package/dist/vault-core/tool-metadata.js.map +1 -1
  27. package/dist/vault-ingress/defaults.d.ts +2 -0
  28. package/dist/vault-ingress/defaults.js +6 -0
  29. package/dist/vault-ingress/defaults.js.map +1 -1
  30. package/dist/vault-ingress/index.d.ts +39 -9
  31. package/dist/vault-ingress/index.js +140 -45
  32. package/dist/vault-ingress/index.js.map +1 -1
  33. package/dist/vault-ingress/remote-transport.d.ts +2 -0
  34. package/dist/vault-ingress/remote-transport.js +33 -4
  35. package/dist/vault-ingress/remote-transport.js.map +1 -1
  36. package/docs/ARCHITECTURE.md +1 -1
  37. package/docs/REFERENCE.md +36 -27
  38. package/docs/WORKS_WITH_CUSTOM_FETCH.md +2 -2
  39. package/docs/api/README.md +2 -2
  40. package/docs/api/classes/IdentityError.md +1 -1
  41. package/docs/api/classes/OwnerClientError.md +1 -1
  42. package/docs/api/classes/VaultCore.md +92 -28
  43. package/docs/api/classes/VaultCoreError.md +1 -1
  44. package/docs/api/enumerations/IdentityErrorCode.md +1 -1
  45. package/docs/api/enumerations/OwnerClientErrorCode.md +1 -1
  46. package/docs/api/functions/createAgentClient.md +1 -1
  47. package/docs/api/functions/createIdentity.md +1 -1
  48. package/docs/api/functions/createOwnerHttpFlowBoundary.md +1 -1
  49. package/docs/api/functions/createOwnerSession.md +1 -1
  50. package/docs/api/functions/createPersistentVaultCoreDependencies.md +1 -1
  51. package/docs/api/functions/createStandardAcquireBoundary.md +1 -1
  52. package/docs/api/functions/createStandardDispatchBoundary.md +1 -1
  53. package/docs/api/functions/createVault.md +1 -1
  54. package/docs/api/functions/createVaultClient.md +1 -1
  55. package/docs/api/functions/createVaultCore.md +1 -1
  56. package/docs/api/functions/createVaultCoreDependencies.md +1 -1
  57. package/docs/api/functions/createVaultService.md +1 -1
  58. package/docs/api/functions/createWorkspaceStorage.md +1 -1
  59. package/docs/api/functions/deriveIdentityId.md +1 -1
  60. package/docs/api/functions/deriveVaultWorkingKeyFromPassword.md +1 -1
  61. package/docs/api/functions/getDefaultWorkspaceDir.md +1 -1
  62. package/docs/api/functions/handleVaultAgentControlHttp.md +1 -1
  63. package/docs/api/functions/handleVaultHttpDispatch.md +1 -1
  64. package/docs/api/functions/initializeVaultCustody.md +1 -1
  65. package/docs/api/functions/listVaults.md +1 -1
  66. package/docs/api/functions/readVaultProfile.md +1 -1
  67. package/docs/api/functions/recoverVault.md +1 -1
  68. package/docs/api/functions/recoverVaultWorkingKey.md +1 -1
  69. package/docs/api/functions/restoreIdentity.md +1 -1
  70. package/docs/api/functions/updateVaultMetadata.md +1 -1
  71. package/docs/api/functions/wrapVaultCoreAsVaultService.md +1 -1
  72. package/docs/api/functions/writeVaultProfile.md +1 -1
  73. package/docs/api/interfaces/AgentClient.md +27 -1
  74. package/docs/api/interfaces/AgentDispatchIntent.md +1 -1
  75. package/docs/api/interfaces/AgentDispatchTransport.md +33 -1
  76. package/docs/api/interfaces/AgentIdentity.md +1 -1
  77. package/docs/api/interfaces/AgentSigner.md +1 -1
  78. package/docs/api/interfaces/AgentSubmitCapabilityRequestInput.md +9 -9
  79. package/docs/api/interfaces/CbioRuntime.md +1 -1
  80. package/docs/api/interfaces/CreateAgentClientOptions.md +1 -1
  81. package/docs/api/interfaces/CreateIdentityOptions.md +1 -1
  82. package/docs/api/interfaces/CreateOwnerSessionOptions.md +1 -1
  83. package/docs/api/interfaces/CreatePersistentVaultCoreDependenciesOptions.md +1 -1
  84. package/docs/api/interfaces/CreateVaultClientOptions.md +1 -1
  85. package/docs/api/interfaces/CreateVaultOptions.md +1 -1
  86. package/docs/api/interfaces/CreatedVault.md +1 -1
  87. package/docs/api/interfaces/DefaultPolicyEngineOptions.md +1 -1
  88. package/docs/api/interfaces/IStorageProvider.md +1 -1
  89. package/docs/api/interfaces/InitializeVaultCustodyOptions.md +1 -1
  90. package/docs/api/interfaces/InitializedVaultCustody.md +1 -1
  91. package/docs/api/interfaces/OwnerAgentProvisionResult.md +1 -1
  92. package/docs/api/interfaces/OwnerSensitiveActionConfirmation.md +1 -1
  93. package/docs/api/interfaces/OwnerSensitiveActionContext.md +1 -1
  94. package/docs/api/interfaces/OwnerSession.md +1 -1
  95. package/docs/api/interfaces/OwnerStoreSecretInput.md +1 -1
  96. package/docs/api/interfaces/OwnerWriteSecretInput.md +1 -1
  97. package/docs/api/interfaces/RecoverVaultOptions.md +1 -1
  98. package/docs/api/interfaces/RecoveredVault.md +1 -1
  99. package/docs/api/interfaces/RestoreIdentityOptions.md +1 -1
  100. package/docs/api/interfaces/Signer.md +1 -1
  101. package/docs/api/interfaces/VaultApproveCapabilityRequestInput.md +1 -1
  102. package/docs/api/interfaces/VaultApproveDispatchInput.md +1 -1
  103. package/docs/api/interfaces/VaultAuditQueryInput.md +1 -1
  104. package/docs/api/interfaces/VaultClient.md +69 -37
  105. package/docs/api/interfaces/VaultCoreDependenciesOptions.md +1 -1
  106. package/docs/api/interfaces/VaultCreateAgentInput.md +1 -1
  107. package/docs/api/interfaces/VaultDeleteSecretInput.md +1 -1
  108. package/docs/api/interfaces/VaultExportSecretInput.md +1 -1
  109. package/docs/api/interfaces/VaultGrantCapabilityInput.md +9 -21
  110. package/docs/api/interfaces/VaultGrantCapabilityRequest.md +1 -1
  111. package/docs/api/interfaces/VaultIdentity.md +1 -1
  112. package/docs/api/interfaces/VaultImportAgentInput.md +1 -1
  113. package/docs/api/interfaces/VaultIssueSessionTokenInput.md +1 -1
  114. package/docs/api/interfaces/VaultListAgentsInput.md +1 -1
  115. package/docs/api/interfaces/VaultListCapabilitiesInput.md +1 -1
  116. package/docs/api/interfaces/VaultListSecretsInput.md +1 -1
  117. package/docs/api/interfaces/VaultMetadata.md +1 -1
  118. package/docs/api/interfaces/VaultObject.md +1 -1
  119. package/docs/api/interfaces/VaultProfile.md +1 -1
  120. package/docs/api/interfaces/VaultReadAgentPrivateKeyInput.md +1 -1
  121. package/docs/api/interfaces/VaultReadSecretPlaintextInput.md +1 -1
  122. package/docs/api/interfaces/VaultRegisterFlowInput.md +1 -1
  123. package/docs/api/interfaces/VaultRevokeCapabilityInput.md +1 -1
  124. package/docs/api/interfaces/VaultRevokeSessionTokenInput.md +1 -1
  125. package/docs/api/interfaces/VaultSigner.md +1 -1
  126. package/docs/api/interfaces/VaultSubmitCapabilityRequestInput.md +11 -17
  127. package/docs/api/interfaces/VaultUpdateAgentInput.md +1 -1
  128. package/docs/api/type-aliases/AgentCapabilityEnvelope.md +1 -1
  129. package/docs/api/type-aliases/AgentVisibleSecretRecord.md +1 -1
  130. package/docs/api/type-aliases/CbioRuntimeModule.md +1 -1
  131. package/docs/api/type-aliases/OwnerGrantCapabilityInput.md +1 -1
  132. package/docs/api/variables/DEFAULT_VAULT_KEY_CUSTODY_BLOB_KEY.md +1 -1
  133. package/docs/zh/README.md +25 -9
  134. package/examples/process-isolation.ts +6 -4
  135. package/package.json +1 -1
@@ -2,6 +2,7 @@ import { AuditAction, AuditOutcome, DispatchStatus, } from "./contracts.js";
2
2
  import { VaultCoreError } from "./errors.js";
3
3
  import { verifySignature } from "../protocol/crypto.js";
4
4
  import { getAgentToolbox } from "./tool-metadata.js";
5
+ import { InMemoryRequestRecordRegistry } from "./defaults.js";
5
6
  const VAULT_MASTER_ID = "vault-master";
6
7
  function toAuditEntry(deps, actor, action, outcome, detail, options) {
7
8
  return {
@@ -98,12 +99,21 @@ export class VaultCore {
98
99
  vaultId: state.vaultId,
99
100
  capabilityId: state.capabilityId ?? "",
100
101
  agentId: state.agentId,
101
- secretIds: state.secretIds ? [...state.secretIds] : undefined,
102
- secretAliases: state.secretAliases ? [...state.secretAliases] : undefined,
103
102
  operation: state.operation,
104
103
  customFlowId: state.customFlowId,
105
- scope: state.scope,
106
- methods: [...state.methods],
104
+ write: {
105
+ secretIds: state.write.secretIds ? [...state.write.secretIds] : undefined,
106
+ scope: state.write.scope,
107
+ methods: [...state.write.methods],
108
+ },
109
+ read: state.actions.read.status === "APPROVED"
110
+ ? {
111
+ mode: state.read.mode,
112
+ paths: state.read.paths ? [...state.read.paths] : undefined,
113
+ }
114
+ : {
115
+ mode: "none",
116
+ },
107
117
  issuedAt: state.issuedAt ?? state.requestedAt,
108
118
  expiresAt: state.expiresAt,
109
119
  rateLimit: state.rateLimit,
@@ -112,49 +122,68 @@ export class VaultCore {
112
122
  }
113
123
  async _buildAgentCapabilityStates(agentId) {
114
124
  return (await this._deps.capabilityStates.list(this._deps.vaultId, agentId)).map((state) => ({
115
- status: state.status,
116
125
  source: state.source,
117
126
  agentId: state.agentId,
118
127
  requestId: state.requestId,
119
128
  capabilityId: state.capabilityId,
120
129
  operation: state.operation,
121
- secretIds: state.secretIds ? [...state.secretIds] : undefined,
122
- secretAliases: state.secretAliases ? [...state.secretAliases] : undefined,
123
130
  customFlowId: state.customFlowId,
124
- scope: state.scope,
125
- methods: [...state.methods],
131
+ write: {
132
+ secretIds: state.write.secretIds ? [...state.write.secretIds] : undefined,
133
+ scope: state.write.scope,
134
+ methods: [...state.write.methods],
135
+ },
136
+ read: {
137
+ mode: state.read.mode,
138
+ paths: state.read.paths ? [...state.read.paths] : undefined,
139
+ },
126
140
  issuedAt: state.issuedAt,
127
141
  requestedAt: state.requestedAt,
128
142
  expiresAt: state.expiresAt,
129
143
  rateLimit: state.rateLimit,
130
144
  skipAudit: state.skipAudit,
131
145
  justification: state.justification,
132
- secretAlias: state.secretAlias,
146
+ secretId: state.secretId,
133
147
  targetUrl: state.targetUrl,
148
+ actions: {
149
+ write: { ...state.actions.write },
150
+ read: { ...state.actions.read },
151
+ },
134
152
  }));
135
153
  }
136
154
  _isExecutablePendingState(state) {
137
- return !!(state.requestId && state.targetUrl && state.secretAlias && state.proof);
155
+ return !!(state.requestId && state.targetUrl && state.proof);
138
156
  }
139
157
  async _executePendingCapabilityState(command, mode) {
140
158
  if (command.vaultId.value !== this._deps.vaultId.value) {
141
159
  throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
142
160
  }
143
161
  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");
162
+ if (!pending) {
163
+ throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
164
+ }
165
+ if (pending.actions.write.status !== "APPROVED") {
166
+ throw new VaultCoreError("write approval required before execution", "VAULT_WRITE_DENIED");
167
+ }
168
+ if (mode === "once" && pending.source !== "dispatch_discovery") {
169
+ throw new VaultCoreError("one-time execution is only available for dispatch discovery requests", "VAULT_WRITE_DENIED");
146
170
  }
147
171
  const issuedAt = this._deps.clock.nowIso();
148
172
  const capability = {
149
173
  vaultId: this._deps.vaultId,
150
174
  agentId: pending.agentId,
151
175
  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
176
  operation: pending.operation,
155
177
  customFlowId: pending.customFlowId,
156
- scope: pending.targetUrl ?? pending.scope,
157
- methods: [...pending.methods],
178
+ write: {
179
+ secretIds: pending.write.secretIds ? [...pending.write.secretIds] : undefined,
180
+ scope: pending.targetUrl ?? pending.write.scope,
181
+ methods: [...pending.write.methods],
182
+ },
183
+ read: {
184
+ mode: pending.read.mode,
185
+ paths: pending.read.paths ? [...pending.read.paths] : undefined,
186
+ },
158
187
  issuedAt,
159
188
  expiresAt: pending.expiresAt,
160
189
  rateLimit: pending.rateLimit,
@@ -166,9 +195,9 @@ export class VaultCore {
166
195
  vaultId: this._deps.vaultId,
167
196
  agent: { kind: "agent", id: pending.agentId },
168
197
  capability,
169
- secretAlias: pending.secretAlias === "unknown" ? undefined : pending.secretAlias,
198
+ secretId: pending.secretId,
170
199
  targetUrl: pending.targetUrl,
171
- method: pending.methods[0] ?? "POST",
200
+ method: pending.write.methods[0] ?? "POST",
172
201
  headers: pending.headers,
173
202
  body: pending.body,
174
203
  proof: pending.proof,
@@ -181,8 +210,8 @@ export class VaultCore {
181
210
  vaultId: this._deps.vaultId,
182
211
  requestId: pending.requestId ?? command.requestId,
183
212
  status: DispatchStatus.SUCCEEDED,
184
- targetUrl: pending.scope,
185
- method: pending.methods[0] ?? "POST",
213
+ targetUrl: pending.write.scope,
214
+ method: pending.write.methods[0] ?? "POST",
186
215
  };
187
216
  }
188
217
  else {
@@ -192,26 +221,13 @@ export class VaultCore {
192
221
  await this._deps.capabilityStates.upsert({
193
222
  ...pending,
194
223
  capabilityId: capability.capabilityId,
195
- status: "GRANTED",
196
224
  source: "owner_grant",
197
225
  issuedAt,
198
226
  decidedAt: issuedAt,
199
227
  });
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
228
  }
207
229
  else {
208
230
  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
231
  }
216
232
  return result;
217
233
  }
@@ -233,8 +249,8 @@ export class VaultCore {
233
249
  capabilityId: request.capability?.capabilityId,
234
250
  operation: request.capability?.operation ?? AuditAction.AUTHORIZE_DISPATCH,
235
251
  targetUrl: request.targetUrl,
236
- secretAlias: options?.secretAlias ?? request.secretAlias,
237
- secretId: options?.secretId,
252
+ secretAlias: options?.secretAlias,
253
+ secretId: options?.secretId ?? request.secretId,
238
254
  }));
239
255
  }
240
256
  async _verifyAgentControlProof(request, action, payload = {}) {
@@ -265,26 +281,32 @@ export class VaultCore {
265
281
  }
266
282
  async _listVisibleSecretsForAgent(agentId) {
267
283
  const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
268
- .filter((state) => state.status === "GRANTED")
284
+ .filter((state) => !!state.capabilityId && !!state.issuedAt && state.actions.write.status === "APPROVED")
269
285
  .map((state) => this._stateToGrantedCapability(state));
270
286
  const capabilityMap = new Map();
271
287
  for (const capability of capabilities) {
272
- for (const alias of capability.secretAliases ?? []) {
273
- const existing = capabilityMap.get(alias) ?? [];
288
+ for (const secretId of capability.write.secretIds ?? []) {
289
+ const existing = capabilityMap.get(secretId) ?? [];
274
290
  existing.push({
275
291
  capabilityId: capability.capabilityId,
276
- scope: capability.scope,
277
- methods: [...capability.methods],
292
+ write: {
293
+ secretIds: capability.write.secretIds ? [...capability.write.secretIds] : undefined,
294
+ scope: capability.write.scope,
295
+ methods: [...capability.write.methods],
296
+ },
297
+ read: {
298
+ mode: capability.read.mode,
299
+ paths: capability.read.paths ? [...capability.read.paths] : undefined,
300
+ },
278
301
  });
279
- capabilityMap.set(alias, existing);
302
+ capabilityMap.set(secretId, existing);
280
303
  }
281
304
  }
282
305
  const records = await this._deps.secrets.list(this._deps.vaultId);
283
306
  return records.map((record) => {
284
- const authorizedCapabilities = capabilityMap.get(record.alias.value) ?? [];
307
+ const authorizedCapabilities = capabilityMap.get(record.secretId.value) ?? [];
285
308
  return {
286
309
  vaultId: record.vaultId,
287
- secretId: record.secretId,
288
310
  alias: record.alias,
289
311
  issuerId: record.issuerId,
290
312
  source: record.source,
@@ -295,6 +317,48 @@ export class VaultCore {
295
317
  };
296
318
  });
297
319
  }
320
+ async _recordRequestExecution(request, capability, result) {
321
+ await this._deps.requests.save({
322
+ vaultId: this._deps.vaultId,
323
+ requestId: request.requestId,
324
+ agentId: request.agent.id,
325
+ capabilityId: capability?.capabilityId,
326
+ operation: capability?.operation ?? "dispatch_http",
327
+ createdAt: this._deps.clock.nowIso(),
328
+ request: {
329
+ targetUrl: request.targetUrl,
330
+ method: request.method,
331
+ headers: request.headers,
332
+ body: request.body,
333
+ secretId: request.secretId,
334
+ },
335
+ response: {
336
+ status: result.responseStatus,
337
+ body: result.responseBody,
338
+ error: result.error,
339
+ },
340
+ execution: {
341
+ status: result.status,
342
+ },
343
+ });
344
+ }
345
+ toVisibleRequestRecord(record, state) {
346
+ const readStatus = state?.actions.read.status ?? "PENDING";
347
+ return {
348
+ requestId: record.requestId,
349
+ createdAt: record.createdAt,
350
+ capabilityId: record.capabilityId,
351
+ operation: record.operation,
352
+ targetUrl: record.request.targetUrl,
353
+ method: record.request.method,
354
+ executionStatus: record.execution.status,
355
+ responseStatus: record.response?.status,
356
+ error: record.response?.error,
357
+ readStatus,
358
+ hasResponseBody: typeof record.response?.body === "string" && record.response.body.length > 0,
359
+ resultVisible: readStatus === "APPROVED",
360
+ };
361
+ }
298
362
  ownerOnCapabilityState(callback) {
299
363
  this._capabilityStateObservers.add(callback);
300
364
  return () => {
@@ -365,10 +429,13 @@ export class VaultCore {
365
429
  try {
366
430
  await this._deps.capabilityStates.upsert({
367
431
  ...command.capability,
368
- status: "GRANTED",
369
432
  source: "owner_grant",
370
433
  requestId: undefined,
371
434
  requestedAt: command.capability.issuedAt,
435
+ actions: {
436
+ write: { action: "write", status: "APPROVED", decidedAt: command.capability.issuedAt },
437
+ read: { action: "read", status: "APPROVED", decidedAt: command.capability.issuedAt },
438
+ },
372
439
  });
373
440
  await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CAPABILITY, AuditOutcome.SUCCEEDED, `capability registered: ${command.capability.capabilityId}`, {
374
441
  capabilityId: command.capability.capabilityId,
@@ -391,27 +458,36 @@ export class VaultCore {
391
458
  if (!command.agentId.trim()) {
392
459
  throw new VaultCoreError("capability request agent id required", "VAULT_IDENTITY_DENIED");
393
460
  }
394
- if (!command.scope.scope.trim()) {
461
+ if (!command.capability.write.scope.trim()) {
395
462
  throw new VaultCoreError("capability request scope required", "VAULT_IDENTITY_DENIED");
396
463
  }
397
- if (command.scope.methods.length === 0) {
464
+ if (command.capability.write.methods.length === 0) {
398
465
  throw new VaultCoreError("capability request method required", "VAULT_IDENTITY_DENIED");
399
466
  }
400
467
  const pendingRecord = {
401
468
  vaultId: this._deps.vaultId,
402
- status: "PENDING",
403
469
  source: "explicit_request",
404
470
  requestId: command.requestId,
405
471
  agentId: command.agentId,
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,
472
+ operation: command.capability.operation,
473
+ write: {
474
+ secretIds: command.capability.write.secretIds ? [...command.capability.write.secretIds] : undefined,
475
+ scope: command.capability.write.scope,
476
+ methods: [...command.capability.write.methods],
477
+ },
478
+ read: {
479
+ mode: command.capability.read.mode,
480
+ paths: command.capability.read.paths ? [...command.capability.read.paths] : undefined,
481
+ },
482
+ rateLimit: command.capability.rateLimit,
483
+ skipAudit: command.capability.skipAudit,
484
+ expiresAt: command.capability.expiresAt,
413
485
  justification: command.justification,
414
486
  requestedAt: command.requestedAt,
487
+ actions: {
488
+ write: { action: "write", status: "PENDING" },
489
+ read: { action: "read", status: "PENDING" },
490
+ },
415
491
  };
416
492
  await this._deps.capabilityStates.upsert(pendingRecord);
417
493
  for (const observer of this._capabilityStateObservers) {
@@ -425,7 +501,7 @@ export class VaultCore {
425
501
  await this._appendAudit(toAuditEntry(this._deps, command.requester, AuditAction.SUBMIT_CAPABILITY_REQUEST, AuditOutcome.PENDING, `capability request submitted for agent: ${command.agentId}`, {
426
502
  requestId: command.requestId,
427
503
  agentId: command.agentId,
428
- operation: command.scope.operation,
504
+ operation: command.capability.operation,
429
505
  }));
430
506
  return pendingRecord;
431
507
  }
@@ -434,17 +510,19 @@ export class VaultCore {
434
510
  throw new VaultCoreError("capability lookup vault mismatch", "VAULT_IDENTITY_DENIED");
435
511
  }
436
512
  const state = await this._deps.capabilityStates.getByCapabilityId(vaultId, agentId, capabilityId);
437
- return state && state.status === "GRANTED" ? this._stateToGrantedCapability(state) : null;
513
+ return state && state.capabilityId && state.issuedAt && state.actions.write.status === "APPROVED"
514
+ ? this._stateToGrantedCapability(state)
515
+ : null;
438
516
  }
439
517
  async ownerRegisterCustomFlow(command) {
440
518
  if (command.vaultId.value !== this._deps.vaultId.value) {
441
- throw new VaultCoreError("custom flow vault mismatch", "VAULT_IDENTITY_DENIED");
519
+ throw new VaultCoreError("request template vault mismatch", "VAULT_IDENTITY_DENIED");
442
520
  }
443
521
  if (!command.flow.flowId.trim()) {
444
- throw new VaultCoreError("custom flow id required", "VAULT_IDENTITY_DENIED");
522
+ throw new VaultCoreError("request template id required", "VAULT_IDENTITY_DENIED");
445
523
  }
446
524
  if (command.flow.mode !== "send_secret" && !command.flow.responseSecret) {
447
- throw new VaultCoreError("custom flow response secret rule required", "VAULT_IDENTITY_DENIED");
525
+ throw new VaultCoreError("request template response secret rule required", "VAULT_IDENTITY_DENIED");
448
526
  }
449
527
  try {
450
528
  await this._deps.customFlows.register({
@@ -458,7 +536,7 @@ export class VaultCore {
458
536
  responseSecret: command.flow.responseSecret,
459
537
  createdAt: this._deps.clock.nowIso(),
460
538
  });
461
- await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CUSTOM_FLOW, AuditOutcome.SUCCEEDED, `custom http flow registered: ${command.flow.flowId}`));
539
+ await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CUSTOM_FLOW, AuditOutcome.SUCCEEDED, `request template registered: ${command.flow.flowId}`));
462
540
  }
463
541
  catch (error) {
464
542
  const detail = error instanceof Error ? error.message : String(error);
@@ -493,7 +571,7 @@ export class VaultCore {
493
571
  try {
494
572
  await this._deps.custody.store(record.secretId, plaintext);
495
573
  await this._deps.secrets.save(record);
496
- await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, `custom flow stored secret: ${alias}`, {
574
+ await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.WRITE_SECRET, AuditOutcome.SUCCEEDED, `request template stored secret: ${alias}`, {
497
575
  secretAlias: record.alias.value,
498
576
  secretId: record.secretId.value,
499
577
  }));
@@ -568,10 +646,10 @@ export class VaultCore {
568
646
  if (request.vaultId.value !== this._deps.vaultId.value) {
569
647
  throw new VaultCoreError("request vault mismatch", "VAULT_DISPATCH_DENIED");
570
648
  }
571
- const record = request.secretAlias
572
- ? await this._deps.secrets.getByAlias({ value: request.secretAlias })
649
+ const record = request.secretId
650
+ ? await this._deps.secrets.getById({ value: request.secretId })
573
651
  : null;
574
- if (request.secretAlias && !record) {
652
+ if (request.secretId && !record) {
575
653
  await this._appendDecisionAudit(request, AuditOutcome.DENIED, "secret not found");
576
654
  return {
577
655
  vaultId: this._deps.vaultId,
@@ -588,7 +666,7 @@ export class VaultCore {
588
666
  catch (error) {
589
667
  const detail = error instanceof Error ? error.message : String(error);
590
668
  await this._appendDecisionAudit(request, AuditOutcome.DENIED, detail, {
591
- secretAlias: record?.alias.value ?? request.secretAlias,
669
+ secretAlias: record?.alias.value,
592
670
  secretId: record?.secretId.value,
593
671
  });
594
672
  throw error;
@@ -599,7 +677,7 @@ export class VaultCore {
599
677
  return { vaultId: this._deps.vaultId, decision: "deny", reason: "agent not found", secretId: null };
600
678
  }
601
679
  const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, request.agent.id))
602
- .filter((state) => state.status === "GRANTED")
680
+ .filter((state) => !!state.capabilityId && !!state.issuedAt && state.actions.write.status === "APPROVED")
603
681
  .map((state) => this._stateToGrantedCapability(state));
604
682
  const requestedCapabilityId = request.capability?.capabilityId;
605
683
  const candidateCapabilities = requestedCapabilityId
@@ -610,21 +688,29 @@ export class VaultCore {
610
688
  // It's a discovery case if the agent and secret exist but no capability matches
611
689
  const pendingRecord = {
612
690
  vaultId: this._deps.vaultId,
613
- status: "PENDING",
614
691
  source: "dispatch_discovery",
615
692
  requestId: request.requestId,
616
693
  agentId: request.agent.id,
617
694
  capabilityId: undefined,
618
695
  operation: "dispatch_http",
619
- secretAliases: request.secretAlias ? [request.secretAlias] : [],
620
- scope: request.targetUrl,
621
- methods: [request.method],
696
+ write: {
697
+ secretIds: request.secretId ? [request.secretId] : undefined,
698
+ scope: request.targetUrl,
699
+ methods: [request.method],
700
+ },
701
+ read: {
702
+ mode: "none",
703
+ },
622
704
  requestedAt: request.requestedAt,
623
- secretAlias: request.secretAlias ?? "unknown",
705
+ secretId: request.secretId,
624
706
  targetUrl: request.targetUrl,
625
707
  headers: request.headers,
626
708
  body: request.body,
627
709
  proof: request.proof,
710
+ actions: {
711
+ write: { action: "write", status: "PENDING" },
712
+ read: { action: "read", status: "PENDING" },
713
+ },
628
714
  };
629
715
  await this._deps.capabilityStates.upsert(pendingRecord);
630
716
  // Notify observers
@@ -637,7 +723,7 @@ export class VaultCore {
637
723
  }
638
724
  }
639
725
  await this._appendDecisionAudit(request, AuditOutcome.PENDING, "dispatch stalled for manual discovery approval", {
640
- secretAlias: record?.alias.value ?? request.secretAlias,
726
+ secretAlias: record?.alias.value,
641
727
  secretId: record?.secretId.value,
642
728
  });
643
729
  return {
@@ -656,7 +742,7 @@ export class VaultCore {
656
742
  catch (error) {
657
743
  const detail = error instanceof Error ? error.message : String(error);
658
744
  await this._appendDecisionAudit(request, AuditOutcome.DENIED, detail, {
659
- secretAlias: record?.alias.value ?? request.secretAlias,
745
+ secretAlias: record?.alias.value,
660
746
  secretId: record?.secretId.value,
661
747
  });
662
748
  return {
@@ -669,7 +755,7 @@ export class VaultCore {
669
755
  // Capability found, proceed
670
756
  if (!capability.skipAudit) {
671
757
  await this._appendDecisionAudit(request, AuditOutcome.ALLOWED, "dispatch authorized", {
672
- secretAlias: record?.alias.value ?? request.secretAlias,
758
+ secretAlias: record?.alias.value,
673
759
  secretId: record?.secretId.value,
674
760
  });
675
761
  }
@@ -720,9 +806,11 @@ export class VaultCore {
720
806
  secretAlias: record.alias.value,
721
807
  secretId: record.secretId.value,
722
808
  }));
809
+ await this._recordRequestExecution(request, authorization.capability, result);
723
810
  return {
724
811
  ...result,
725
812
  vaultId: this._deps.vaultId,
813
+ responseBody: undefined,
726
814
  };
727
815
  }
728
816
  async ownerReadAudit(actor, query, request) {
@@ -766,18 +854,16 @@ export class VaultCore {
766
854
  }
767
855
  }
768
856
  isCapabilityMatch(capability, request, secretId) {
769
- // Match either alias- or id-based capability grants when a secret is specified.
770
- if (request.secretAlias) {
771
- const aliasMatched = capability.secretAliases?.includes(request.secretAlias) ?? false;
772
- const idMatched = secretId ? (capability.secretIds?.includes(secretId) ?? false) : false;
773
- if (!aliasMatched && !idMatched) {
857
+ if (request.secretId) {
858
+ const idMatched = secretId ? (capability.write.secretIds?.includes(secretId) ?? false) : false;
859
+ if (!idMatched) {
774
860
  return false;
775
861
  }
776
862
  }
777
- if (request.method && capability.methods?.length > 0 && !capability.methods.includes(request.method)) {
863
+ if (request.method && capability.write.methods?.length > 0 && !capability.write.methods.includes(request.method)) {
778
864
  return false;
779
865
  }
780
- if (capability.scope && !isScopeMatch(capability.scope, request.targetUrl)) {
866
+ if (capability.write.scope && !isScopeMatch(capability.write.scope, request.targetUrl)) {
781
867
  return false;
782
868
  }
783
869
  return true;
@@ -791,7 +877,7 @@ export class VaultCore {
791
877
  }
792
878
  async ownerListCapabilities(actor, agentId, request) {
793
879
  const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
794
- .filter((state) => state.status === "GRANTED")
880
+ .filter((state) => !!state.capabilityId && !!state.issuedAt && state.actions.write.status === "APPROVED")
795
881
  .map((state) => this._stateToGrantedCapability(state));
796
882
  await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.LIST_CAPABILITIES, AuditOutcome.ALLOWED, "capabilities listed", {
797
883
  requestId: request?.requestId,
@@ -806,7 +892,6 @@ export class VaultCore {
806
892
  }));
807
893
  return records.map((record) => ({
808
894
  vaultId: record.vaultId,
809
- secretId: record.secretId,
810
895
  alias: record.alias,
811
896
  issuerId: record.issuerId,
812
897
  source: record.source,
@@ -828,6 +913,35 @@ export class VaultCore {
828
913
  await this._verifyAgentControlProof(request, "list_secrets");
829
914
  return this._listVisibleSecretsForAgent(request.agent.id);
830
915
  }
916
+ async agentListRequests(request) {
917
+ if (request.vaultId.value !== this._deps.vaultId.value) {
918
+ throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
919
+ }
920
+ await this._verifyAgentControlProof(request, "list_requests");
921
+ const records = await this._deps.requests.list(this._deps.vaultId, request.agent.id);
922
+ const states = await this._deps.capabilityStates.list(this._deps.vaultId, request.agent.id);
923
+ const stateByRequestId = new Map(states.filter((state) => state.requestId).map((state) => [state.requestId, state]));
924
+ return records.map((record) => this.toVisibleRequestRecord(record, stateByRequestId.get(record.requestId) ?? null));
925
+ }
926
+ async agentGetRequest(request) {
927
+ if (request.vaultId.value !== this._deps.vaultId.value) {
928
+ throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
929
+ }
930
+ await this._verifyAgentControlProof(request, "read_request_result", { targetRequestId: request.targetRequestId });
931
+ const record = await this._deps.requests.get(this._deps.vaultId, request.targetRequestId);
932
+ if (!record || record.agentId !== request.agent.id) {
933
+ throw new VaultCoreError("request record not found", "VAULT_READ_DENIED");
934
+ }
935
+ const state = await this._deps.capabilityStates.getByRequestId(this._deps.vaultId, request.targetRequestId);
936
+ const readApproved = state?.actions.read.status === "APPROVED";
937
+ return {
938
+ requestId: record.requestId,
939
+ executionStatus: record.execution.status,
940
+ responseStatus: record.response?.status,
941
+ responseBody: readApproved ? record.response?.body : undefined,
942
+ error: record.response?.error,
943
+ };
944
+ }
831
945
  async agentGetRuntimeManifest(command) {
832
946
  if (command.vaultId.value !== this._deps.vaultId.value) {
833
947
  throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
@@ -860,10 +974,9 @@ export class VaultCore {
860
974
  throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
861
975
  }
862
976
  await this._verifyAgentControlProof(command, "submit_capability_request", {
863
- scope: command.scope.scope,
864
- methods: command.scope.methods,
865
- operation: command.scope.operation,
866
- secretAliases: command.scope.secretAliases ?? [],
977
+ write: command.capability.write,
978
+ read: command.capability.read,
979
+ operation: command.capability.operation,
867
980
  justification: command.justification ?? null,
868
981
  });
869
982
  return this.ownerSubmitCapabilityRequest({
@@ -871,7 +984,7 @@ export class VaultCore {
871
984
  requestId: command.requestId,
872
985
  requester: command.agent,
873
986
  agentId: command.agent.id,
874
- scope: command.scope,
987
+ capability: command.capability,
875
988
  justification: command.justification,
876
989
  requestedAt: command.requestedAt,
877
990
  });
@@ -881,10 +994,14 @@ export class VaultCore {
881
994
  if (!existing) {
882
995
  throw new VaultCoreError("capability not found", "VAULT_CAPABILITY_NOT_FOUND");
883
996
  }
997
+ const decidedAt = this._deps.clock.nowIso();
884
998
  await this._deps.capabilityStates.upsert({
885
999
  ...existing,
886
- status: "REJECTED",
887
- decidedAt: this._deps.clock.nowIso(),
1000
+ decidedAt,
1001
+ actions: {
1002
+ write: { action: "write", status: "REJECTED", decidedAt },
1003
+ read: { action: "read", status: "REJECTED", decidedAt },
1004
+ },
888
1005
  });
889
1006
  await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REVOKE_CAPABILITY, AuditOutcome.SUCCEEDED, "capability revoked", {
890
1007
  requestId: command.requestId,
@@ -936,29 +1053,110 @@ export class VaultCore {
936
1053
  throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
937
1054
  }
938
1055
  return (await this._deps.capabilityStates.list(command.vaultId, command.agentId))
939
- .filter((state) => !command.status || state.status === command.status);
1056
+ .filter((state) => !command.writeStatus || state.actions.write.status === command.writeStatus)
1057
+ .filter((state) => !command.readStatus || state.actions.read.status === command.readStatus);
1058
+ }
1059
+ async ownerApproveCapabilityWrite(command) {
1060
+ if (command.vaultId.value !== this._deps.vaultId.value) {
1061
+ throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
1062
+ }
1063
+ const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
1064
+ if (!pending) {
1065
+ throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
1066
+ }
1067
+ if (pending.actions.write.status !== "PENDING") {
1068
+ throw new VaultCoreError("write approval not pending", "VAULT_WRITE_DENIED");
1069
+ }
1070
+ const decidedAt = this._deps.clock.nowIso();
1071
+ const next = {
1072
+ ...pending,
1073
+ decidedAt,
1074
+ actions: {
1075
+ ...pending.actions,
1076
+ write: {
1077
+ action: "write",
1078
+ status: "APPROVED",
1079
+ decidedAt,
1080
+ },
1081
+ },
1082
+ };
1083
+ await this._deps.capabilityStates.upsert(next);
1084
+ await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_WRITE, AuditOutcome.SUCCEEDED, `approved write policy for capability request ${command.requestId}`, {
1085
+ requestId: command.requestId,
1086
+ agentId: pending.agentId,
1087
+ operation: pending.operation,
1088
+ }));
1089
+ return next;
1090
+ }
1091
+ async ownerApproveCapabilityRead(command) {
1092
+ if (command.vaultId.value !== this._deps.vaultId.value) {
1093
+ throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
1094
+ }
1095
+ const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
1096
+ if (!pending) {
1097
+ throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
1098
+ }
1099
+ if (pending.actions.write.status !== "APPROVED") {
1100
+ throw new VaultCoreError("write approval required before read approval", "VAULT_WRITE_DENIED");
1101
+ }
1102
+ if (pending.actions.read.status !== "PENDING") {
1103
+ throw new VaultCoreError("read approval not pending", "VAULT_WRITE_DENIED");
1104
+ }
1105
+ const decidedAt = this._deps.clock.nowIso();
1106
+ const next = {
1107
+ ...pending,
1108
+ decidedAt,
1109
+ actions: {
1110
+ ...pending.actions,
1111
+ read: {
1112
+ action: "read",
1113
+ status: "APPROVED",
1114
+ decidedAt,
1115
+ },
1116
+ },
1117
+ };
1118
+ await this._deps.capabilityStates.upsert(next);
1119
+ await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_READ, AuditOutcome.SUCCEEDED, `approved read policy for capability request ${command.requestId}`, {
1120
+ requestId: command.requestId,
1121
+ agentId: pending.agentId,
1122
+ operation: pending.operation,
1123
+ }));
1124
+ return next;
940
1125
  }
941
- async ownerExecuteCapabilityStateOnce(command) {
1126
+ async ownerAllowOnce(command) {
942
1127
  return this._executePendingCapabilityState(command, "once");
943
1128
  }
944
- async ownerExecuteCapabilityStateAndGrant(command) {
1129
+ async ownerAllowAlways(command) {
945
1130
  return this._executePendingCapabilityState(command, "grant");
946
1131
  }
947
- async ownerRejectCapabilityState(command) {
1132
+ async ownerDeny(command) {
948
1133
  if (command.vaultId.value !== this._deps.vaultId.value) {
949
1134
  throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
950
1135
  }
951
1136
  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");
1137
+ if (!pending) {
1138
+ throw new VaultCoreError("capability action record not found", "VAULT_REQUEST_NOT_FOUND");
1139
+ }
1140
+ const decidedAt = this._deps.clock.nowIso();
1141
+ const rejectWrite = pending.actions.write.status === "PENDING";
1142
+ const rejectRead = !rejectWrite && pending.actions.read.status === "PENDING";
1143
+ if (!rejectWrite && !rejectRead) {
1144
+ throw new VaultCoreError("no capability action approval is pending", "VAULT_WRITE_DENIED");
954
1145
  }
955
1146
  const rejectedState = {
956
1147
  ...pending,
957
- status: "REJECTED",
958
- decidedAt: this._deps.clock.nowIso(),
1148
+ decidedAt,
1149
+ actions: {
1150
+ write: rejectWrite
1151
+ ? { action: "write", status: "REJECTED", decidedAt }
1152
+ : { ...pending.actions.write },
1153
+ read: rejectRead
1154
+ ? { action: "read", status: "REJECTED", decidedAt }
1155
+ : { ...pending.actions.read },
1156
+ },
959
1157
  };
960
1158
  await this._deps.capabilityStates.upsert(rejectedState);
961
- await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REJECT_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `rejected capability request ${command.requestId}`, {
1159
+ await this._appendAudit(toAuditEntry(this._deps, command.owner, rejectWrite ? AuditAction.REJECT_CAPABILITY_WRITE : AuditAction.REJECT_CAPABILITY_READ, AuditOutcome.SUCCEEDED, `rejected capability request ${command.requestId}`, {
962
1160
  requestId: command.requestId,
963
1161
  agentId: pending.agentId,
964
1162
  operation: pending.operation,
@@ -967,6 +1165,9 @@ export class VaultCore {
967
1165
  }
968
1166
  }
969
1167
  export function createVaultCore(deps) {
970
- return new VaultCore(deps);
1168
+ return new VaultCore({
1169
+ ...deps,
1170
+ requests: deps.requests ?? new InMemoryRequestRecordRegistry(),
1171
+ });
971
1172
  }
972
1173
  //# sourceMappingURL=core.js.map