managed-deepagents 0.0.3-dev.25 → 0.0.3-dev.29

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 (65) hide show
  1. package/dist/identity.d.ts +273 -0
  2. package/dist/identity.d.ts.map +1 -0
  3. package/dist/identity.js +358 -0
  4. package/dist/identity.js.map +1 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/runtime/auth.d.ts +44 -0
  10. package/dist/runtime/auth.d.ts.map +1 -0
  11. package/dist/runtime/auth.js +168 -0
  12. package/dist/runtime/auth.js.map +1 -0
  13. package/dist/runtime/credentials.d.ts +50 -32
  14. package/dist/runtime/credentials.d.ts.map +1 -1
  15. package/dist/runtime/credentials.js +174 -57
  16. package/dist/runtime/credentials.js.map +1 -1
  17. package/dist/runtime/identity-http.d.ts +8 -0
  18. package/dist/runtime/identity-http.d.ts.map +1 -0
  19. package/dist/runtime/identity-http.js +23 -0
  20. package/dist/runtime/identity-http.js.map +1 -0
  21. package/dist/runtime/identity-runtime.d.ts +54 -0
  22. package/dist/runtime/identity-runtime.d.ts.map +1 -0
  23. package/dist/runtime/identity-runtime.js +151 -0
  24. package/dist/runtime/identity-runtime.js.map +1 -0
  25. package/dist/runtime/index.d.ts +11 -1
  26. package/dist/runtime/index.d.ts.map +1 -1
  27. package/dist/runtime/index.js +90 -69
  28. package/dist/runtime/index.js.map +1 -1
  29. package/dist/runtime/managed-middleware.d.ts +25 -0
  30. package/dist/runtime/managed-middleware.d.ts.map +1 -0
  31. package/dist/runtime/managed-middleware.js +76 -0
  32. package/dist/runtime/managed-middleware.js.map +1 -0
  33. package/dist/runtime/managed-tools.d.ts +61 -0
  34. package/dist/runtime/managed-tools.d.ts.map +1 -0
  35. package/dist/runtime/managed-tools.js +102 -0
  36. package/dist/runtime/managed-tools.js.map +1 -0
  37. package/dist/runtime/sandbox-manager.d.ts +3 -21
  38. package/dist/runtime/sandbox-manager.d.ts.map +1 -1
  39. package/dist/runtime/sandbox-manager.js +41 -125
  40. package/dist/runtime/sandbox-manager.js.map +1 -1
  41. package/dist/runtime/setup-script.d.ts +2 -4
  42. package/dist/runtime/setup-script.d.ts.map +1 -1
  43. package/dist/runtime/setup-script.js +2 -4
  44. package/dist/runtime/setup-script.js.map +1 -1
  45. package/dist/runtime/types.d.ts +21 -5
  46. package/dist/runtime/types.d.ts.map +1 -1
  47. package/dist/runtime/validated-token.d.ts +25 -0
  48. package/dist/runtime/validated-token.d.ts.map +1 -0
  49. package/dist/runtime/validated-token.js +260 -0
  50. package/dist/runtime/validated-token.js.map +1 -0
  51. package/dist/sandbox.d.ts +18 -18
  52. package/dist/sandbox.d.ts.map +1 -1
  53. package/dist/sandbox.js +37 -9
  54. package/dist/sandbox.js.map +1 -1
  55. package/dist/types.d.ts +9 -0
  56. package/dist/types.d.ts.map +1 -1
  57. package/package.json +12 -9
  58. package/dist/runtime/dev-notice.d.ts +0 -7
  59. package/dist/runtime/dev-notice.d.ts.map +0 -1
  60. package/dist/runtime/dev-notice.js +0 -27
  61. package/dist/runtime/dev-notice.js.map +0 -1
  62. package/dist/runtime/local-dev-sandbox.d.ts +0 -13
  63. package/dist/runtime/local-dev-sandbox.d.ts.map +0 -1
  64. package/dist/runtime/local-dev-sandbox.js +0 -39
  65. package/dist/runtime/local-dev-sandbox.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,OAAO,EACP,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,YAAY,GACb,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE5E,YAAY,EACV,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,OAAO,EACP,WAAW,EACX,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,OAAO,EACP,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAEpC,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,YAAY,GACb,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -2,4 +2,5 @@ export { defineDeepAgent } from "./define-deep-agent.js";
2
2
  export { defineMcpServers } from "./connectors.js";
3
3
  export { defineSchedule } from "./schedules.js";
4
4
  export { defineSandbox } from "./sandbox.js";
5
+ export { defineIdentity, providers, validateIdentity } from "./identity.js";
5
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { Auth } from "@langchain/langgraph-sdk/auth";
2
+ import type { IdentityConfig, IdentityDefinition } from "../identity.js";
3
+ /**
4
+ * The user object `@auth.authenticate` returns. `identity`/`permissions` satisfy
5
+ * the LangGraph base shape; the `mda_*` fields carry the identity envelope into
6
+ * `config.configurable.langgraph_auth_user` for the runtime seam.
7
+ */
8
+ export interface ManagedUser {
9
+ identity: string;
10
+ permissions: string[];
11
+ mda_actor_id: string;
12
+ mda_tenant_id?: string;
13
+ mda_actor_email?: string;
14
+ mda_claims?: Record<string, unknown>;
15
+ [key: string]: unknown;
16
+ }
17
+ /**
18
+ * Build the managed LangGraph custom-auth handler from a declared identity.
19
+ *
20
+ * Handles both ingress modes: `trusted_backend` (reserved headers behind a
21
+ * shared ingress secret) and `validated_token` (browser-direct IdP tokens
22
+ * verified via JWKS / introspection / MDA-signed guest). Also registers thread
23
+ * and store scoping with a studio bypass.
24
+ */
25
+ export declare function buildManagedAuth(identity: IdentityDefinition): Auth;
26
+ /**
27
+ * Fail-closed (403) unless every identity segment the memory scope requires is
28
+ * present in the store `namespace`. `agent`/`none` scopes carry no per-identity
29
+ * restriction (shared or absent memory), so any namespace is allowed.
30
+ */
31
+ export declare function assertStoreAccess(cfg: IdentityConfig, user: ManagedUser, value: unknown): void;
32
+ /**
33
+ * Resolve the caller identity for `trusted_backend`: validate the shared ingress
34
+ * secret, then trust the reserved actor/tenant headers. Fail-closed (401) on a
35
+ * missing/invalid secret, a missing actor, or a missing tenant under multi.
36
+ */
37
+ export declare function resolveTrustedBackendUser(cfg: IdentityConfig, request: Request): ManagedUser;
38
+ /** Derive the owner value `@auth.on.threads` filters on, per the `threads` axis. */
39
+ export declare function threadOwnerKey(cfg: IdentityConfig, user: ManagedUser): string;
40
+ /** True when this deployment exposes guest issuance and the request targets it. */
41
+ export declare function isGuestIssuanceRequest(request: Request, cfg: IdentityConfig): boolean;
42
+ /** Placeholder identity so LangGraph auth middleware allows tokenless issuance. */
43
+ export declare function guestIssuanceBypassUser(): ManagedUser;
44
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/runtime/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EAGL,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAuBzE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAsCnE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,OAAO,GACb,IAAI,CAuBN;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,OAAO,GACf,WAAW,CAgBb;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAY7E;AAuBD,mFAAmF;AACnF,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,cAAc,GAClB,OAAO,CAaT;AAED,mFAAmF;AACnF,wBAAgB,uBAAuB,IAAI,WAAW,CAMrD"}
@@ -0,0 +1,168 @@
1
+ import { Auth, HTTPException, isStudioUser, } from "@langchain/langgraph-sdk/auth";
2
+ import { memoryIdentitySegments } from "./identity-runtime.js";
3
+ import { resolveValidatedTokenUser } from "./validated-token.js";
4
+ /**
5
+ * Reserved transport for the trusted-backend ingress mode. Actor/tenant travel
6
+ * as reserved headers (mapped into `config.configurable` by the generated
7
+ * `langgraph.json` allowlist); the ingress secret is validated here and is
8
+ * deliberately **not** allowlisted, so it never reaches `config.configurable`.
9
+ */
10
+ const ACTOR_HEADER = "x-mda-actor-id";
11
+ const TENANT_HEADER = "x-mda-tenant-id";
12
+ const INGRESS_SECRET_HEADER = "x-mda-ingress-secret";
13
+ /** Deployment env var carrying the shared secret the trusted backend presents. */
14
+ const INGRESS_SECRET_ENV = "MDA_INGRESS_SECRET";
15
+ /** Path MDA exposes for anonymous guest token issuance (must stay public). */
16
+ const GUEST_ISSUANCE_PATH = "/identity/guest";
17
+ /** Synthetic actor used only to satisfy auth middleware on the issuance route. */
18
+ const GUEST_ISSUER_ACTOR = "mda:guest-issuer";
19
+ /**
20
+ * Build the managed LangGraph custom-auth handler from a declared identity.
21
+ *
22
+ * Handles both ingress modes: `trusted_backend` (reserved headers behind a
23
+ * shared ingress secret) and `validated_token` (browser-direct IdP tokens
24
+ * verified via JWKS / introspection / MDA-signed guest). Also registers thread
25
+ * and store scoping with a studio bypass.
26
+ */
27
+ export function buildManagedAuth(identity) {
28
+ const cfg = identity.config;
29
+ const http = cfg.ingress.http;
30
+ const auth = new Auth();
31
+ auth.authenticate(async (request) => {
32
+ if (isGuestIssuanceRequest(request, cfg)) {
33
+ return guestIssuanceBypassUser();
34
+ }
35
+ if (http === "trusted_backend") {
36
+ return resolveTrustedBackendUser(cfg, request);
37
+ }
38
+ return resolveValidatedTokenUser(cfg, http, request);
39
+ });
40
+ auth.on("threads", ({ user, value }) => {
41
+ if (isStudioUser(user)) {
42
+ return true;
43
+ }
44
+ const key = threadOwnerKey(cfg, user);
45
+ if (value && typeof value === "object") {
46
+ const container = value;
47
+ container.metadata = { ...(container.metadata ?? {}), owner: key };
48
+ }
49
+ return { owner: key };
50
+ });
51
+ // Mirror the memory namespace so raw store ops can't cross the identity
52
+ // boundary a run is scoped to (§5, §6.2).
53
+ auth.on("store", ({ user, value }) => {
54
+ if (isStudioUser(user)) {
55
+ return true;
56
+ }
57
+ assertStoreAccess(cfg, user, value);
58
+ return true;
59
+ });
60
+ return auth;
61
+ }
62
+ /**
63
+ * Fail-closed (403) unless every identity segment the memory scope requires is
64
+ * present in the store `namespace`. `agent`/`none` scopes carry no per-identity
65
+ * restriction (shared or absent memory), so any namespace is allowed.
66
+ */
67
+ export function assertStoreAccess(cfg, user, value) {
68
+ const required = memoryIdentitySegments(cfg.scoping.memory, cfg.tenancy, user.mda_actor_id ? String(user.mda_actor_id) : undefined, user.mda_tenant_id ? String(user.mda_tenant_id) : undefined);
69
+ if (required.length === 0) {
70
+ return;
71
+ }
72
+ const namespace = value &&
73
+ typeof value === "object" &&
74
+ Array.isArray(value.namespace)
75
+ ? value.namespace.map(String)
76
+ : [];
77
+ for (const segment of required) {
78
+ if (!namespace.includes(segment)) {
79
+ throw new HTTPException(403, {
80
+ message: "store namespace is outside the caller's identity boundary",
81
+ });
82
+ }
83
+ }
84
+ }
85
+ /**
86
+ * Resolve the caller identity for `trusted_backend`: validate the shared ingress
87
+ * secret, then trust the reserved actor/tenant headers. Fail-closed (401) on a
88
+ * missing/invalid secret, a missing actor, or a missing tenant under multi.
89
+ */
90
+ export function resolveTrustedBackendUser(cfg, request) {
91
+ requireIngressSecret(request);
92
+ const actor = request.headers.get(ACTOR_HEADER);
93
+ if (!actor) {
94
+ throw new HTTPException(401, { message: `missing ${ACTOR_HEADER}` });
95
+ }
96
+ const tenant = request.headers.get(TENANT_HEADER) ?? undefined;
97
+ assertTenantPresentIfMulti(cfg, tenant);
98
+ return {
99
+ identity: actor,
100
+ permissions: [],
101
+ mda_actor_id: actor,
102
+ ...(tenant ? { mda_tenant_id: tenant } : {}),
103
+ };
104
+ }
105
+ /** Derive the owner value `@auth.on.threads` filters on, per the `threads` axis. */
106
+ export function threadOwnerKey(cfg, user) {
107
+ const scope = cfg.scoping.threads ?? "actor";
108
+ const actor = String(user.mda_actor_id ?? "");
109
+ const tenant = user.mda_tenant_id ? String(user.mda_tenant_id) : undefined;
110
+ if (scope === "tenant") {
111
+ return tenant ?? actor;
112
+ }
113
+ if (scope === "channel") {
114
+ const channel = user.mda_source_thread_id;
115
+ return channel ? String(channel) : actor;
116
+ }
117
+ return actor;
118
+ }
119
+ function requireIngressSecret(request) {
120
+ const expected = process.env[INGRESS_SECRET_ENV];
121
+ const provided = request.headers.get(INGRESS_SECRET_HEADER);
122
+ if (!expected || !provided || !constantTimeEquals(expected, provided)) {
123
+ throw new HTTPException(401, {
124
+ message: "invalid or missing ingress secret",
125
+ });
126
+ }
127
+ }
128
+ function assertTenantPresentIfMulti(cfg, tenant) {
129
+ if (cfg.tenancy === "multi" && !tenant) {
130
+ throw new HTTPException(401, {
131
+ message: "missing tenant for multi-tenant deployment",
132
+ });
133
+ }
134
+ }
135
+ /** True when this deployment exposes guest issuance and the request targets it. */
136
+ export function isGuestIssuanceRequest(request, cfg) {
137
+ if (request.method !== "POST") {
138
+ return false;
139
+ }
140
+ const { pathname } = new URL(request.url);
141
+ if (pathname !== GUEST_ISSUANCE_PATH) {
142
+ return false;
143
+ }
144
+ const http = cfg.ingress.http;
145
+ if (http === "trusted_backend") {
146
+ return false;
147
+ }
148
+ return http.providers.some((provider) => provider.guest?.issue);
149
+ }
150
+ /** Placeholder identity so LangGraph auth middleware allows tokenless issuance. */
151
+ export function guestIssuanceBypassUser() {
152
+ return {
153
+ identity: GUEST_ISSUER_ACTOR,
154
+ permissions: [],
155
+ mda_actor_id: GUEST_ISSUER_ACTOR,
156
+ };
157
+ }
158
+ function constantTimeEquals(a, b) {
159
+ if (a.length !== b.length) {
160
+ return false;
161
+ }
162
+ let mismatch = 0;
163
+ for (let i = 0; i < a.length; i += 1) {
164
+ mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
165
+ }
166
+ return mismatch === 0;
167
+ }
168
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/runtime/auth.ts"],"names":[],"mappings":"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"}
@@ -1,42 +1,60 @@
1
- import type { SandboxDefinition } from "../sandbox.js";
1
+ import type { CredentialTarget, IdentityConfig, ResolvedCredential, RuntimeIdentity } from "../identity.js";
2
2
  /**
3
- * Per-provider environment variables MDA expects to hold the credentials each
4
- * sandbox provider needs. Keyed by the provider's `providerId` (when set) or
5
- * its class name.
3
+ * Component 5 Credentials, custom mode (`scoping.credentials: "custom"`, §7.2).
6
4
  *
7
- * The value is a list of alternative credential *sets*: a provider is
8
- * considered credentialed if **every** variable in **any one** set is present.
9
- * An empty list means the provider needs no credentials (e.g. it runs locally).
5
+ * The escape hatch Agent Auth can't cover: bespoke per-actor credential minting
6
+ * via an in-process resolver callback or a hosted token-exchange endpoint. It
7
+ * runs in the worker (not the shared ingress auth handler), applies the output
8
+ * to downstream calls, and caches per `(actor, tenant, target)` until the
9
+ * credential's `expiresAt`. Secrets live only in this in-memory cache keyed by
10
+ * identity — never in thread state or traces (§7.2, §8, identity-final §10).
10
11
  *
11
- * Sources: the deepagents sandbox docs and each provider's own SDK docs.
12
- * - LangSmith: https://docs.langchain.com/langsmith/sandboxes
13
- * - Daytona / E2B / Modal / Runloop / Vercel / AgentCore / Deno: provider SDK docs.
12
+ * The managed modes (`agent`/`actor`) route to Agent Auth
13
+ * (`@langchain/auth`/`langchain-auth`, §7.1) and are wired separately; this
14
+ * module is only the custom path.
14
15
  */
15
- export declare const PROVIDER_CREDENTIAL_ENV_VARS: Record<string, string[][]>;
16
- export interface ProviderCredentialStatus {
17
- /**
18
- * - `available`: required credentials are present (or none are needed).
19
- * - `missing`: a known provider's credentials are absent.
20
- * - `unknown`: provider is not in {@link PROVIDER_CREDENTIAL_ENV_VARS}.
21
- */
22
- status: "available" | "missing" | "unknown";
23
- /** The provider id / class name used for the lookup. */
24
- provider: string;
25
- /** Env vars from the primary credential set, surfaced when `missing`. */
26
- missing: string[];
16
+ /** Env var holding the HMAC secret MDA signs the endpoint assertion with. */
17
+ export declare const CREDENTIAL_SIGNING_SECRET_ENV = "MDA_TOKEN_EXCHANGE_SECRET";
18
+ /** Per-call options for {@link CredentialsProvider.for}. */
19
+ export interface CredentialForOptions {
20
+ /** Skip the cache and force a fresh mint (e.g. after a downstream 401). */
21
+ forceRefresh?: boolean;
27
22
  }
28
23
  /**
29
- * Whether MDA is running under `mda dev`. The CLI sets `MDA_DEV` when it
30
- * launches the local LangGraph dev server; it is never set for `mda deploy`.
24
+ * The per-run credential surface a tool receives as `runtime.credentials`.
25
+ * Tools call `runtime.credentials.for(target)` and attach the returned headers
26
+ * to their downstream request.
31
27
  */
32
- export declare function isDevMode(): boolean;
33
- /** Resolve the lookup key for a sandbox provider (its id, else class name). */
34
- export declare function providerKey(provider: SandboxDefinition["provider"]): string;
35
- /** Whether every variable in a credential set is set and non-empty. */
36
- export declare function credentialSetSatisfied(envVars: string[]): boolean;
28
+ export interface CredentialsProvider {
29
+ for(target: CredentialTarget, options?: CredentialForOptions): Promise<ResolvedCredential>;
30
+ }
31
+ /**
32
+ * Side effects the executor depends on, injectable so resolution is
33
+ * deterministic under test (no real clock, network, or signing key).
34
+ */
35
+ export interface CredentialDeps {
36
+ fetch: typeof fetch;
37
+ now: () => number;
38
+ /** Signs the assertion MDA presents to a hosted token-exchange endpoint. */
39
+ signAssertion: (identity: RuntimeIdentity, target: CredentialTarget) => Promise<string>;
40
+ }
41
+ /**
42
+ * TTL cache for resolved credentials, keyed by `(actor, tenant, target)` so
43
+ * per-repo/per-install tokens never collide. Kept out of thread state; entries
44
+ * expire at the credential's `expiresAt`.
45
+ */
46
+ export declare class CredentialCache {
47
+ private readonly entries;
48
+ get(key: string, nowMs: number): ResolvedCredential | undefined;
49
+ set(key: string, value: ResolvedCredential): void;
50
+ clear(): void;
51
+ }
52
+ /** Clear the process-level credential cache. Test-only. */
53
+ export declare function clearCredentialCacheForTests(): void;
37
54
  /**
38
- * Determine whether the configured provider's credentials are available so
39
- * `mda dev` can decide between the real provider and the local fallback.
55
+ * Build the `runtime.credentials` provider for a run, or `undefined` when the
56
+ * deployment isn't in custom-credentials mode. The provider is bound to the
57
+ * run's resolved identity; every `for(target)` call is scoped to that actor.
40
58
  */
41
- export declare function providerCredentialStatus(provider: SandboxDefinition["provider"]): ProviderCredentialStatus;
59
+ export declare function createCredentialsProvider(cfg: IdentityConfig, identity: RuntimeIdentity | undefined, deps?: Partial<CredentialDeps>, cache?: CredentialCache): CredentialsProvider | undefined;
42
60
  //# sourceMappingURL=credentials.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/runtime/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAyBnE,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;IAC5C,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,GAAG,MAAM,CAG3E;AAED,uEAAuE;AACvE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAEjE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,GACtC,wBAAwB,CAU1B"}
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/runtime/credentials.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,eAAe,EAChB,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;;;;GAaG;AAEH,6EAA6E;AAC7E,eAAO,MAAM,6BAA6B,8BAA8B,CAAC;AAKzE,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACnC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,CACD,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,4EAA4E;IAC5E,aAAa,EAAE,CACb,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,gBAAgB,KACrB,OAAO,CAAC,MAAM,CAAC,CAAC;CACtB;AAQD;;;;GAIG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IAEzD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAY/D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,IAAI;IASjD,KAAK,IAAI,IAAI;CAGd;AAKD,2DAA2D;AAC3D,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,eAAe,GAAG,SAAS,EACrC,IAAI,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,EAC9B,KAAK,GAAE,eAA8B,GACpC,mBAAmB,GAAG,SAAS,CA8BjC"}
@@ -1,72 +1,189 @@
1
+ import { SignJWT } from "jose";
1
2
  /**
2
- * Per-provider environment variables MDA expects to hold the credentials each
3
- * sandbox provider needs. Keyed by the provider's `providerId` (when set) or
4
- * its class name.
3
+ * Component 5 Credentials, custom mode (`scoping.credentials: "custom"`, §7.2).
5
4
  *
6
- * The value is a list of alternative credential *sets*: a provider is
7
- * considered credentialed if **every** variable in **any one** set is present.
8
- * An empty list means the provider needs no credentials (e.g. it runs locally).
5
+ * The escape hatch Agent Auth can't cover: bespoke per-actor credential minting
6
+ * via an in-process resolver callback or a hosted token-exchange endpoint. It
7
+ * runs in the worker (not the shared ingress auth handler), applies the output
8
+ * to downstream calls, and caches per `(actor, tenant, target)` until the
9
+ * credential's `expiresAt`. Secrets live only in this in-memory cache keyed by
10
+ * identity — never in thread state or traces (§7.2, §8, identity-final §10).
9
11
  *
10
- * Sources: the deepagents sandbox docs and each provider's own SDK docs.
11
- * - LangSmith: https://docs.langchain.com/langsmith/sandboxes
12
- * - Daytona / E2B / Modal / Runloop / Vercel / AgentCore / Deno: provider SDK docs.
12
+ * The managed modes (`agent`/`actor`) route to Agent Auth
13
+ * (`@langchain/auth`/`langchain-auth`, §7.1) and are wired separately; this
14
+ * module is only the custom path.
13
15
  */
14
- export const PROVIDER_CREDENTIAL_ENV_VARS = {
15
- // LangSmith managed sandboxes.
16
- LangSmithSandbox: [["LANGSMITH_API_KEY"]],
17
- // Daytona (`langchain-daytona` / deepagents `DaytonaSandbox`).
18
- DaytonaSandbox: [["DAYTONA_API_KEY"]],
19
- // E2B (`langchain-e2b` / deepagents `E2BSandbox`).
20
- E2BSandbox: [["E2B_API_KEY"]],
21
- // Modal requires both a token id and secret.
22
- ModalSandbox: [["MODAL_TOKEN_ID", "MODAL_TOKEN_SECRET"]],
23
- // Runloop devboxes.
24
- RunloopSandbox: [["RUNLOOP_API_KEY"]],
25
- // Vercel: either an OIDC token, or an access token + team + project ids.
26
- VercelSandbox: [
27
- ["VERCEL_OIDC_TOKEN"],
28
- ["VERCEL_TOKEN", "VERCEL_TEAM_ID", "VERCEL_PROJECT_ID"],
29
- ],
30
- // AWS Bedrock AgentCore: standard AWS credential resolution.
31
- AgentCoreSandbox: [
32
- ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"],
33
- ["AWS_PROFILE"],
34
- ],
35
- // Deno Deploy microVMs (`@langchain/deno`) need an organization token.
36
- DenoSandbox: [["DENO_DEPLOY_TOKEN"]],
37
- // Node VFS runs locally and needs no credentials.
38
- NodeVFSSandbox: [],
39
- };
16
+ /** Env var holding the HMAC secret MDA signs the endpoint assertion with. */
17
+ export const CREDENTIAL_SIGNING_SECRET_ENV = "MDA_TOKEN_EXCHANGE_SECRET";
18
+ /** How long a signed endpoint assertion is valid, in seconds. */
19
+ const ASSERTION_TTL_SECONDS = 60;
40
20
  /**
41
- * Whether MDA is running under `mda dev`. The CLI sets `MDA_DEV` when it
42
- * launches the local LangGraph dev server; it is never set for `mda deploy`.
21
+ * TTL cache for resolved credentials, keyed by `(actor, tenant, target)` so
22
+ * per-repo/per-install tokens never collide. Kept out of thread state; entries
23
+ * expire at the credential's `expiresAt`.
43
24
  */
44
- export function isDevMode() {
45
- const value = process.env.MDA_DEV;
46
- return typeof value === "string" && value.length > 0;
25
+ export class CredentialCache {
26
+ entries = new Map();
27
+ get(key, nowMs) {
28
+ const hit = this.entries.get(key);
29
+ if (!hit) {
30
+ return undefined;
31
+ }
32
+ if (hit.expiresAtMs !== undefined && nowMs >= hit.expiresAtMs) {
33
+ this.entries.delete(key);
34
+ return undefined;
35
+ }
36
+ return hit.value;
37
+ }
38
+ set(key, value) {
39
+ let expiresAtMs;
40
+ if (value.expiresAt) {
41
+ const parsed = Date.parse(value.expiresAt);
42
+ expiresAtMs = Number.isNaN(parsed) ? undefined : parsed;
43
+ }
44
+ this.entries.set(key, { value, expiresAtMs });
45
+ }
46
+ clear() {
47
+ this.entries.clear();
48
+ }
47
49
  }
48
- /** Resolve the lookup key for a sandbox provider (its id, else class name). */
49
- export function providerKey(provider) {
50
- const p = provider;
51
- return (p.providerId ?? p.name ?? "").trim();
50
+ /** Process-level cache shared across runs for the lifetime of the deployment. */
51
+ const defaultCache = new CredentialCache();
52
+ /** Clear the process-level credential cache. Test-only. */
53
+ export function clearCredentialCacheForTests() {
54
+ defaultCache.clear();
55
+ }
56
+ /**
57
+ * Build the `runtime.credentials` provider for a run, or `undefined` when the
58
+ * deployment isn't in custom-credentials mode. The provider is bound to the
59
+ * run's resolved identity; every `for(target)` call is scoped to that actor.
60
+ */
61
+ export function createCredentialsProvider(cfg, identity, deps, cache = defaultCache) {
62
+ if (cfg.scoping.credentials !== "custom" || !cfg.credentials) {
63
+ return undefined;
64
+ }
65
+ const resolved = {
66
+ fetch: deps?.fetch ?? fetch,
67
+ now: deps?.now ?? (() => Date.now()),
68
+ signAssertion: deps?.signAssertion ?? defaultSignAssertion,
69
+ };
70
+ return {
71
+ async for(target, options) {
72
+ if (!identity) {
73
+ throw new Error("runtime.credentials.for(...) requires a resolved identity, but none " +
74
+ "was available for this run.");
75
+ }
76
+ const key = cacheKey(identity, target);
77
+ if (!options?.forceRefresh) {
78
+ const hit = cache.get(key, resolved.now());
79
+ if (hit) {
80
+ return hit;
81
+ }
82
+ }
83
+ // Fail-closed: a resolver/endpoint error propagates and nothing is cached.
84
+ const out = await resolveCredential(cfg, identity, target, resolved);
85
+ cache.set(key, out);
86
+ return out;
87
+ },
88
+ };
52
89
  }
53
- /** Whether every variable in a credential set is set and non-empty. */
54
- export function credentialSetSatisfied(envVars) {
55
- return envVars.every((name) => (process.env[name] ?? "").trim().length > 0);
90
+ async function resolveCredential(cfg, identity, target, deps) {
91
+ const creds = cfg.credentials;
92
+ if (!creds) {
93
+ throw new Error("No credentials resolver is configured for this deployment.");
94
+ }
95
+ if ("resolve" in creds) {
96
+ const out = await creds.resolve({ identity, target });
97
+ return normalizeResolved(out, "resolver");
98
+ }
99
+ const assertion = await deps.signAssertion(identity, target);
100
+ return callTokenExchange(creds.endpoint.url, assertion, deps);
101
+ }
102
+ async function callTokenExchange(url, assertion, deps) {
103
+ const response = await deps.fetch(url, {
104
+ method: "POST",
105
+ headers: {
106
+ "content-type": "application/json",
107
+ authorization: `Bearer ${assertion}`,
108
+ },
109
+ body: JSON.stringify({ assertion }),
110
+ });
111
+ if (!response.ok) {
112
+ throw new Error(`Credential token-exchange endpoint ${url} responded ${response.status}.`);
113
+ }
114
+ return normalizeResolved(await response.json(), `endpoint ${url}`);
115
+ }
116
+ async function defaultSignAssertion(identity, target) {
117
+ const secret = process.env[CREDENTIAL_SIGNING_SECRET_ENV];
118
+ if (!secret) {
119
+ throw new Error(`A hosted credential endpoint requires a signing secret. Set ` +
120
+ `${CREDENTIAL_SIGNING_SECRET_ENV} in the deployment environment.`);
121
+ }
122
+ const issuedAt = Math.floor(Date.now() / 1000);
123
+ return new SignJWT({
124
+ actor_id: identity.actor.id,
125
+ actor_type: identity.actor.type,
126
+ tenant_id: identity.tenant?.id,
127
+ target: { kind: target.kind, name: target.name, intent: target.intent },
128
+ })
129
+ .setProtectedHeader({ alg: "HS256" })
130
+ .setSubject(identity.actor.id)
131
+ .setIssuedAt(issuedAt)
132
+ .setExpirationTime(issuedAt + ASSERTION_TTL_SECONDS)
133
+ .sign(new TextEncoder().encode(secret));
56
134
  }
57
135
  /**
58
- * Determine whether the configured provider's credentials are available so
59
- * `mda dev` can decide between the real provider and the local fallback.
136
+ * The cache key that discriminates a minted credential: the actor + tenant it
137
+ * belongs to, plus the target's kind/name/intent/thread and metadata (§7.2).
60
138
  */
61
- export function providerCredentialStatus(provider) {
62
- const key = providerKey(provider);
63
- const sets = PROVIDER_CREDENTIAL_ENV_VARS[key];
64
- if (sets === undefined) {
65
- return { status: "unknown", provider: key || "unknown", missing: [] };
139
+ function cacheKey(identity, target) {
140
+ return stableStringify([
141
+ identity.actor.id,
142
+ identity.tenant?.id ?? null,
143
+ target.kind,
144
+ target.name,
145
+ target.intent ?? null,
146
+ target.threadId ?? null,
147
+ target.metadata ?? null,
148
+ ]);
149
+ }
150
+ /** Deterministic JSON with object keys sorted, so the cache key is stable. */
151
+ function stableStringify(value) {
152
+ if (value === null || typeof value !== "object") {
153
+ return JSON.stringify(value) ?? "null";
154
+ }
155
+ if (Array.isArray(value)) {
156
+ return `[${value.map(stableStringify).join(",")}]`;
157
+ }
158
+ const obj = value;
159
+ return `{${Object.keys(obj)
160
+ .sort()
161
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(obj[key])}`)
162
+ .join(",")}}`;
163
+ }
164
+ /** Validate a resolver/endpoint result into a {@link ResolvedCredential}. */
165
+ function normalizeResolved(value, sourceLabel) {
166
+ if (!value || typeof value !== "object") {
167
+ throw new Error(`Credential ${sourceLabel} returned a non-object result.`);
168
+ }
169
+ const { headers, expiresAt } = value;
170
+ if (!headers || typeof headers !== "object" || Array.isArray(headers)) {
171
+ throw new Error(`Credential ${sourceLabel} result is missing a headers object.`);
172
+ }
173
+ const normalizedHeaders = {};
174
+ for (const [name, headerValue] of Object.entries(headers)) {
175
+ if (typeof headerValue !== "string") {
176
+ throw new Error(`Credential ${sourceLabel} header "${name}" must be a string.`);
177
+ }
178
+ normalizedHeaders[name] = headerValue;
66
179
  }
67
- if (sets.length === 0 || sets.some(credentialSetSatisfied)) {
68
- return { status: "available", provider: key, missing: [] };
180
+ const result = { headers: normalizedHeaders };
181
+ if (expiresAt !== undefined) {
182
+ if (typeof expiresAt !== "string") {
183
+ throw new Error(`Credential ${sourceLabel} \`expiresAt\` must be an ISO string.`);
184
+ }
185
+ result.expiresAt = expiresAt;
69
186
  }
70
- return { status: "missing", provider: key, missing: sets[0] ?? [] };
187
+ return result;
71
188
  }
72
189
  //# sourceMappingURL=credentials.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/runtime/credentials.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAA+B;IACtE,+BAA+B;IAC/B,gBAAgB,EAAE,CAAC,CAAC,mBAAmB,CAAC,CAAC;IACzC,+DAA+D;IAC/D,cAAc,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACrC,mDAAmD;IACnD,UAAU,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC;IAC7B,6CAA6C;IAC7C,YAAY,EAAE,CAAC,CAAC,gBAAgB,EAAE,oBAAoB,CAAC,CAAC;IACxD,oBAAoB;IACpB,cAAc,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACrC,yEAAyE;IACzE,aAAa,EAAE;QACb,CAAC,mBAAmB,CAAC;QACrB,CAAC,cAAc,EAAE,gBAAgB,EAAE,mBAAmB,CAAC;KACxD;IACD,6DAA6D;IAC7D,gBAAgB,EAAE;QAChB,CAAC,mBAAmB,EAAE,uBAAuB,CAAC;QAC9C,CAAC,aAAa,CAAC;KAChB;IACD,uEAAuE;IACvE,WAAW,EAAE,CAAC,CAAC,mBAAmB,CAAC,CAAC;IACpC,kDAAkD;IAClD,cAAc,EAAE,EAAE;CACnB,CAAC;AAeF;;;GAGG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;IAClC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AACvD,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,QAAuC;IACjE,MAAM,CAAC,GAAG,QAAkD,CAAC;IAC7D,OAAO,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,sBAAsB,CAAC,OAAiB;IACtD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAAuC;IAEvC,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,4BAA4B,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACxE,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,EAAE,CAAC;QAC3D,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC7D,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;AACtE,CAAC"}
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/runtime/credentials.ts"],"names":[],"mappings":"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"}