@kya-os/mcp-i 1.9.0 → 1.11.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/dist/227.js +1 -0
- package/dist/{263.js → 242.js} +1 -1
- package/dist/283.js +1 -0
- package/dist/317.js +1 -0
- package/dist/354.js +1 -0
- package/dist/361.js +1 -0
- package/dist/452.js +1 -0
- package/dist/533.js +1 -0
- package/dist/622.js +1 -0
- package/dist/65.js +1 -0
- package/dist/839.js +1 -0
- package/dist/843.js +1 -0
- package/dist/{644.js → 861.js} +1 -1
- package/dist/914.js +1 -0
- package/dist/95.js +1 -0
- package/dist/cli-adapter/index.js +1 -1
- package/dist/runtime/102.js +1 -0
- package/dist/runtime/adapter-express.js +3 -3
- package/dist/runtime/adapter-nextjs.js +5 -5
- package/dist/runtime/audit.d.ts +1 -1
- package/dist/runtime/debug.d.ts +1 -1
- package/dist/runtime/debug.js +3 -2
- package/dist/runtime/delegation-hooks.d.ts +1 -1
- package/dist/runtime/http.js +3 -3
- package/dist/runtime/identity.d.ts +4 -4
- package/dist/runtime/identity.js +4 -4
- package/dist/runtime/index.d.ts +6 -2
- package/dist/runtime/index.js +10 -2
- package/dist/runtime/mcpi-runtime.d.ts +9 -1
- package/dist/runtime/mcpi-runtime.js +20 -3
- package/dist/runtime/outbound-delegation.d.ts +5 -3
- package/dist/runtime/outbound-delegation.js +14 -8
- package/dist/runtime/outbound-identity-bridge.d.ts +40 -0
- package/dist/runtime/outbound-identity-bridge.js +114 -0
- package/dist/runtime/proof-batch-queue.d.ts +1 -1
- package/dist/runtime/request-context.d.ts +10 -0
- package/dist/runtime/request-context.js +4 -0
- package/dist/runtime/resume-token-store-kv.d.ts +44 -0
- package/dist/runtime/resume-token-store-kv.js +93 -0
- package/dist/runtime/resume-token-store.d.ts +30 -0
- package/dist/runtime/resume-token-store.js +46 -0
- package/dist/runtime/session.js +6 -0
- package/dist/runtime/stdio.js +3 -3
- package/dist/runtime/utils/tools.d.ts +1 -1
- package/dist/runtime/utils/tools.js +29 -7
- package/dist/runtime/well-known.d.ts +12 -1
- package/dist/runtime/well-known.js +13 -2
- package/package.json +7 -5
- package/dist/114.js +0 -1
- package/dist/139.js +0 -1
- package/dist/200.js +0 -1
- package/dist/202.js +0 -1
- package/dist/238.js +0 -1
- package/dist/294.js +0 -1
- package/dist/374.js +0 -1
- package/dist/529.js +0 -1
- package/dist/627.js +0 -1
- package/dist/669.js +0 -1
- package/dist/857.js +0 -1
- package/dist/997.js +0 -1
- package/dist/runtime/verifier-middleware.d.ts +0 -99
- package/dist/runtime/verifier-middleware.js +0 -416
|
@@ -113,16 +113,16 @@ export declare class IdentityManager {
|
|
|
113
113
|
export declare const defaultIdentityManager: IdentityManager;
|
|
114
114
|
/**
|
|
115
115
|
* Extract agent ID from DID
|
|
116
|
-
* @deprecated Use extractAgentId from @kya-os/mcp-i-
|
|
116
|
+
* @deprecated Use extractAgentId from @kya-os/mcp-i-runtime/utils/did-helpers instead
|
|
117
117
|
* This re-export is maintained for backward compatibility
|
|
118
118
|
*/
|
|
119
|
-
export { extractAgentId } from '@kya-os/mcp-i-
|
|
119
|
+
export { extractAgentId } from '@kya-os/mcp-i-runtime/utils/did-helpers';
|
|
120
120
|
/**
|
|
121
121
|
* Extract agent slug from DID
|
|
122
|
-
* @deprecated Use extractAgentSlug from @kya-os/mcp-i-
|
|
122
|
+
* @deprecated Use extractAgentSlug from @kya-os/mcp-i-runtime/utils/did-helpers instead
|
|
123
123
|
* This re-export is maintained for backward compatibility
|
|
124
124
|
*/
|
|
125
|
-
export { extractAgentSlug } from '@kya-os/mcp-i-
|
|
125
|
+
export { extractAgentSlug } from '@kya-os/mcp-i-runtime/utils/did-helpers';
|
|
126
126
|
/**
|
|
127
127
|
* Convenience function to ensure identity
|
|
128
128
|
*/
|
package/dist/runtime/identity.js
CHANGED
|
@@ -293,17 +293,17 @@ exports.IdentityManager = IdentityManager;
|
|
|
293
293
|
exports.defaultIdentityManager = new IdentityManager();
|
|
294
294
|
/**
|
|
295
295
|
* Extract agent ID from DID
|
|
296
|
-
* @deprecated Use extractAgentId from @kya-os/mcp-i-
|
|
296
|
+
* @deprecated Use extractAgentId from @kya-os/mcp-i-runtime/utils/did-helpers instead
|
|
297
297
|
* This re-export is maintained for backward compatibility
|
|
298
298
|
*/
|
|
299
|
-
var did_helpers_1 = require("@kya-os/mcp-i-
|
|
299
|
+
var did_helpers_1 = require("@kya-os/mcp-i-runtime/utils/did-helpers");
|
|
300
300
|
Object.defineProperty(exports, "extractAgentId", { enumerable: true, get: function () { return did_helpers_1.extractAgentId; } });
|
|
301
301
|
/**
|
|
302
302
|
* Extract agent slug from DID
|
|
303
|
-
* @deprecated Use extractAgentSlug from @kya-os/mcp-i-
|
|
303
|
+
* @deprecated Use extractAgentSlug from @kya-os/mcp-i-runtime/utils/did-helpers instead
|
|
304
304
|
* This re-export is maintained for backward compatibility
|
|
305
305
|
*/
|
|
306
|
-
var did_helpers_2 = require("@kya-os/mcp-i-
|
|
306
|
+
var did_helpers_2 = require("@kya-os/mcp-i-runtime/utils/did-helpers");
|
|
307
307
|
Object.defineProperty(exports, "extractAgentSlug", { enumerable: true, get: function () { return did_helpers_2.extractAgentSlug; } });
|
|
308
308
|
/**
|
|
309
309
|
* Convenience function to ensure identity
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -16,6 +16,9 @@ export { DebugManager, createDebugEndpoint, type DebugVerificationResult, type D
|
|
|
16
16
|
export { DemoManager, createDemoManager, DemoConsole, formatVerifyLink, type DemoConfig, } from "./demo";
|
|
17
17
|
export { type DelegationVerifier, type DelegationVerifierConfig, type VerifyDelegationResult, type VerifyDelegationOptions, createDelegationVerifier, checkScopes, validateDelegation, extractScopes, } from "./delegation-verifier";
|
|
18
18
|
export { type AuthHandshakeConfig, type VerifyOrHintsResult, type AgentReputation, type ResumeTokenStore, verifyOrHints, hasSensitiveScopes, MemoryResumeTokenStore, } from "./auth-handshake";
|
|
19
|
+
export { createResumeTokenStore, type ResumeTokenStoreConfig, } from "./resume-token-store";
|
|
20
|
+
export { CloudflareKVResumeTokenStore, type ResumeTokenKVNamespace, } from "./resume-token-store-kv";
|
|
21
|
+
export { ensureOutboundIdentityBridge } from "./outbound-identity-bridge";
|
|
19
22
|
export { type ProofDestination, type ProofBatchQueueConfig, ProofBatchQueue, KTAProofDestination, AgentShieldProofDestination, createProofBatchQueue, } from "./proof-batch-queue";
|
|
20
23
|
export { CloudflareKVDelegationVerifier, } from "./delegation-verifier-kv";
|
|
21
24
|
export { AgentShieldAPIDelegationVerifier, } from "./delegation-verifier-agentshield";
|
|
@@ -23,5 +26,6 @@ export { MemoryDelegationVerifier, } from "./delegation-verifier-memory";
|
|
|
23
26
|
export { type ToolProtectionConfig, type ToolProtectionMap, type ToolProtectionConfigSource, ToolProtectionResolver, createToolProtectionResolver, InlineToolProtectionSource, FileToolProtectionSource, AgentShieldToolProtectionSource, } from "./tool-protection";
|
|
24
27
|
export { toolProtectionRegistry, isToolProtected, getToolProtection, } from "./tool-protection-registry";
|
|
25
28
|
export type { HandshakeRequest, SessionContext, NonceCache, NonceCacheEntry, NonceCacheConfig, } from "@kya-os/contracts/handshake";
|
|
26
|
-
export type { ProofMeta, DetachedProof, CanonicalHashes, AuditRecord, } from "@kya-os/
|
|
27
|
-
export type { DelegationRecord, DelegationStatus,
|
|
29
|
+
export type { ProofMeta, DetachedProof, CanonicalHashes, AuditRecord, } from "@kya-os/mcp/types" with { "resolution-mode": "import" };
|
|
30
|
+
export type { DelegationRecord, DelegationStatus, DelegationConstraints, } from "@kya-os/mcp/types" with { "resolution-mode": "import" };
|
|
31
|
+
export type { DelegationChain } from "@kya-os/contracts/delegation";
|
package/dist/runtime/index.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* audit logging, and well-known endpoints.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
10
|
-
exports.getToolProtection = exports.isToolProtected = exports.toolProtectionRegistry = exports.AgentShieldToolProtectionSource = void 0;
|
|
9
|
+
exports.ToolProtectionResolver = exports.MemoryDelegationVerifier = exports.AgentShieldAPIDelegationVerifier = exports.CloudflareKVDelegationVerifier = exports.createProofBatchQueue = exports.AgentShieldProofDestination = exports.KTAProofDestination = exports.ProofBatchQueue = exports.ensureOutboundIdentityBridge = exports.CloudflareKVResumeTokenStore = exports.createResumeTokenStore = exports.MemoryResumeTokenStore = exports.hasSensitiveScopes = exports.verifyOrHints = exports.extractScopes = exports.validateDelegation = exports.checkScopes = exports.createDelegationVerifier = exports.formatVerifyLink = exports.DemoConsole = exports.createDemoManager = exports.DemoManager = exports.createDebugEndpoint = exports.DebugManager = exports.extractDIDFromPath = exports.validateAgentDocument = exports.validateDIDDocument = exports.createWellKnownHandler = exports.WellKnownManager = exports.validateAuditRecord = exports.parseAuditLine = exports.logKeyRotationAudit = exports.defaultAuditLogger = exports.AuditLogger = exports.extractCanonicalData = exports.createProofResponse = exports.ProofGenerator = exports.validateHandshakeFormat = exports.createHandshakeRequest = exports.defaultSessionManager = exports.SessionManager = exports.IDENTITY_ERRORS = exports.ensureIdentity = exports.defaultIdentityManager = exports.IdentityManager = exports.RUNTIME_ERRORS = exports.RuntimeFactory = exports.createMCPIRuntime = exports.MCPIRuntime = exports.MCPINodeRuntimeWrapper = void 0;
|
|
10
|
+
exports.getToolProtection = exports.isToolProtected = exports.toolProtectionRegistry = exports.AgentShieldToolProtectionSource = exports.FileToolProtectionSource = exports.InlineToolProtectionSource = exports.createToolProtectionResolver = void 0;
|
|
11
11
|
// Main runtime - Node.js implementation with providers
|
|
12
12
|
var mcpi_runtime_wrapper_1 = require("./mcpi-runtime-wrapper");
|
|
13
13
|
Object.defineProperty(exports, "MCPINodeRuntimeWrapper", { enumerable: true, get: function () { return mcpi_runtime_wrapper_1.MCPINodeRuntimeWrapper; } });
|
|
@@ -69,6 +69,14 @@ var auth_handshake_1 = require("./auth-handshake");
|
|
|
69
69
|
Object.defineProperty(exports, "verifyOrHints", { enumerable: true, get: function () { return auth_handshake_1.verifyOrHints; } });
|
|
70
70
|
Object.defineProperty(exports, "hasSensitiveScopes", { enumerable: true, get: function () { return auth_handshake_1.hasSensitiveScopes; } });
|
|
71
71
|
Object.defineProperty(exports, "MemoryResumeTokenStore", { enumerable: true, get: function () { return auth_handshake_1.MemoryResumeTokenStore; } });
|
|
72
|
+
// Durable resume-token store factory (pluggable persistence)
|
|
73
|
+
var resume_token_store_1 = require("./resume-token-store");
|
|
74
|
+
Object.defineProperty(exports, "createResumeTokenStore", { enumerable: true, get: function () { return resume_token_store_1.createResumeTokenStore; } });
|
|
75
|
+
var resume_token_store_kv_1 = require("./resume-token-store-kv");
|
|
76
|
+
Object.defineProperty(exports, "CloudflareKVResumeTokenStore", { enumerable: true, get: function () { return resume_token_store_kv_1.CloudflareKVResumeTokenStore; } });
|
|
77
|
+
// Outbound identity bridge (mcp-i → compute interceptor)
|
|
78
|
+
var outbound_identity_bridge_1 = require("./outbound-identity-bridge");
|
|
79
|
+
Object.defineProperty(exports, "ensureOutboundIdentityBridge", { enumerable: true, get: function () { return outbound_identity_bridge_1.ensureOutboundIdentityBridge; } });
|
|
72
80
|
// Proof batching (NEW - Phase 1)
|
|
73
81
|
var proof_batch_queue_1 = require("./proof-batch-queue");
|
|
74
82
|
Object.defineProperty(exports, "ProofBatchQueue", { enumerable: true, get: function () { return proof_batch_queue_1.ProofBatchQueue; } });
|
|
@@ -9,7 +9,8 @@ import { ToolRequest, ToolResponse } from "./proof";
|
|
|
9
9
|
import { WellKnownConfig } from "./well-known";
|
|
10
10
|
import { DemoManager } from "./demo";
|
|
11
11
|
import { DelegationVerifierConfig } from "./delegation-verifier";
|
|
12
|
-
import {
|
|
12
|
+
import { ResumeTokenStoreConfig } from "./resume-token-store";
|
|
13
|
+
import type { NeedsAuthorizationError } from "@kya-os/mcp/types" with { "resolution-mode": "import" };
|
|
13
14
|
import { ToolProtectionMap, ToolProtectionResolver } from "./tool-protection";
|
|
14
15
|
/**
|
|
15
16
|
* Runtime environment check
|
|
@@ -89,6 +90,13 @@ export interface MCPIRuntimeConfig {
|
|
|
89
90
|
minReputationScore?: number;
|
|
90
91
|
/** Resume token TTL in milliseconds */
|
|
91
92
|
resumeTokenTtl?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Resume-token store selection. Omit for the in-memory single-process
|
|
95
|
+
* dev default (tokens lost on restart, not shared across instances).
|
|
96
|
+
* Set `{ type: 'cloudflare-kv', kvNamespace }` for durable consent→reuse
|
|
97
|
+
* on multi-instance / serverless deployments.
|
|
98
|
+
*/
|
|
99
|
+
resumeTokenStore?: ResumeTokenStoreConfig;
|
|
92
100
|
/** Require authorization for unknown agents */
|
|
93
101
|
requireAuthForUnknown?: boolean;
|
|
94
102
|
};
|
|
@@ -17,7 +17,9 @@ const debug_1 = require("./debug");
|
|
|
17
17
|
const demo_1 = require("./demo");
|
|
18
18
|
const delegation_verifier_1 = require("./delegation-verifier");
|
|
19
19
|
const auth_handshake_1 = require("./auth-handshake");
|
|
20
|
+
const resume_token_store_1 = require("./resume-token-store");
|
|
20
21
|
const request_context_1 = require("./request-context");
|
|
22
|
+
const outbound_identity_bridge_1 = require("./outbound-identity-bridge");
|
|
21
23
|
const tool_protection_1 = require("./tool-protection");
|
|
22
24
|
const tool_protection_registry_1 = require("./tool-protection-registry");
|
|
23
25
|
const mcp_i_core_1 = require("@kya-os/mcp-i-core");
|
|
@@ -51,9 +53,16 @@ class MCPIRuntime {
|
|
|
51
53
|
this.sessionManager = new session_1.SessionManager(config.session);
|
|
52
54
|
// Initialize audit logger
|
|
53
55
|
this.auditLogger = new audit_1.AuditLogger(config.audit);
|
|
54
|
-
// Initialize resume token store (NEW - Phase 1)
|
|
55
|
-
//
|
|
56
|
-
|
|
56
|
+
// Initialize resume token store (NEW - Phase 1).
|
|
57
|
+
// Durable stores (e.g. Cloudflare KV) are selected from config so a token
|
|
58
|
+
// minted at consent time survives cold starts / restarts / multi-instance
|
|
59
|
+
// — otherwise the user is re-prompted. Memory is the EXPLICIT single-process
|
|
60
|
+
// dev default. Mirrors the delegation-verifier factory pattern.
|
|
61
|
+
const resumeTtl = config.delegation?.authorization?.resumeTokenTtl || 600_000;
|
|
62
|
+
const resumeStoreConfig = config.delegation?.authorization?.resumeTokenStore;
|
|
63
|
+
this.resumeTokenStore = resumeStoreConfig
|
|
64
|
+
? (0, resume_token_store_1.createResumeTokenStore)({ ttlMs: resumeTtl, ...resumeStoreConfig })
|
|
65
|
+
: new auth_handshake_1.MemoryResumeTokenStore(resumeTtl);
|
|
57
66
|
// Initialize delegation verifier (NEW - Phase 1)
|
|
58
67
|
if (config.delegation?.enabled && config.delegation?.verifier) {
|
|
59
68
|
this.delegationVerifier = (0, delegation_verifier_1.createDelegationVerifier)(config.delegation.verifier);
|
|
@@ -99,6 +108,10 @@ class MCPIRuntime {
|
|
|
99
108
|
identityBadge: this.config.demo?.identityBadge || false,
|
|
100
109
|
environment: this.config.identity?.environment || "development",
|
|
101
110
|
});
|
|
111
|
+
// Bridge AsyncLocalStorage delegation/session context into the compute
|
|
112
|
+
// outbound-identity interceptor when it is preloaded in this process.
|
|
113
|
+
// Best-effort + idempotent — a no-op when compute is not present.
|
|
114
|
+
(0, outbound_identity_bridge_1.ensureOutboundIdentityBridge)();
|
|
102
115
|
console.error(`✅ XMCP-I Runtime initialized`);
|
|
103
116
|
console.error(` DID: ${this.cachedIdentity.did}`);
|
|
104
117
|
console.error(` Key ID: ${this.cachedIdentity.kid}`);
|
|
@@ -207,6 +220,10 @@ class MCPIRuntime {
|
|
|
207
220
|
delegationId: options.delegationRef,
|
|
208
221
|
delegationChain: options.delegationChain,
|
|
209
222
|
delegationScopes: options.delegationScopes,
|
|
223
|
+
// JWS-compact DelegationCredential VC from the verified result —
|
|
224
|
+
// emitted on KYA-OS-Delegation-Credential when the verifier
|
|
225
|
+
// returned a presentable credential.
|
|
226
|
+
delegationCredential: verifyResult.credential?.credential_jwt,
|
|
210
227
|
identity,
|
|
211
228
|
});
|
|
212
229
|
}
|
|
@@ -5,15 +5,17 @@
|
|
|
5
5
|
* HTTP requests made during delegated tool handler execution.
|
|
6
6
|
*
|
|
7
7
|
* Reads delegation context from AsyncLocalStorage (via getContext()),
|
|
8
|
-
* builds a signed Ed25519 JWT, and injects
|
|
9
|
-
*
|
|
8
|
+
* builds a signed Ed25519 JWT, and injects the canonical KYA-OS-* Layer 2
|
|
9
|
+
* delegation headers:
|
|
10
|
+
* KYA-OS-Delegation-Chain, KYA-OS-Delegation-Proof, KYA-OS-Granted-Scopes,
|
|
11
|
+
* and KYA-OS-Delegation-Credential when a DelegationCredential VC is present.
|
|
10
12
|
*
|
|
11
13
|
* Behavior:
|
|
12
14
|
* - Skips injection for internal hostnames (localhost, *.vouched.id, Fly IPs)
|
|
13
15
|
* - On signing failure: logs warning, continues WITHOUT delegation headers
|
|
14
16
|
* - Anonymous/non-delegated calls: no headers injected
|
|
15
17
|
*
|
|
16
|
-
* Related Spec: MCP-I §
|
|
18
|
+
* Related Spec: DIF MCP-I §8 — Outbound Delegation Propagation
|
|
17
19
|
*/
|
|
18
20
|
interface Logger {
|
|
19
21
|
debug: (msg: string, meta?: Record<string, unknown>) => void;
|
|
@@ -6,20 +6,23 @@
|
|
|
6
6
|
* HTTP requests made during delegated tool handler execution.
|
|
7
7
|
*
|
|
8
8
|
* Reads delegation context from AsyncLocalStorage (via getContext()),
|
|
9
|
-
* builds a signed Ed25519 JWT, and injects
|
|
10
|
-
*
|
|
9
|
+
* builds a signed Ed25519 JWT, and injects the canonical KYA-OS-* Layer 2
|
|
10
|
+
* delegation headers:
|
|
11
|
+
* KYA-OS-Delegation-Chain, KYA-OS-Delegation-Proof, KYA-OS-Granted-Scopes,
|
|
12
|
+
* and KYA-OS-Delegation-Credential when a DelegationCredential VC is present.
|
|
11
13
|
*
|
|
12
14
|
* Behavior:
|
|
13
15
|
* - Skips injection for internal hostnames (localhost, *.vouched.id, Fly IPs)
|
|
14
16
|
* - On signing failure: logs warning, continues WITHOUT delegation headers
|
|
15
17
|
* - Anonymous/non-delegated calls: no headers injected
|
|
16
18
|
*
|
|
17
|
-
* Related Spec: MCP-I §
|
|
19
|
+
* Related Spec: DIF MCP-I §8 — Outbound Delegation Propagation
|
|
18
20
|
*/
|
|
19
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
22
|
exports.isInternalDelegationTarget = isInternalDelegationTarget;
|
|
21
23
|
exports.installDelegationInterceptor = installDelegationInterceptor;
|
|
22
24
|
const mcp_i_core_1 = require("@kya-os/mcp-i-core");
|
|
25
|
+
// C2/C3: @kya-os/mcp-i is CJS; @kya-os/mcp is ESM-only (TS1479 blocks static import).
|
|
23
26
|
const request_context_js_1 = require("./request-context.js");
|
|
24
27
|
// Flag to avoid double-installing
|
|
25
28
|
const DELEGATION_INTERCEPTED_FLAG = "__kyaDelegationIntercepted";
|
|
@@ -85,7 +88,7 @@ function installDelegationInterceptor(logger) {
|
|
|
85
88
|
const baseHeaders = input instanceof Request ? input.headers : undefined;
|
|
86
89
|
const headers = new Headers(init?.headers ?? baseHeaders);
|
|
87
90
|
// Only inject if we have a delegation context and haven't already injected
|
|
88
|
-
if (!headers.has("
|
|
91
|
+
if (!headers.has("KYA-OS-Delegation-Proof")) {
|
|
89
92
|
const ctx = (0, request_context_js_1.getContext)();
|
|
90
93
|
if (ctx?.delegationRef &&
|
|
91
94
|
ctx.delegationPrivateKeyJwk &&
|
|
@@ -108,10 +111,13 @@ function installDelegationInterceptor(logger) {
|
|
|
108
111
|
kid: ctx.delegationAgentKid,
|
|
109
112
|
targetHostname,
|
|
110
113
|
});
|
|
111
|
-
headers.set("
|
|
112
|
-
headers.set("
|
|
113
|
-
headers.set("
|
|
114
|
-
|
|
114
|
+
headers.set("KYA-OS-Delegation-Chain", ctx.delegationChain ?? ctx.delegationRef);
|
|
115
|
+
headers.set("KYA-OS-Delegation-Proof", proof);
|
|
116
|
+
headers.set("KYA-OS-Granted-Scopes", (ctx.delegationScopes ?? []).join(","));
|
|
117
|
+
// Emit the JWS-compact DelegationCredential VC when the context carries one.
|
|
118
|
+
if (ctx.delegationCredential) {
|
|
119
|
+
headers.set("KYA-OS-Delegation-Credential", ctx.delegationCredential);
|
|
120
|
+
}
|
|
115
121
|
logger?.debug("Delegation headers injected", {
|
|
116
122
|
delegationRef: ctx.delegationRef,
|
|
117
123
|
targetHostname,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound Identity Bridge (mcp-i → @kya-os/compute interceptor)
|
|
3
|
+
*
|
|
4
|
+
* Bridges this package's AsyncLocalStorage request context into the compute
|
|
5
|
+
* outbound-identity interceptor so the Layer-2 delegation headers and the
|
|
6
|
+
* `KYA-OS-Session-Id` anchor are emitted on the compute (OpenClaw sidecar)
|
|
7
|
+
* path — resolving the long-standing `interceptor.ts:183-188` TODO.
|
|
8
|
+
*
|
|
9
|
+
* Why a global rendezvous (not a direct import): neither package depends on
|
|
10
|
+
* the other (compute must not import the SDK; the SDK must not pull in the
|
|
11
|
+
* Fly.io sidecar). When the compute interceptor installs it publishes a
|
|
12
|
+
* registrar on `Symbol.for("kya-os.outbound-identity.registry")`; this module
|
|
13
|
+
* reads that registrar and registers providers backed by `getContext()`. When
|
|
14
|
+
* compute is absent (pure-Node / Cloudflare deployments) the registrar is
|
|
15
|
+
* missing and this is a clean no-op — those paths inject delegation headers via
|
|
16
|
+
* their own fetch wrap (`outbound-delegation.ts`).
|
|
17
|
+
*
|
|
18
|
+
* Authority model follows ADR-001: the DelegationCredential VC is
|
|
19
|
+
* authoritative; `-Chain` / `-Granted-Scopes` are advisory hints.
|
|
20
|
+
*
|
|
21
|
+
* Related Spec: DIF MCP-I §8 — Outbound Delegation Propagation
|
|
22
|
+
*/
|
|
23
|
+
interface Logger {
|
|
24
|
+
debug: (msg: string, meta?: Record<string, unknown>) => void;
|
|
25
|
+
warn: (msg: string, meta?: Record<string, unknown>) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Register the AsyncLocalStorage-backed delegation / session providers with the
|
|
29
|
+
* compute outbound-identity interceptor, if it is installed in this process.
|
|
30
|
+
*
|
|
31
|
+
* Idempotent and best-effort: safe to call from multiple init sites. Returns
|
|
32
|
+
* true when the registrar was found and providers were wired, false otherwise.
|
|
33
|
+
*/
|
|
34
|
+
export declare function ensureOutboundIdentityBridge(logger?: Logger): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Test-only reset of the idempotency guard. Not part of the public runtime API.
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
export declare function __resetOutboundIdentityBridgeForTests(): void;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Outbound Identity Bridge (mcp-i → @kya-os/compute interceptor)
|
|
4
|
+
*
|
|
5
|
+
* Bridges this package's AsyncLocalStorage request context into the compute
|
|
6
|
+
* outbound-identity interceptor so the Layer-2 delegation headers and the
|
|
7
|
+
* `KYA-OS-Session-Id` anchor are emitted on the compute (OpenClaw sidecar)
|
|
8
|
+
* path — resolving the long-standing `interceptor.ts:183-188` TODO.
|
|
9
|
+
*
|
|
10
|
+
* Why a global rendezvous (not a direct import): neither package depends on
|
|
11
|
+
* the other (compute must not import the SDK; the SDK must not pull in the
|
|
12
|
+
* Fly.io sidecar). When the compute interceptor installs it publishes a
|
|
13
|
+
* registrar on `Symbol.for("kya-os.outbound-identity.registry")`; this module
|
|
14
|
+
* reads that registrar and registers providers backed by `getContext()`. When
|
|
15
|
+
* compute is absent (pure-Node / Cloudflare deployments) the registrar is
|
|
16
|
+
* missing and this is a clean no-op — those paths inject delegation headers via
|
|
17
|
+
* their own fetch wrap (`outbound-delegation.ts`).
|
|
18
|
+
*
|
|
19
|
+
* Authority model follows ADR-001: the DelegationCredential VC is
|
|
20
|
+
* authoritative; `-Chain` / `-Granted-Scopes` are advisory hints.
|
|
21
|
+
*
|
|
22
|
+
* Related Spec: DIF MCP-I §8 — Outbound Delegation Propagation
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.ensureOutboundIdentityBridge = ensureOutboundIdentityBridge;
|
|
26
|
+
exports.__resetOutboundIdentityBridgeForTests = __resetOutboundIdentityBridgeForTests;
|
|
27
|
+
const mcp_i_core_1 = require("@kya-os/mcp-i-core");
|
|
28
|
+
const request_context_1 = require("./request-context");
|
|
29
|
+
/**
|
|
30
|
+
* Must match `OUTBOUND_IDENTITY_REGISTRY_KEY` in
|
|
31
|
+
* `@kya-os/compute`'s `outbound-identity/interceptor.ts`. `Symbol.for` resolves
|
|
32
|
+
* the same registry symbol in both packages within one process.
|
|
33
|
+
*/
|
|
34
|
+
const OUTBOUND_IDENTITY_REGISTRY_KEY = Symbol.for("kya-os.outbound-identity.registry");
|
|
35
|
+
// Idempotency guard — the registered providers are closures over getContext(),
|
|
36
|
+
// so a single registration covers all subsequent requests in this process.
|
|
37
|
+
let bridged = false;
|
|
38
|
+
function readRegistry() {
|
|
39
|
+
// REASON: globalThis has no index signature for our Symbol key; one localized
|
|
40
|
+
// cast reads the typed cross-package slot compute published at install.
|
|
41
|
+
return globalThis[OUTBOUND_IDENTITY_REGISTRY_KEY];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build a compute-compatible delegation provider from the request context.
|
|
45
|
+
* Returns undefined per-call when the active context carries no delegation (or
|
|
46
|
+
* lacks the signing material), so anonymous / non-delegated calls inject no
|
|
47
|
+
* delegation headers.
|
|
48
|
+
*/
|
|
49
|
+
function delegationProvider() {
|
|
50
|
+
const ctx = (0, request_context_1.getContext)();
|
|
51
|
+
if (!ctx?.delegationRef ||
|
|
52
|
+
!ctx.delegationPrivateKeyJwk ||
|
|
53
|
+
!ctx.delegationAgentKid) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const delegationChain = ctx.delegationChain ?? ctx.delegationRef;
|
|
57
|
+
const delegationScopes = ctx.delegationScopes ?? [];
|
|
58
|
+
const agentDid = ctx.session?.serverDid ?? "";
|
|
59
|
+
const userDid = ctx.session?.userDid ?? "";
|
|
60
|
+
const privateKeyJwk = ctx.delegationPrivateKeyJwk;
|
|
61
|
+
const kid = ctx.delegationAgentKid;
|
|
62
|
+
const delegationRef = ctx.delegationRef;
|
|
63
|
+
return {
|
|
64
|
+
delegationRef,
|
|
65
|
+
delegationChain,
|
|
66
|
+
delegationScopes,
|
|
67
|
+
agentDid,
|
|
68
|
+
userDid,
|
|
69
|
+
kid,
|
|
70
|
+
delegationCredential: ctx.delegationCredential,
|
|
71
|
+
buildProof: (targetHostname) => (0, mcp_i_core_1.buildDelegationProofJWT)({
|
|
72
|
+
agentDid,
|
|
73
|
+
userDid,
|
|
74
|
+
delegationId: delegationRef,
|
|
75
|
+
delegationChain,
|
|
76
|
+
scopes: delegationScopes,
|
|
77
|
+
privateKeyJwk,
|
|
78
|
+
kid,
|
|
79
|
+
targetHostname,
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Resolve the stable MCP session id from the active request context. */
|
|
84
|
+
function sessionIdProvider() {
|
|
85
|
+
return (0, request_context_1.getContext)()?.session?.sessionId;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Register the AsyncLocalStorage-backed delegation / session providers with the
|
|
89
|
+
* compute outbound-identity interceptor, if it is installed in this process.
|
|
90
|
+
*
|
|
91
|
+
* Idempotent and best-effort: safe to call from multiple init sites. Returns
|
|
92
|
+
* true when the registrar was found and providers were wired, false otherwise.
|
|
93
|
+
*/
|
|
94
|
+
function ensureOutboundIdentityBridge(logger) {
|
|
95
|
+
if (bridged)
|
|
96
|
+
return true;
|
|
97
|
+
const registry = readRegistry();
|
|
98
|
+
if (!registry?.setDelegationProvider) {
|
|
99
|
+
logger?.debug("Outbound identity bridge: compute interceptor not present — skipping");
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
registry.setDelegationProvider(delegationProvider);
|
|
103
|
+
registry.setSessionIdProvider?.(sessionIdProvider);
|
|
104
|
+
bridged = true;
|
|
105
|
+
logger?.debug("Outbound identity bridge: delegation + session providers registered");
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Test-only reset of the idempotency guard. Not part of the public runtime API.
|
|
110
|
+
* @internal
|
|
111
|
+
*/
|
|
112
|
+
function __resetOutboundIdentityBridgeForTests() {
|
|
113
|
+
bridged = false;
|
|
114
|
+
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* Related: PHASE_1_XMCP_I_SERVER.md Epic 3 (Proof Batching)
|
|
18
18
|
*/
|
|
19
|
-
import { DetachedProof } from "@kya-os/
|
|
19
|
+
import type { DetachedProof } from "@kya-os/mcp/types" with { "resolution-mode": "import" };
|
|
20
20
|
/**
|
|
21
21
|
* Proof submission destination
|
|
22
22
|
*/
|
|
@@ -19,6 +19,8 @@ export interface RequestContext {
|
|
|
19
19
|
delegationChain?: string;
|
|
20
20
|
/** Scopes granted by the active delegation */
|
|
21
21
|
delegationScopes?: string[];
|
|
22
|
+
/** JWS-compact DelegationCredential VC for the active delegation (emitted on KYA-OS-Delegation-Credential when present) */
|
|
23
|
+
delegationCredential?: string;
|
|
22
24
|
/** Agent Ed25519 private key JWK for delegation proof signing */
|
|
23
25
|
delegationPrivateKeyJwk?: Ed25519PrivateJWK;
|
|
24
26
|
/** Key ID used in delegation proof JWT header */
|
|
@@ -56,6 +58,7 @@ export declare function setDelegationContext(fields: {
|
|
|
56
58
|
delegationRef: string;
|
|
57
59
|
delegationChain: string;
|
|
58
60
|
delegationScopes: string[];
|
|
61
|
+
delegationCredential?: string;
|
|
59
62
|
delegationPrivateKeyJwk?: Ed25519PrivateJWK;
|
|
60
63
|
delegationAgentKid?: string;
|
|
61
64
|
}): void;
|
|
@@ -70,6 +73,13 @@ export declare function setDelegationContextFromIdentity(params: {
|
|
|
70
73
|
delegationId: string;
|
|
71
74
|
delegationChain: string;
|
|
72
75
|
delegationScopes: string[];
|
|
76
|
+
/**
|
|
77
|
+
* JWS-compact DelegationCredential VC for the active delegation. Threaded
|
|
78
|
+
* through to KYA-OS-Delegation-Credential on outbound requests. Sourced
|
|
79
|
+
* from the verified delegation result (e.g. credential.credential_jwt).
|
|
80
|
+
* Omit when the verifier did not return a presentable VC.
|
|
81
|
+
*/
|
|
82
|
+
delegationCredential?: string;
|
|
73
83
|
identity: {
|
|
74
84
|
privateKey: string;
|
|
75
85
|
publicKey: string;
|
|
@@ -71,6 +71,9 @@ function setDelegationContext(fields) {
|
|
|
71
71
|
context.delegationRef = fields.delegationRef;
|
|
72
72
|
context.delegationChain = fields.delegationChain;
|
|
73
73
|
context.delegationScopes = fields.delegationScopes;
|
|
74
|
+
if (fields.delegationCredential !== undefined) {
|
|
75
|
+
context.delegationCredential = fields.delegationCredential;
|
|
76
|
+
}
|
|
74
77
|
if (fields.delegationPrivateKeyJwk !== undefined) {
|
|
75
78
|
context.delegationPrivateKeyJwk = fields.delegationPrivateKeyJwk;
|
|
76
79
|
}
|
|
@@ -90,6 +93,7 @@ function setDelegationContextFromIdentity(params) {
|
|
|
90
93
|
delegationRef: params.delegationId,
|
|
91
94
|
delegationChain: params.delegationChain,
|
|
92
95
|
delegationScopes: params.delegationScopes,
|
|
96
|
+
delegationCredential: params.delegationCredential,
|
|
93
97
|
delegationPrivateKeyJwk: params.identity?.privateKey
|
|
94
98
|
? {
|
|
95
99
|
kty: "OKP",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare KV Resume Token Store
|
|
3
|
+
*
|
|
4
|
+
* Durable resume-token store backed by a Cloudflare Workers KV namespace, so a
|
|
5
|
+
* resume token minted at consent time survives cold starts, process restarts,
|
|
6
|
+
* and multi-instance deployments (the failure mode the in-memory store causes —
|
|
7
|
+
* the user gets re-prompted because the token vanished). Mirrors the
|
|
8
|
+
* delegation-verifier-kv.ts adapter shape.
|
|
9
|
+
*
|
|
10
|
+
* Key structure:
|
|
11
|
+
* - `resume:{token}` — JSON-encoded {@link StoredResumeToken}
|
|
12
|
+
*
|
|
13
|
+
* Tokens are minted with CSPRNG entropy (see generateResumeToken in
|
|
14
|
+
* @kya-os/mcp-i-core), never time+Math.random.
|
|
15
|
+
*/
|
|
16
|
+
import { type ResumeTokenStore } from "@kya-os/mcp-i-core";
|
|
17
|
+
/**
|
|
18
|
+
* Minimal Cloudflare KV surface needed by the store. `put` accepts the standard
|
|
19
|
+
* KV `{ expirationTtl }` (seconds) so the namespace also evicts expired tokens
|
|
20
|
+
* natively; logical expiry via `expiresAt` is enforced regardless.
|
|
21
|
+
*/
|
|
22
|
+
export interface ResumeTokenKVNamespace {
|
|
23
|
+
get(key: string): Promise<string | null>;
|
|
24
|
+
put(key: string, value: string, options?: {
|
|
25
|
+
expirationTtl?: number;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
delete(key: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export declare class CloudflareKVResumeTokenStore implements ResumeTokenStore {
|
|
30
|
+
private kv;
|
|
31
|
+
private ttlMs;
|
|
32
|
+
constructor(kv: ResumeTokenKVNamespace, ttlMs?: number);
|
|
33
|
+
private key;
|
|
34
|
+
private read;
|
|
35
|
+
create(agentDid: string, scopes: string[], metadata?: Record<string, unknown>): Promise<string>;
|
|
36
|
+
get(token: string): Promise<{
|
|
37
|
+
agentDid: string;
|
|
38
|
+
scopes: string[];
|
|
39
|
+
createdAt: number;
|
|
40
|
+
expiresAt: number;
|
|
41
|
+
metadata?: Record<string, unknown>;
|
|
42
|
+
} | null>;
|
|
43
|
+
fulfill(token: string): Promise<void>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cloudflare KV Resume Token Store
|
|
4
|
+
*
|
|
5
|
+
* Durable resume-token store backed by a Cloudflare Workers KV namespace, so a
|
|
6
|
+
* resume token minted at consent time survives cold starts, process restarts,
|
|
7
|
+
* and multi-instance deployments (the failure mode the in-memory store causes —
|
|
8
|
+
* the user gets re-prompted because the token vanished). Mirrors the
|
|
9
|
+
* delegation-verifier-kv.ts adapter shape.
|
|
10
|
+
*
|
|
11
|
+
* Key structure:
|
|
12
|
+
* - `resume:{token}` — JSON-encoded {@link StoredResumeToken}
|
|
13
|
+
*
|
|
14
|
+
* Tokens are minted with CSPRNG entropy (see generateResumeToken in
|
|
15
|
+
* @kya-os/mcp-i-core), never time+Math.random.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.CloudflareKVResumeTokenStore = void 0;
|
|
19
|
+
const mcp_i_core_1 = require("@kya-os/mcp-i-core");
|
|
20
|
+
// Cloudflare KV requires expirationTtl >= 60 seconds.
|
|
21
|
+
const MIN_KV_TTL_SECONDS = 60;
|
|
22
|
+
function toTtlSeconds(ms) {
|
|
23
|
+
return Math.max(MIN_KV_TTL_SECONDS, Math.ceil(ms / 1000));
|
|
24
|
+
}
|
|
25
|
+
class CloudflareKVResumeTokenStore {
|
|
26
|
+
kv;
|
|
27
|
+
ttlMs;
|
|
28
|
+
constructor(kv, ttlMs = 600_000) {
|
|
29
|
+
this.kv = kv;
|
|
30
|
+
this.ttlMs = ttlMs;
|
|
31
|
+
}
|
|
32
|
+
key(token) {
|
|
33
|
+
return `resume:${token}`;
|
|
34
|
+
}
|
|
35
|
+
async read(token) {
|
|
36
|
+
const raw = await this.kv.get(this.key(token));
|
|
37
|
+
if (!raw)
|
|
38
|
+
return null;
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(raw);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async create(agentDid, scopes, metadata) {
|
|
47
|
+
const token = (0, mcp_i_core_1.generateResumeToken)("rt");
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
const record = {
|
|
50
|
+
agentDid,
|
|
51
|
+
scopes,
|
|
52
|
+
createdAt: now,
|
|
53
|
+
expiresAt: now + this.ttlMs,
|
|
54
|
+
metadata,
|
|
55
|
+
fulfilled: false,
|
|
56
|
+
};
|
|
57
|
+
await this.kv.put(this.key(token), JSON.stringify(record), {
|
|
58
|
+
expirationTtl: toTtlSeconds(this.ttlMs),
|
|
59
|
+
});
|
|
60
|
+
return token;
|
|
61
|
+
}
|
|
62
|
+
async get(token) {
|
|
63
|
+
const record = await this.read(token);
|
|
64
|
+
if (!record)
|
|
65
|
+
return null;
|
|
66
|
+
if (Date.now() > record.expiresAt) {
|
|
67
|
+
await this.kv.delete(this.key(token));
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (record.fulfilled)
|
|
71
|
+
return null;
|
|
72
|
+
return {
|
|
73
|
+
agentDid: record.agentDid,
|
|
74
|
+
scopes: record.scopes,
|
|
75
|
+
createdAt: record.createdAt,
|
|
76
|
+
expiresAt: record.expiresAt,
|
|
77
|
+
metadata: record.metadata,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async fulfill(token) {
|
|
81
|
+
const record = await this.read(token);
|
|
82
|
+
if (!record)
|
|
83
|
+
return;
|
|
84
|
+
record.fulfilled = true;
|
|
85
|
+
// Preserve the original expiry window so a fulfilled token can't outlive
|
|
86
|
+
// its TTL; floor at the KV minimum.
|
|
87
|
+
const remainingMs = Math.max(0, record.expiresAt - Date.now());
|
|
88
|
+
await this.kv.put(this.key(token), JSON.stringify(record), {
|
|
89
|
+
expirationTtl: toTtlSeconds(remainingMs),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.CloudflareKVResumeTokenStore = CloudflareKVResumeTokenStore;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resume Token Store Factory
|
|
3
|
+
*
|
|
4
|
+
* Selects a {@link ResumeTokenStore} implementation from config, mirroring the
|
|
5
|
+
* delegation-verifier factory (`createDelegationVerifier`). This replaces the
|
|
6
|
+
* hardcoded in-memory store in the runtime so consent→reuse can be made durable
|
|
7
|
+
* (Cloudflare KV) on multi-instance / serverless deployments, while keeping the
|
|
8
|
+
* in-memory store as an EXPLICIT single-process dev default.
|
|
9
|
+
*/
|
|
10
|
+
import { type ResumeTokenStore } from "@kya-os/mcp-i-core";
|
|
11
|
+
import { type ResumeTokenKVNamespace } from "./resume-token-store-kv";
|
|
12
|
+
/**
|
|
13
|
+
* Configuration for resume-token stores.
|
|
14
|
+
*
|
|
15
|
+
* - `memory` — in-memory TTL map. Single-process dev only; tokens are lost on
|
|
16
|
+
* restart and not shared across instances.
|
|
17
|
+
* - `cloudflare-kv` — durable, edge-replicated. Survives cold starts/restarts.
|
|
18
|
+
*/
|
|
19
|
+
export interface ResumeTokenStoreConfig {
|
|
20
|
+
type: "memory" | "cloudflare-kv";
|
|
21
|
+
/** Token TTL in milliseconds (default: 600_000 = 10 minutes). */
|
|
22
|
+
ttlMs?: number;
|
|
23
|
+
/** Cloudflare KV namespace (required for `cloudflare-kv`). */
|
|
24
|
+
kvNamespace?: ResumeTokenKVNamespace;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a resume-token store from config. Memory is an explicit opt-in for
|
|
28
|
+
* single-process development; durable stores must be selected deliberately.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createResumeTokenStore(config: ResumeTokenStoreConfig): ResumeTokenStore;
|