@the-ai-company/cbio-node-runtime 1.56.0 → 1.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -20
- package/dist/clients/agent/client.d.ts +2 -2
- package/dist/clients/agent/contracts.d.ts +3 -2
- package/dist/clients/owner/client.d.ts +7 -10
- package/dist/clients/owner/client.js +16 -32
- package/dist/clients/owner/client.js.map +1 -1
- package/dist/clients/owner/contracts.d.ts +11 -9
- package/dist/vault-core/contracts.d.ts +48 -35
- package/dist/vault-core/contracts.js.map +1 -1
- package/dist/vault-core/core.d.ts +15 -21
- package/dist/vault-core/core.js +209 -152
- package/dist/vault-core/core.js.map +1 -1
- package/dist/vault-core/defaults.d.ts +8 -20
- package/dist/vault-core/defaults.js +14 -37
- package/dist/vault-core/defaults.js.map +1 -1
- package/dist/vault-core/index.d.ts +3 -3
- package/dist/vault-core/index.js +1 -1
- package/dist/vault-core/index.js.map +1 -1
- package/dist/vault-core/persistence.d.ts +8 -6
- package/dist/vault-core/persistence.js +16 -8
- package/dist/vault-core/persistence.js.map +1 -1
- package/dist/vault-core/ports.d.ts +8 -20
- package/dist/vault-ingress/defaults.d.ts +2 -2
- package/dist/vault-ingress/index.d.ts +14 -33
- package/dist/vault-ingress/index.js +18 -31
- package/dist/vault-ingress/index.js.map +1 -1
- package/dist/vault-ingress/remote-transport.d.ts +2 -2
- package/dist/vault-ingress/remote-transport.js.map +1 -1
- package/docs/MIGRATION-1.51.md +1 -1
- package/docs/REFERENCE.md +26 -28
- package/docs/api/README.md +2 -2
- package/docs/api/classes/IdentityError.md +1 -1
- package/docs/api/classes/OwnerClientError.md +1 -1
- package/docs/api/classes/VaultCore.md +34 -94
- package/docs/api/classes/VaultCoreError.md +1 -1
- package/docs/api/enumerations/IdentityErrorCode.md +1 -1
- package/docs/api/enumerations/OwnerClientErrorCode.md +1 -1
- package/docs/api/functions/createAgentClient.md +1 -1
- package/docs/api/functions/createIdentity.md +1 -1
- package/docs/api/functions/createOwnerHttpFlowBoundary.md +1 -1
- package/docs/api/functions/createOwnerSession.md +1 -1
- package/docs/api/functions/createPersistentVaultCoreDependencies.md +1 -1
- package/docs/api/functions/createStandardAcquireBoundary.md +1 -1
- package/docs/api/functions/createStandardDispatchBoundary.md +1 -1
- package/docs/api/functions/createVault.md +1 -1
- package/docs/api/functions/createVaultClient.md +1 -1
- package/docs/api/functions/createVaultCore.md +1 -1
- package/docs/api/functions/createVaultCoreDependencies.md +1 -1
- package/docs/api/functions/createVaultService.md +1 -1
- package/docs/api/functions/createWorkspaceStorage.md +1 -1
- package/docs/api/functions/deriveIdentityId.md +1 -1
- package/docs/api/functions/deriveVaultWorkingKeyFromPassword.md +1 -1
- package/docs/api/functions/getDefaultWorkspaceDir.md +1 -1
- package/docs/api/functions/handleVaultAgentControlHttp.md +1 -1
- package/docs/api/functions/handleVaultHttpDispatch.md +1 -1
- package/docs/api/functions/initializeVaultCustody.md +1 -1
- package/docs/api/functions/listVaults.md +1 -1
- package/docs/api/functions/readVaultProfile.md +1 -1
- package/docs/api/functions/recoverVault.md +1 -1
- package/docs/api/functions/recoverVaultWorkingKey.md +1 -1
- package/docs/api/functions/restoreIdentity.md +1 -1
- package/docs/api/functions/updateVaultMetadata.md +1 -1
- package/docs/api/functions/wrapVaultCoreAsVaultService.md +1 -1
- package/docs/api/functions/writeVaultProfile.md +1 -1
- package/docs/api/interfaces/AgentClient.md +5 -5
- package/docs/api/interfaces/AgentDispatchIntent.md +1 -1
- package/docs/api/interfaces/AgentDispatchTransport.md +5 -5
- package/docs/api/interfaces/AgentIdentity.md +1 -1
- package/docs/api/interfaces/AgentSigner.md +1 -1
- package/docs/api/interfaces/AgentSubmitCapabilityRequestInput.md +1 -1
- package/docs/api/interfaces/CbioRuntime.md +1 -1
- package/docs/api/interfaces/CreateAgentClientOptions.md +1 -1
- package/docs/api/interfaces/CreateIdentityOptions.md +1 -1
- package/docs/api/interfaces/CreateOwnerSessionOptions.md +1 -1
- package/docs/api/interfaces/CreatePersistentVaultCoreDependenciesOptions.md +1 -1
- package/docs/api/interfaces/CreateVaultClientOptions.md +1 -1
- package/docs/api/interfaces/CreateVaultOptions.md +1 -1
- package/docs/api/interfaces/CreatedVault.md +1 -1
- package/docs/api/interfaces/DefaultPolicyEngineOptions.md +1 -1
- package/docs/api/interfaces/IStorageProvider.md +1 -1
- package/docs/api/interfaces/InitializeVaultCustodyOptions.md +1 -1
- package/docs/api/interfaces/InitializedVaultCustody.md +1 -1
- package/docs/api/interfaces/OwnerAgentProvisionResult.md +1 -1
- package/docs/api/interfaces/OwnerDefineSecretTargetsInput.md +1 -1
- package/docs/api/interfaces/OwnerSecretTargetBinding.md +1 -1
- package/docs/api/interfaces/OwnerSensitiveActionConfirmation.md +1 -1
- package/docs/api/interfaces/OwnerSensitiveActionContext.md +1 -1
- package/docs/api/interfaces/OwnerSession.md +1 -1
- package/docs/api/interfaces/OwnerStoreSecretInput.md +1 -1
- package/docs/api/interfaces/OwnerWriteSecretInput.md +1 -1
- package/docs/api/interfaces/RecoverVaultOptions.md +1 -1
- package/docs/api/interfaces/RecoveredVault.md +1 -1
- package/docs/api/interfaces/RestoreIdentityOptions.md +1 -1
- package/docs/api/interfaces/Signer.md +1 -1
- package/docs/api/interfaces/VaultApproveCapabilityRequestInput.md +1 -1
- package/docs/api/interfaces/VaultApproveDispatchInput.md +1 -1
- package/docs/api/interfaces/VaultAuditQueryInput.md +1 -1
- package/docs/api/interfaces/VaultClient.md +40 -76
- package/docs/api/interfaces/VaultCoreDependenciesOptions.md +1 -1
- package/docs/api/interfaces/VaultCreateAgentInput.md +1 -1
- package/docs/api/interfaces/VaultDeleteSecretInput.md +1 -1
- package/docs/api/interfaces/VaultExportSecretInput.md +1 -1
- package/docs/api/interfaces/VaultGrantCapabilityInput.md +1 -1
- package/docs/api/interfaces/VaultGrantCapabilityRequest.md +1 -1
- package/docs/api/interfaces/VaultIdentity.md +1 -1
- package/docs/api/interfaces/VaultImportAgentInput.md +1 -1
- package/docs/api/interfaces/VaultIssueSessionTokenInput.md +1 -1
- package/docs/api/interfaces/VaultListAgentsInput.md +1 -1
- package/docs/api/interfaces/VaultListCapabilitiesInput.md +1 -1
- package/docs/api/interfaces/VaultListSecretsInput.md +1 -1
- package/docs/api/interfaces/VaultMetadata.md +1 -1
- package/docs/api/interfaces/VaultObject.md +1 -1
- package/docs/api/interfaces/VaultProfile.md +1 -1
- package/docs/api/interfaces/VaultReadAgentPrivateKeyInput.md +1 -1
- package/docs/api/interfaces/VaultReadSecretPlaintextInput.md +1 -1
- package/docs/api/interfaces/VaultRegisterFlowInput.md +1 -1
- package/docs/api/interfaces/VaultRevokeCapabilityInput.md +1 -1
- package/docs/api/interfaces/VaultRevokeSessionTokenInput.md +1 -1
- package/docs/api/interfaces/VaultSigner.md +1 -1
- package/docs/api/interfaces/VaultSubmitCapabilityRequestInput.md +1 -1
- package/docs/api/interfaces/VaultUpdateAgentInput.md +1 -1
- package/docs/api/type-aliases/AgentCapabilityEnvelope.md +1 -1
- package/docs/api/type-aliases/AgentVisibleSecretRecord.md +1 -1
- package/docs/api/type-aliases/CbioRuntimeModule.md +1 -1
- package/docs/api/type-aliases/OwnerGrantCapabilityInput.md +1 -1
- package/docs/api/variables/DEFAULT_VAULT_KEY_CUSTODY_BLOB_KEY.md +1 -1
- package/docs/zh/README.md +19 -7
- package/package.json +1 -1
package/dist/vault-core/core.js
CHANGED
|
@@ -83,8 +83,7 @@ function createAgentControlBinding(requestId, requestedAt, agentId, action, payl
|
|
|
83
83
|
*/
|
|
84
84
|
export class VaultCore {
|
|
85
85
|
_deps;
|
|
86
|
-
|
|
87
|
-
_pendingCapabilityObservers = new Set();
|
|
86
|
+
_capabilityStateObservers = new Set();
|
|
88
87
|
constructor(_deps) {
|
|
89
88
|
this._deps = _deps;
|
|
90
89
|
}
|
|
@@ -93,6 +92,128 @@ export class VaultCore {
|
|
|
93
92
|
throw new VaultCoreError("owner access denied", code);
|
|
94
93
|
}
|
|
95
94
|
}
|
|
95
|
+
_stateToGrantedCapability(state) {
|
|
96
|
+
return {
|
|
97
|
+
vaultId: state.vaultId,
|
|
98
|
+
capabilityId: state.capabilityId ?? "",
|
|
99
|
+
agentId: state.agentId,
|
|
100
|
+
secretIds: state.secretIds ? [...state.secretIds] : undefined,
|
|
101
|
+
secretAliases: state.secretAliases ? [...state.secretAliases] : undefined,
|
|
102
|
+
operation: state.operation,
|
|
103
|
+
customFlowId: state.customFlowId,
|
|
104
|
+
scope: state.scope,
|
|
105
|
+
methods: [...state.methods],
|
|
106
|
+
issuedAt: state.issuedAt ?? state.requestedAt,
|
|
107
|
+
expiresAt: state.expiresAt,
|
|
108
|
+
rateLimit: state.rateLimit,
|
|
109
|
+
skipAudit: state.skipAudit,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async _buildAgentCapabilityStates(agentId) {
|
|
113
|
+
return (await this._deps.capabilityStates.list(this._deps.vaultId, agentId)).map((state) => ({
|
|
114
|
+
status: state.status,
|
|
115
|
+
source: state.source,
|
|
116
|
+
agentId: state.agentId,
|
|
117
|
+
requestId: state.requestId,
|
|
118
|
+
capabilityId: state.capabilityId,
|
|
119
|
+
operation: state.operation,
|
|
120
|
+
secretIds: state.secretIds ? [...state.secretIds] : undefined,
|
|
121
|
+
secretAliases: state.secretAliases ? [...state.secretAliases] : undefined,
|
|
122
|
+
customFlowId: state.customFlowId,
|
|
123
|
+
scope: state.scope,
|
|
124
|
+
methods: [...state.methods],
|
|
125
|
+
issuedAt: state.issuedAt,
|
|
126
|
+
requestedAt: state.requestedAt,
|
|
127
|
+
expiresAt: state.expiresAt,
|
|
128
|
+
rateLimit: state.rateLimit,
|
|
129
|
+
skipAudit: state.skipAudit,
|
|
130
|
+
justification: state.justification,
|
|
131
|
+
secretAlias: state.secretAlias,
|
|
132
|
+
targetUrl: state.targetUrl,
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
_isExecutablePendingState(state) {
|
|
136
|
+
return !!(state.requestId && state.targetUrl && state.secretAlias && state.proof);
|
|
137
|
+
}
|
|
138
|
+
async _executePendingCapabilityState(command, mode) {
|
|
139
|
+
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
140
|
+
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
141
|
+
}
|
|
142
|
+
const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
|
|
143
|
+
if (!pending || pending.status !== "PENDING") {
|
|
144
|
+
throw new VaultCoreError("pending capability state not found", "VAULT_REQUEST_NOT_FOUND");
|
|
145
|
+
}
|
|
146
|
+
const issuedAt = this._deps.clock.nowIso();
|
|
147
|
+
const capability = {
|
|
148
|
+
vaultId: this._deps.vaultId,
|
|
149
|
+
agentId: pending.agentId,
|
|
150
|
+
capabilityId: pending.capabilityId ?? this._deps.ids.newCapabilityId(),
|
|
151
|
+
secretIds: pending.secretIds ? [...pending.secretIds] : undefined,
|
|
152
|
+
secretAliases: pending.secretAliases ? [...pending.secretAliases] : (pending.secretAlias ? [pending.secretAlias] : []),
|
|
153
|
+
operation: pending.operation,
|
|
154
|
+
customFlowId: pending.customFlowId,
|
|
155
|
+
scope: pending.targetUrl ?? pending.scope,
|
|
156
|
+
methods: [...pending.methods],
|
|
157
|
+
issuedAt,
|
|
158
|
+
expiresAt: pending.expiresAt,
|
|
159
|
+
rateLimit: pending.rateLimit,
|
|
160
|
+
skipAudit: pending.skipAudit,
|
|
161
|
+
};
|
|
162
|
+
let result;
|
|
163
|
+
if (this._isExecutablePendingState(pending)) {
|
|
164
|
+
result = await this.agentDispatchSecret({
|
|
165
|
+
vaultId: this._deps.vaultId,
|
|
166
|
+
agent: { kind: "agent", id: pending.agentId },
|
|
167
|
+
capability,
|
|
168
|
+
secretAlias: pending.secretAlias === "unknown" ? undefined : pending.secretAlias,
|
|
169
|
+
targetUrl: pending.targetUrl,
|
|
170
|
+
method: pending.methods[0] ?? "POST",
|
|
171
|
+
headers: pending.headers,
|
|
172
|
+
body: pending.body,
|
|
173
|
+
proof: pending.proof,
|
|
174
|
+
requestId: pending.requestId,
|
|
175
|
+
requestedAt: pending.requestedAt,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
else if (mode === "grant") {
|
|
179
|
+
result = {
|
|
180
|
+
vaultId: this._deps.vaultId,
|
|
181
|
+
requestId: pending.requestId ?? command.requestId,
|
|
182
|
+
status: DispatchStatus.SUCCEEDED,
|
|
183
|
+
targetUrl: pending.scope,
|
|
184
|
+
method: pending.methods[0] ?? "POST",
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
throw new VaultCoreError("pending capability state is not executable", "VAULT_WRITE_DENIED");
|
|
189
|
+
}
|
|
190
|
+
if (mode === "grant") {
|
|
191
|
+
await this._deps.capabilityStates.upsert({
|
|
192
|
+
...pending,
|
|
193
|
+
capabilityId: capability.capabilityId,
|
|
194
|
+
status: "GRANTED",
|
|
195
|
+
source: "owner_grant",
|
|
196
|
+
issuedAt,
|
|
197
|
+
decidedAt: issuedAt,
|
|
198
|
+
});
|
|
199
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `executed and granted capability state ${command.requestId}`, {
|
|
200
|
+
requestId: command.requestId,
|
|
201
|
+
agentId: pending.agentId,
|
|
202
|
+
capabilityId: capability.capabilityId,
|
|
203
|
+
operation: capability.operation,
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
await this._deps.capabilityStates.deleteByRequestId(command.vaultId, command.requestId);
|
|
208
|
+
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.APPROVE_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `executed once and deleted capability state ${command.requestId}`, {
|
|
209
|
+
requestId: command.requestId,
|
|
210
|
+
agentId: pending.agentId,
|
|
211
|
+
capabilityId: capability.capabilityId,
|
|
212
|
+
operation: capability.operation,
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
96
217
|
get vaultId() {
|
|
97
218
|
return this._deps.vaultId;
|
|
98
219
|
}
|
|
@@ -142,7 +263,9 @@ export class VaultCore {
|
|
|
142
263
|
}
|
|
143
264
|
}
|
|
144
265
|
async _listVisibleSecretsForAgent(agentId) {
|
|
145
|
-
const capabilities = await this._deps.
|
|
266
|
+
const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
|
|
267
|
+
.filter((state) => state.status === "GRANTED")
|
|
268
|
+
.map((state) => this._stateToGrantedCapability(state));
|
|
146
269
|
const capabilityMap = new Map();
|
|
147
270
|
for (const capability of capabilities) {
|
|
148
271
|
for (const alias of capability.secretAliases ?? []) {
|
|
@@ -171,16 +294,10 @@ export class VaultCore {
|
|
|
171
294
|
};
|
|
172
295
|
});
|
|
173
296
|
}
|
|
174
|
-
|
|
175
|
-
this.
|
|
176
|
-
return () => {
|
|
177
|
-
this._pendingObservers.delete(callback);
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
ownerOnPendingCapabilityRequest(callback) {
|
|
181
|
-
this._pendingCapabilityObservers.add(callback);
|
|
297
|
+
ownerOnCapabilityState(callback) {
|
|
298
|
+
this._capabilityStateObservers.add(callback);
|
|
182
299
|
return () => {
|
|
183
|
-
this.
|
|
300
|
+
this._capabilityStateObservers.delete(callback);
|
|
184
301
|
};
|
|
185
302
|
}
|
|
186
303
|
async ownerRegisterAgentIdentity(command) {
|
|
@@ -245,7 +362,13 @@ export class VaultCore {
|
|
|
245
362
|
throw new VaultCoreError("capability id required", "VAULT_IDENTITY_DENIED");
|
|
246
363
|
}
|
|
247
364
|
try {
|
|
248
|
-
await this._deps.
|
|
365
|
+
await this._deps.capabilityStates.upsert({
|
|
366
|
+
...command.capability,
|
|
367
|
+
status: "GRANTED",
|
|
368
|
+
source: "owner_grant",
|
|
369
|
+
requestId: undefined,
|
|
370
|
+
requestedAt: command.capability.issuedAt,
|
|
371
|
+
});
|
|
249
372
|
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REGISTER_CAPABILITY, AuditOutcome.SUCCEEDED, `capability registered: ${command.capability.capabilityId}`, {
|
|
250
373
|
capabilityId: command.capability.capabilityId,
|
|
251
374
|
operation: command.capability.operation,
|
|
@@ -275,28 +398,27 @@ export class VaultCore {
|
|
|
275
398
|
}
|
|
276
399
|
const pendingRecord = {
|
|
277
400
|
vaultId: this._deps.vaultId,
|
|
401
|
+
status: "PENDING",
|
|
402
|
+
source: "explicit_request",
|
|
278
403
|
requestId: command.requestId,
|
|
279
|
-
requester: command.requester,
|
|
280
404
|
agentId: command.agentId,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
expiresAt: command.scope.expiresAt,
|
|
289
|
-
},
|
|
405
|
+
operation: command.scope.operation,
|
|
406
|
+
secretAliases: command.scope.secretAliases ? [...command.scope.secretAliases] : [],
|
|
407
|
+
scope: command.scope.scope,
|
|
408
|
+
methods: [...command.scope.methods],
|
|
409
|
+
rateLimit: command.scope.rateLimit,
|
|
410
|
+
skipAudit: command.scope.skipAudit,
|
|
411
|
+
expiresAt: command.scope.expiresAt,
|
|
290
412
|
justification: command.justification,
|
|
291
413
|
requestedAt: command.requestedAt,
|
|
292
414
|
};
|
|
293
|
-
await this._deps.
|
|
294
|
-
for (const observer of this.
|
|
415
|
+
await this._deps.capabilityStates.upsert(pendingRecord);
|
|
416
|
+
for (const observer of this._capabilityStateObservers) {
|
|
295
417
|
try {
|
|
296
418
|
observer(pendingRecord);
|
|
297
419
|
}
|
|
298
420
|
catch (error) {
|
|
299
|
-
console.error("VaultCore: error in
|
|
421
|
+
console.error("VaultCore: error in capability state observer:", error);
|
|
300
422
|
}
|
|
301
423
|
}
|
|
302
424
|
await this._appendAudit(toAuditEntry(this._deps, command.requester, AuditAction.SUBMIT_CAPABILITY_REQUEST, AuditOutcome.PENDING, `capability request submitted for agent: ${command.agentId}`, {
|
|
@@ -310,7 +432,8 @@ export class VaultCore {
|
|
|
310
432
|
if (vaultId.value !== this._deps.vaultId.value) {
|
|
311
433
|
throw new VaultCoreError("capability lookup vault mismatch", "VAULT_IDENTITY_DENIED");
|
|
312
434
|
}
|
|
313
|
-
|
|
435
|
+
const state = await this._deps.capabilityStates.getByCapabilityId(vaultId, agentId, capabilityId);
|
|
436
|
+
return state && state.status === "GRANTED" ? this._stateToGrantedCapability(state) : null;
|
|
314
437
|
}
|
|
315
438
|
async ownerRegisterCustomFlow(command) {
|
|
316
439
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
@@ -513,7 +636,9 @@ export class VaultCore {
|
|
|
513
636
|
if (!agentRecord) {
|
|
514
637
|
return { vaultId: this._deps.vaultId, decision: "deny", reason: "agent not found", secretId: null, executorTarget: null };
|
|
515
638
|
}
|
|
516
|
-
const capabilities = await this._deps.
|
|
639
|
+
const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, request.agent.id))
|
|
640
|
+
.filter((state) => state.status === "GRANTED")
|
|
641
|
+
.map((state) => this._stateToGrantedCapability(state));
|
|
517
642
|
const requestedCapabilityId = request.capability?.capabilityId;
|
|
518
643
|
const candidateCapabilities = requestedCapabilityId
|
|
519
644
|
? capabilities.filter((cap) => cap.capabilityId === requestedCapabilityId)
|
|
@@ -527,25 +652,31 @@ export class VaultCore {
|
|
|
527
652
|
if (!capability) {
|
|
528
653
|
// It's a discovery case if the agent and secret exist but no capability matches
|
|
529
654
|
const pendingRecord = {
|
|
655
|
+
vaultId: this._deps.vaultId,
|
|
656
|
+
status: "PENDING",
|
|
657
|
+
source: "dispatch_discovery",
|
|
530
658
|
requestId: request.requestId,
|
|
531
659
|
agentId: request.agent.id,
|
|
532
660
|
capabilityId: undefined,
|
|
661
|
+
operation: "dispatch_http",
|
|
662
|
+
secretAliases: request.secretAlias ? [request.secretAlias] : [],
|
|
663
|
+
scope: request.targetUrl,
|
|
664
|
+
methods: [request.method],
|
|
665
|
+
requestedAt: request.requestedAt,
|
|
533
666
|
secretAlias: request.secretAlias ?? "unknown",
|
|
534
667
|
targetUrl: request.targetUrl,
|
|
535
|
-
method: request.method,
|
|
536
668
|
headers: request.headers,
|
|
537
669
|
body: request.body,
|
|
538
|
-
requestedAt: request.requestedAt,
|
|
539
670
|
proof: request.proof,
|
|
540
671
|
};
|
|
541
|
-
await this._deps.
|
|
672
|
+
await this._deps.capabilityStates.upsert(pendingRecord);
|
|
542
673
|
// Notify observers
|
|
543
|
-
for (const observer of this.
|
|
674
|
+
for (const observer of this._capabilityStateObservers) {
|
|
544
675
|
try {
|
|
545
676
|
observer(pendingRecord);
|
|
546
677
|
}
|
|
547
678
|
catch (error) {
|
|
548
|
-
console.error("VaultCore: error in
|
|
679
|
+
console.error("VaultCore: error in capability state observer:", error);
|
|
549
680
|
}
|
|
550
681
|
}
|
|
551
682
|
await this._appendDecisionAudit(request, AuditOutcome.PENDING, "dispatch stalled for manual discovery approval", {
|
|
@@ -705,7 +836,9 @@ export class VaultCore {
|
|
|
705
836
|
return identities;
|
|
706
837
|
}
|
|
707
838
|
async ownerListCapabilities(actor, agentId, request) {
|
|
708
|
-
const capabilities = await this._deps.
|
|
839
|
+
const capabilities = (await this._deps.capabilityStates.list(this._deps.vaultId, agentId))
|
|
840
|
+
.filter((state) => state.status === "GRANTED")
|
|
841
|
+
.map((state) => this._stateToGrantedCapability(state));
|
|
709
842
|
await this._appendAudit(toAuditEntry(this._deps, actor, AuditAction.LIST_CAPABILITIES, AuditOutcome.ALLOWED, "capabilities listed", {
|
|
710
843
|
requestId: request?.requestId,
|
|
711
844
|
agentId,
|
|
@@ -732,7 +865,7 @@ export class VaultCore {
|
|
|
732
865
|
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
733
866
|
}
|
|
734
867
|
await this._verifyAgentControlProof(request, "list_capabilities");
|
|
735
|
-
return this.
|
|
868
|
+
return this._buildAgentCapabilityStates(request.agent.id);
|
|
736
869
|
}
|
|
737
870
|
async agentListSecrets(request) {
|
|
738
871
|
if (request.vaultId.value !== this._deps.vaultId.value) {
|
|
@@ -745,13 +878,25 @@ export class VaultCore {
|
|
|
745
878
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
746
879
|
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
747
880
|
}
|
|
748
|
-
|
|
881
|
+
await this._verifyAgentControlProof(command, "get_manifest");
|
|
882
|
+
const agentRecord = await this._deps.agentIdentities.get(this._deps.vaultId, command.agent.id);
|
|
883
|
+
if (!agentRecord) {
|
|
884
|
+
throw new VaultCoreError("agent identity not registered", "VAULT_DISPATCH_DENIED");
|
|
885
|
+
}
|
|
886
|
+
const capabilities = await this._buildAgentCapabilityStates(command.agent.id);
|
|
749
887
|
const vaultNickname = "CBIO Vault"; // TODO: Pull from profile if available
|
|
750
888
|
return {
|
|
751
889
|
agentId: command.agent.id,
|
|
752
890
|
vaultId: this._deps.vaultId.value,
|
|
753
891
|
vaultNickname,
|
|
754
892
|
issuedAt: this._deps.clock.nowIso(),
|
|
893
|
+
agent: {
|
|
894
|
+
agentId: agentRecord.agentId,
|
|
895
|
+
identityId: agentRecord.identityId,
|
|
896
|
+
publicKey: agentRecord.publicKey,
|
|
897
|
+
nickname: agentRecord.nickname,
|
|
898
|
+
metadata: agentRecord.metadata,
|
|
899
|
+
},
|
|
755
900
|
capabilities,
|
|
756
901
|
tools: getAgentToolbox(),
|
|
757
902
|
};
|
|
@@ -778,7 +923,15 @@ export class VaultCore {
|
|
|
778
923
|
});
|
|
779
924
|
}
|
|
780
925
|
async ownerRevokeCapability(command) {
|
|
781
|
-
await this._deps.
|
|
926
|
+
const existing = await this._deps.capabilityStates.getByCapabilityId(command.vaultId, command.agentId, command.capabilityId);
|
|
927
|
+
if (!existing) {
|
|
928
|
+
throw new VaultCoreError("capability not found", "VAULT_CAPABILITY_NOT_FOUND");
|
|
929
|
+
}
|
|
930
|
+
await this._deps.capabilityStates.upsert({
|
|
931
|
+
...existing,
|
|
932
|
+
status: "REJECTED",
|
|
933
|
+
decidedAt: this._deps.clock.nowIso(),
|
|
934
|
+
});
|
|
782
935
|
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REVOKE_CAPABILITY, AuditOutcome.SUCCEEDED, "capability revoked", {
|
|
783
936
|
requestId: command.requestId,
|
|
784
937
|
agentId: command.agentId,
|
|
@@ -824,135 +977,39 @@ export class VaultCore {
|
|
|
824
977
|
await this._deps.sessionTokens.revoke(request.token);
|
|
825
978
|
await this._appendAudit(toAuditEntry(this._deps, request.actor, AuditAction.REVOKE_SESSION_TOKEN, AuditOutcome.SUCCEEDED, "session token revoked"));
|
|
826
979
|
}
|
|
827
|
-
async
|
|
980
|
+
async ownerListCapabilityStates(command) {
|
|
828
981
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
829
982
|
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
830
983
|
}
|
|
831
|
-
return this._deps.
|
|
984
|
+
return (await this._deps.capabilityStates.list(command.vaultId, command.agentId))
|
|
985
|
+
.filter((state) => !command.status || state.status === command.status);
|
|
832
986
|
}
|
|
833
|
-
async
|
|
834
|
-
|
|
835
|
-
throw new VaultCoreError("read vault mismatch", "VAULT_READ_DENIED");
|
|
836
|
-
}
|
|
837
|
-
return this._deps.pendingCapabilityRequests.list(command.vaultId);
|
|
987
|
+
async ownerExecuteCapabilityStateOnce(command) {
|
|
988
|
+
return this._executePendingCapabilityState(command, "once");
|
|
838
989
|
}
|
|
839
|
-
async
|
|
840
|
-
|
|
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;
|
|
990
|
+
async ownerExecuteCapabilityStateAndGrant(command) {
|
|
991
|
+
return this._executePendingCapabilityState(command, "grant");
|
|
869
992
|
}
|
|
870
|
-
async
|
|
993
|
+
async ownerRejectCapabilityState(command) {
|
|
871
994
|
if (command.vaultId.value !== this._deps.vaultId.value) {
|
|
872
995
|
throw new VaultCoreError("write vault mismatch", "VAULT_WRITE_DENIED");
|
|
873
996
|
}
|
|
874
|
-
const pending = await this._deps.
|
|
875
|
-
if (!pending) {
|
|
876
|
-
throw new VaultCoreError("pending capability
|
|
997
|
+
const pending = await this._deps.capabilityStates.getByRequestId(command.vaultId, command.requestId);
|
|
998
|
+
if (!pending || pending.status !== "PENDING") {
|
|
999
|
+
throw new VaultCoreError("pending capability state not found", "VAULT_REQUEST_NOT_FOUND");
|
|
877
1000
|
}
|
|
878
|
-
|
|
1001
|
+
const rejectedState = {
|
|
1002
|
+
...pending,
|
|
1003
|
+
status: "REJECTED",
|
|
1004
|
+
decidedAt: this._deps.clock.nowIso(),
|
|
1005
|
+
};
|
|
1006
|
+
await this._deps.capabilityStates.upsert(rejectedState);
|
|
879
1007
|
await this._appendAudit(toAuditEntry(this._deps, command.owner, AuditAction.REJECT_CAPABILITY_REQUEST, AuditOutcome.SUCCEEDED, `rejected capability request ${command.requestId}`, {
|
|
880
1008
|
requestId: command.requestId,
|
|
881
1009
|
agentId: pending.agentId,
|
|
882
|
-
operation: pending.
|
|
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,
|
|
1010
|
+
operation: pending.operation,
|
|
955
1011
|
}));
|
|
1012
|
+
return rejectedState;
|
|
956
1013
|
}
|
|
957
1014
|
}
|
|
958
1015
|
export function createVaultCore(deps) {
|