@agent-assembly/sdk 0.0.1-beta.3 → 0.0.1-beta.4

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 (56) hide show
  1. package/dist/cjs/core/gateway-resolver.js +73 -3
  2. package/dist/cjs/core/init-assembly.js +151 -33
  3. package/dist/cjs/gateway/client.js +63 -1
  4. package/dist/cjs/gateway/index.js +2 -1
  5. package/dist/cjs/hooks/ai-sdk.js +3 -5
  6. package/dist/cjs/hooks/langchain.js +12 -3
  7. package/dist/cjs/hooks/mastra.js +10 -6
  8. package/dist/cjs/hooks/openai-agents.js +1 -3
  9. package/dist/cjs/native/client.js +70 -25
  10. package/dist/cjs/op-control.js +55 -1
  11. package/dist/cjs/runtime.js +73 -7
  12. package/dist/esm/core/gateway-resolver.js +72 -3
  13. package/dist/esm/core/gateway-resolver.js.map +1 -1
  14. package/dist/esm/core/init-assembly.js +150 -32
  15. package/dist/esm/core/init-assembly.js.map +1 -1
  16. package/dist/esm/gateway/client.js +62 -1
  17. package/dist/esm/gateway/client.js.map +1 -1
  18. package/dist/esm/gateway/index.js +1 -1
  19. package/dist/esm/gateway/index.js.map +1 -1
  20. package/dist/esm/hooks/ai-sdk.js +3 -5
  21. package/dist/esm/hooks/ai-sdk.js.map +1 -1
  22. package/dist/esm/hooks/langchain.js +12 -3
  23. package/dist/esm/hooks/langchain.js.map +1 -1
  24. package/dist/esm/hooks/mastra.js +10 -6
  25. package/dist/esm/hooks/mastra.js.map +1 -1
  26. package/dist/esm/hooks/openai-agents.js +1 -3
  27. package/dist/esm/hooks/openai-agents.js.map +1 -1
  28. package/dist/esm/native/client.js +68 -24
  29. package/dist/esm/native/client.js.map +1 -1
  30. package/dist/esm/op-control.js +53 -1
  31. package/dist/esm/op-control.js.map +1 -1
  32. package/dist/esm/runtime.js +72 -7
  33. package/dist/esm/runtime.js.map +1 -1
  34. package/dist/types/core/gateway-resolver.d.ts +18 -1
  35. package/dist/types/core/gateway-resolver.d.ts.map +1 -1
  36. package/dist/types/core/init-assembly.d.ts +2 -1
  37. package/dist/types/core/init-assembly.d.ts.map +1 -1
  38. package/dist/types/gateway/client.d.ts +17 -0
  39. package/dist/types/gateway/client.d.ts.map +1 -1
  40. package/dist/types/gateway/index.d.ts +1 -1
  41. package/dist/types/gateway/index.d.ts.map +1 -1
  42. package/dist/types/hooks/ai-sdk.d.ts.map +1 -1
  43. package/dist/types/hooks/langchain.d.ts +11 -0
  44. package/dist/types/hooks/langchain.d.ts.map +1 -1
  45. package/dist/types/hooks/mastra.d.ts.map +1 -1
  46. package/dist/types/hooks/openai-agents.d.ts.map +1 -1
  47. package/dist/types/native/client.d.ts +33 -0
  48. package/dist/types/native/client.d.ts.map +1 -1
  49. package/dist/types/op-control.d.ts +29 -2
  50. package/dist/types/op-control.d.ts.map +1 -1
  51. package/dist/types/runtime.d.ts +27 -5
  52. package/dist/types/runtime.d.ts.map +1 -1
  53. package/dist/types/types/assembly-config.d.ts +6 -0
  54. package/dist/types/types/assembly-config.d.ts.map +1 -1
  55. package/native/aa-ffi-node/index.d.ts +74 -0
  56. package/package.json +5 -5
@@ -33,7 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.__testing = exports.AASM_AUTO_START_ARGV = exports.LEGACY_ENV_API_KEY = exports.LEGACY_ENV_GATEWAY_URL = exports.ENV_API_KEY = exports.ENV_GATEWAY_URL = exports.DEFAULT_CONFIG_FILE_PATH = exports.DEFAULT_AUTO_START_TIMEOUT_MS = exports.DEFAULT_PROBE_TIMEOUT_MS = exports.DEFAULT_HEALTHZ_PATH = exports.DEFAULT_GATEWAY_URL = void 0;
36
+ exports.__testing = exports.AASM_AUTO_START_ARGV = exports.LEGACY_ENV_API_KEY = exports.LEGACY_ENV_GATEWAY_URL = exports.ENV_AUTO_START = exports.ENV_API_KEY = exports.ENV_GATEWAY_URL = exports.DEFAULT_CONFIG_FILE_PATH = exports.DEFAULT_AUTO_START_TIMEOUT_MS = exports.DEFAULT_PROBE_TIMEOUT_MS = exports.DEFAULT_HEALTHZ_PATH = exports.DEFAULT_GATEWAY_URL = void 0;
37
+ exports.assertAllowedAasmPath = assertAllowedAasmPath;
37
38
  exports.probeHealthz = probeHealthz;
38
39
  exports.waitForHealthz = waitForHealthz;
39
40
  exports.loadConfigFile = loadConfigFile;
@@ -61,7 +62,9 @@ const index_js_1 = require("../errors/index.js");
61
62
  * as deprecated aliases (a one-time warning is logged when a legacy name
62
63
  * supplies the value)
63
64
  * 3. Config file (~/.aasm/config.yaml, optional js-yaml soft dep)
64
- * 4. Local default: probe http://localhost:7391, auto-start if absent
65
+ * 4. Local default: probe http://localhost:7391; when absent, auto-start the
66
+ * local `aasm` gateway ONLY if `AA_AUTO_START` is opted in and the binary
67
+ * resolves to an allow-listed install dir — otherwise raise an error.
65
68
  */
66
69
  exports.DEFAULT_GATEWAY_URL = "http://localhost:7391";
67
70
  exports.DEFAULT_HEALTHZ_PATH = "/healthz";
@@ -70,6 +73,56 @@ exports.DEFAULT_AUTO_START_TIMEOUT_MS = 5000;
70
73
  exports.DEFAULT_CONFIG_FILE_PATH = "~/.aasm/config.yaml";
71
74
  exports.ENV_GATEWAY_URL = "AA_GATEWAY_URL";
72
75
  exports.ENV_API_KEY = "AA_API_KEY";
76
+ /**
77
+ * Opt-in gate for auto-starting a local gateway. Auto-start spawns the `aasm`
78
+ * binary resolved from `$PATH`, so it is gated behind an explicit opt-in rather
79
+ * than running silently: a `$PATH` entry an attacker can write to would
80
+ * otherwise be executed by any process that calls `initAssembly()`. Set to
81
+ * `1`/`true`/`yes` to permit auto-start.
82
+ */
83
+ exports.ENV_AUTO_START = "AA_AUTO_START";
84
+ /** Truthy values that enable {@link ENV_AUTO_START}. */
85
+ function autoStartEnabled() {
86
+ const raw = process.env[exports.ENV_AUTO_START]?.trim().toLowerCase();
87
+ return raw === "1" || raw === "true" || raw === "yes";
88
+ }
89
+ /**
90
+ * Directories an auto-started `aasm` binary is permitted to live in. The
91
+ * resolved path must be absolute and sit inside one of these install roots,
92
+ * which blocks a `$PATH`-injected `./aasm` (cwd) or a binary planted in an
93
+ * arbitrary writable directory from being spawned. Mirrors the documented
94
+ * install locations (Homebrew, system, user-local, cargo).
95
+ */
96
+ function allowedInstallDirs() {
97
+ const home = (0, node_os_1.homedir)();
98
+ return [
99
+ "/usr/local/bin",
100
+ "/usr/bin",
101
+ "/opt/homebrew/bin",
102
+ (0, node_path_1.join)(home, ".local", "bin"),
103
+ (0, node_path_1.join)(home, ".cargo", "bin"),
104
+ "/usr/local/cargo/bin",
105
+ ];
106
+ }
107
+ /**
108
+ * Throw {@link ConfigurationError} unless `aasmPath` is an absolute path inside
109
+ * an allow-listed install directory (see {@link allowedInstallDirs}). This is
110
+ * the integrity gate for the auto-start subprocess — without it the SDK would
111
+ * execute whatever `aasm` happened to be first on `$PATH`.
112
+ */
113
+ function assertAllowedAasmPath(aasmPath) {
114
+ if (!(0, node_path_1.isAbsolute)(aasmPath)) {
115
+ throw new index_js_1.ConfigurationError(`Refusing to auto-start a non-absolute 'aasm' path: ${aasmPath}. ` +
116
+ `Set ${exports.ENV_GATEWAY_URL} to an already-running gateway instead.`);
117
+ }
118
+ const resolved = (0, node_path_1.resolve)(aasmPath);
119
+ const ok = allowedInstallDirs().some((dir) => resolved.startsWith(dir + "/"));
120
+ if (!ok) {
121
+ throw new index_js_1.ConfigurationError(`Refusing to auto-start 'aasm' from an untrusted location: ${resolved}. ` +
122
+ `Install it under one of: ${allowedInstallDirs().join(", ")}, ` +
123
+ `or set ${exports.ENV_GATEWAY_URL} to an already-running gateway.`);
124
+ }
125
+ }
73
126
  /**
74
127
  * Deprecated environment-variable names, kept as backwards-compatible aliases.
75
128
  *
@@ -154,7 +207,11 @@ async function waitForHealthz(baseUrl, timeoutMs = exports.DEFAULT_AUTO_START_TI
154
207
  return probeHealthz(baseUrl);
155
208
  }
156
209
  function expandHome(p) {
157
- return p.startsWith("~") ? (0, node_path_1.resolve)((0, node_os_1.homedir)(), p.slice(p.startsWith("~/") ? 2 : 1)) : p;
210
+ if (!p.startsWith("~")) {
211
+ return p;
212
+ }
213
+ const prefixLength = p.startsWith("~/") ? 2 : 1;
214
+ return (0, node_path_1.resolve)((0, node_os_1.homedir)(), p.slice(prefixLength));
158
215
  }
159
216
  /**
160
217
  * Load ``~/.aasm/config.yaml`` if present.
@@ -246,6 +303,11 @@ async function autoStartGateway(baseUrl = exports.DEFAULT_GATEWAY_URL, timeoutMs
246
303
  throw new index_js_1.ConfigurationError(`No gateway found at ${baseUrl} and 'aasm' is not on PATH. ` +
247
304
  "Install it with: npm install -g @agent-assembly/cli (or pnpm add -g)");
248
305
  }
306
+ // Integrity gate: only spawn an absolute path from an allow-listed install
307
+ // dir, and surface the resolved path so the operator can see exactly which
308
+ // binary the SDK is about to execute.
309
+ assertAllowedAasmPath(aasmPath);
310
+ console.info(`[agent-assembly] auto-starting gateway from ${aasmPath}`);
249
311
  _seams.spawnAasm(aasmPath);
250
312
  if (!(await waitForHealthz(baseUrl, timeoutMs))) {
251
313
  throw new index_js_1.GatewayError(`Auto-started gateway at ${baseUrl} did not become ready ` +
@@ -276,6 +338,14 @@ async function resolveGatewayUrl(explicit) {
276
338
  if (await _seams.probeHealthz(exports.DEFAULT_GATEWAY_URL)) {
277
339
  return exports.DEFAULT_GATEWAY_URL;
278
340
  }
341
+ // Auto-start is opt-in: spawning the local `aasm` binary is a privileged
342
+ // side effect, so a missing gateway is a hard error unless the operator has
343
+ // explicitly enabled AA_AUTO_START.
344
+ if (!autoStartEnabled()) {
345
+ throw new index_js_1.ConfigurationError(`No gateway found at ${exports.DEFAULT_GATEWAY_URL}. Start one with 'aasm start ` +
346
+ `--mode local', set ${exports.ENV_GATEWAY_URL} to a running gateway, or set ` +
347
+ `${exports.ENV_AUTO_START}=1 to allow the SDK to auto-start a local gateway.`);
348
+ }
279
349
  await _seams.autoStartGateway(exports.DEFAULT_GATEWAY_URL);
280
350
  return exports.DEFAULT_GATEWAY_URL;
281
351
  }
@@ -42,6 +42,7 @@ exports.initAssembly = initAssembly;
42
42
  const node_module_1 = require("node:module");
43
43
  const client_js_1 = require("../gateway/client.js");
44
44
  const client_js_2 = require("../native/client.js");
45
+ const index_js_1 = require("../errors/index.js");
45
46
  const enforcement_mode_js_1 = require("../types/enforcement-mode.js");
46
47
  const ai_sdk_detection_js_1 = require("../hooks/ai-sdk-detection.js");
47
48
  const ai_sdk_js_1 = require("../hooks/ai-sdk.js");
@@ -51,7 +52,7 @@ const mastra_detection_js_1 = require("../hooks/mastra-detection.js");
51
52
  const mastra_js_1 = require("../hooks/mastra.js");
52
53
  const openai_agents_detection_js_1 = require("../hooks/openai-agents-detection.js");
53
54
  const openai_agents_js_1 = require("../hooks/openai-agents.js");
54
- const index_js_1 = require("../lineage/index.js");
55
+ const index_js_2 = require("../lineage/index.js");
55
56
  const gateway_resolver_js_1 = require("./gateway-resolver.js");
56
57
  const requireFromCwd = (0, node_module_1.createRequire)(`${process.cwd()}/`);
57
58
  /** Env-var fallback for ``gatewayUrl`` read at ``initAssembly`` entry. */
@@ -76,14 +77,73 @@ function buildRegistrationEvent(config) {
76
77
  event.enforcement_mode = config.enforcementMode;
77
78
  return event;
78
79
  }
79
- function createClient(config) {
80
+ /**
81
+ * Build the {@link RegisterOptions} for the native `register` gRPC call
82
+ * (AAASM-3400) from the resolved config and the detected frameworks. `name`
83
+ * falls back to `agentId`; `framework` is the first detected framework (or
84
+ * `"none"` when running without an adapter); `gatewayEndpoint` is set only when
85
+ * a gateway URL was resolved so the native default endpoint resolution is
86
+ * preserved when it was not. `teamId` / `parentAgentId` carry the agent's
87
+ * team-budget scoping and topology lineage to the gateway (AAASM-3415); each is
88
+ * set only when present so an unset field stays absent.
89
+ */
90
+ function buildRegisterOptions(config, frameworks) {
91
+ const agentId = config.agentId ?? "";
92
+ return {
93
+ agentId,
94
+ name: config.name ?? agentId,
95
+ framework: frameworks[0] ?? "none",
96
+ ...(config.gatewayUrl ? { gatewayEndpoint: config.gatewayUrl } : {}),
97
+ ...(config.teamId ? { teamId: config.teamId } : {}),
98
+ ...(config.parentAgentId ? { parentAgentId: config.parentAgentId } : {})
99
+ };
100
+ }
101
+ /**
102
+ * The only built-in {@link AssemblyMode} for which {@link createClient}
103
+ * constructs a gateway client whose `check()` consults a real authoritative
104
+ * verdict (the native `queryPolicy` against a reachable `aa-runtime`). Every
105
+ * other mode falls back to the allow-all no-op client.
106
+ */
107
+ const CHECK_CAPABLE_MODE = "napi-inprocess";
108
+ function createClient(config, nativeClientOverride) {
80
109
  const mode = config.mode ?? "auto";
81
110
  if (config.gatewayClient) {
82
111
  return config.gatewayClient;
83
112
  }
113
+ // AAASM-3105 (fail closed): the no-op gateway client's `check()` is allow-all,
114
+ // so registering under live `"enforce"` while routing through it would let a
115
+ // policy-denied action proceed unchecked — a silent fail-open. When the caller
116
+ // explicitly asks for `"enforce"` but supplies no check-capable mode (and no
117
+ // own `gatewayClient`), refuse loudly instead of pretending to enforce. An
118
+ // omitted `enforcementMode` keeps the pre-feature behavior (server-side
119
+ // default), and `"observe"` / `"disabled"` intentionally let actions through.
120
+ if (config.enforcementMode === "enforce" && mode !== CHECK_CAPABLE_MODE) {
121
+ throw new index_js_1.ConfigurationError(`enforcementMode "enforce" requires a check-capable client, but mode "${mode}" ` +
122
+ `routes through the allow-all no-op gateway client, which cannot block a ` +
123
+ `denied action. Use mode "${CHECK_CAPABLE_MODE}", supply your own ` +
124
+ `gatewayClient, or set enforcementMode to "observe"/"disabled".`);
125
+ }
84
126
  // HTTP routes use controlPlaneUrl when set, otherwise fall back to the
85
127
  // resolved gatewayUrl so pre-feature callers keep their existing base URL.
86
128
  const httpBaseUrl = config.controlPlaneUrl ?? config.gatewayUrl;
129
+ // AAASM-3050: in napi-inprocess mode, route `check()` through the native
130
+ // runtime so a reachable aa-runtime's DENY actually blocks a tool. The
131
+ // native primitive fails open when the runtime is absent or slow, and the
132
+ // gateway client swallows local faults, so this never blocks without a
133
+ // runtime — preserving the pre-feature fail-open behavior.
134
+ if (mode === "napi-inprocess") {
135
+ // Reuse the caller-supplied native client when present so the registered
136
+ // session (the one `register()` stored the gateway token on) is the same
137
+ // session `queryPolicy` runs against. Standalone callers (and the routing
138
+ // tests) get a freshly-built client instead.
139
+ const nativeClient = nativeClientOverride ??
140
+ (0, client_js_2.createNativeClient)({
141
+ gateway: config.gatewayUrl ?? "",
142
+ apiKey: config.apiKey ?? "",
143
+ mode: "napi-inprocess"
144
+ });
145
+ return (0, client_js_1.createNativeGatewayClient)(mode, nativeClient, config.agentId, httpBaseUrl);
146
+ }
87
147
  return (0, client_js_1.createNoopGatewayClient)(mode, httpBaseUrl);
88
148
  }
89
149
  function isPackageInstalled(packageName) {
@@ -177,7 +237,7 @@ async function patchDetectedVercelAiSdk(client, frameworks, agentId) {
177
237
  }
178
238
  return (0, ai_sdk_js_1.patchVercelAiSdk)({
179
239
  gatewayClient: client,
180
- ...(agentId !== undefined ? { agentId } : {})
240
+ ...(agentId === undefined ? {} : { agentId })
181
241
  });
182
242
  }
183
243
  async function patchDetectedLangGraph(frameworks, agentId) {
@@ -198,7 +258,13 @@ async function patchDetectedOpenAIAgents(client, frameworks) {
198
258
  }
199
259
  return (0, openai_agents_js_1.patchOpenAIAgents)({ gatewayClient: client });
200
260
  }
201
- async function initAssembly(config = {}) {
261
+ /**
262
+ * Validate caller-supplied `initAssembly` config, throwing `RangeError` on the
263
+ * two fields that can arrive malformed from non-TS callers (plain JS, JSON
264
+ * config, dynamic input). Extracted to keep `initAssembly` below the cognitive
265
+ * complexity threshold; behaviour-preserving.
266
+ */
267
+ function validateConfig(config) {
202
268
  if (config.delegationReason !== undefined && config.delegationReason.length > 256) {
203
269
  throw new RangeError("delegationReason must be <= 256 characters");
204
270
  }
@@ -208,9 +274,51 @@ async function initAssembly(config = {}) {
208
274
  if (config.enforcementMode !== undefined && !enforcement_mode_js_1.ENFORCEMENT_MODES.includes(config.enforcementMode)) {
209
275
  throw new RangeError(`enforcementMode must be one of: ${enforcement_mode_js_1.ENFORCEMENT_MODES.join(", ")} (got: ${String(config.enforcementMode)})`);
210
276
  }
277
+ }
278
+ /**
279
+ * Run every framework detect-and-patch path for the resolved config. Extracted
280
+ * from `initAssembly` to keep its cognitive complexity below threshold;
281
+ * behaviour-preserving (same calls, same order).
282
+ */
283
+ async function applyFrameworkPatches(config, client, frameworks) {
284
+ const langChainHandler = await registerLangChainHandler(config, client, frameworks);
285
+ const wrappedLangChainTools = await wrapLangChainTools(config, client, frameworks);
286
+ const vercelAiSdkPatched = await patchDetectedVercelAiSdk(client, frameworks, config.agentId);
287
+ const openAIAgentsPatched = await patchDetectedOpenAIAgents(client, frameworks);
288
+ const langGraphPatched = await patchDetectedLangGraph(frameworks, config.agentId);
289
+ const mastraPatched = await patchDetectedMastra(frameworks, config.agentId);
290
+ return {
291
+ langChainHandler,
292
+ wrappedLangChainTools,
293
+ vercelAiSdkPatched,
294
+ openAIAgentsPatched,
295
+ langGraphPatched,
296
+ mastraPatched
297
+ };
298
+ }
299
+ /**
300
+ * Build the deduped list of active adapter ids from the registered adapters plus
301
+ * whichever framework patches actually took effect. Extracted from
302
+ * `initAssembly` to keep its cognitive complexity below threshold.
303
+ */
304
+ function buildActiveAdapters(adapters, patches) {
305
+ return [
306
+ ...new Set([
307
+ ...adapters.map((adapter) => adapter.id),
308
+ ...(patches.langChainHandler ? ["langchain-js"] : []),
309
+ ...(patches.wrappedLangChainTools.length > 0 ? ["langchain-js"] : []),
310
+ ...(patches.vercelAiSdkPatched ? ["vercel-ai-sdk"] : []),
311
+ ...(patches.openAIAgentsPatched ? ["openai-agents"] : []),
312
+ ...(patches.langGraphPatched ? ["langgraph-js"] : []),
313
+ ...(patches.mastraPatched ? ["mastra"] : [])
314
+ ])
315
+ ];
316
+ }
317
+ async function initAssembly(config = {}) {
318
+ validateConfig(config);
211
319
  // Auto-populate parentAgentId from the async context store when not explicitly provided.
212
320
  // This allows child agents spawned inside framework hooks to inherit lineage automatically.
213
- const resolvedParentAgentId = config.parentAgentId ?? (0, index_js_1.currentAgentId)();
321
+ const resolvedParentAgentId = config.parentAgentId ?? (0, index_js_2.currentAgentId)();
214
322
  // Env-var fallbacks read at entry: explicit config field > env-var > the
215
323
  // downstream resolver chain (which may itself error if required and absent).
216
324
  const gatewayUrlInput = config.gatewayUrl ?? process.env[exports.ENV_GATEWAY_URL];
@@ -221,47 +329,57 @@ async function initAssembly(config = {}) {
221
329
  ...config,
222
330
  gatewayUrl: resolvedGatewayUrl,
223
331
  apiKey: resolvedApiKey,
224
- ...(controlPlaneUrlInput !== undefined ? { controlPlaneUrl: controlPlaneUrlInput } : {}),
332
+ ...(controlPlaneUrlInput === undefined ? {} : { controlPlaneUrl: controlPlaneUrlInput }),
225
333
  ...(resolvedParentAgentId ? { parentAgentId: resolvedParentAgentId } : {})
226
334
  };
227
- const client = createClient(resolvedConfig);
228
335
  const frameworks = detectFrameworks();
229
- const adapters = await registerAdapters(frameworks);
230
- await startNetworkLayerIfNeeded(client, resolvedConfig);
231
- // Send topology registration event through the native transport on every boot
232
- // except sdk-only mode (which has no sidecar to register with).
336
+ // Build the native transport up front (every mode except sdk-only, which has
337
+ // no sidecar) so the same session backs both the gateway client's `check()`
338
+ // and the agent registration the gateway token `register()` stores on the
339
+ // session is then attached to every subsequent `queryPolicy` request.
233
340
  let nativeClient;
234
341
  if (resolvedConfig.mode !== "sdk-only") {
235
342
  nativeClient = (0, client_js_2.createNativeClient)({
236
343
  gateway: resolvedGatewayUrl,
237
344
  apiKey: resolvedApiKey,
238
- mode: resolvedConfig.mode === "napi-inprocess" ? "napi-inprocess" : "grpc-sidecar",
345
+ mode: resolvedConfig.mode === "napi-inprocess" ? "napi-inprocess" : "grpc-sidecar"
239
346
  });
347
+ }
348
+ const client = createClient(resolvedConfig, nativeClient);
349
+ const adapters = await registerAdapters(frameworks);
350
+ await startNetworkLayerIfNeeded(client, resolvedConfig);
351
+ if (nativeClient !== undefined) {
352
+ // AAASM-3403: register the agent over the native SDK→gateway gRPC call so
353
+ // the gateway issues a credential token (stored on this session) that
354
+ // unblocks subsequent policy queries. Advisory: a failed registration must
355
+ // not abort init — the agent proceeds unregistered and the proxy / eBPF
356
+ // layers remain authoritative.
357
+ try {
358
+ await nativeClient.register(buildRegisterOptions(resolvedConfig, frameworks));
359
+ }
360
+ catch (error) {
361
+ console.warn(`[agent-assembly] agent registration failed; proceeding unregistered: ${String(error)}`);
362
+ }
363
+ // Topology lineage metadata still flows as an audit event (parent / team /
364
+ // delegation), which `register` does not carry.
240
365
  nativeClient.sendEvent(buildRegistrationEvent(resolvedConfig));
241
366
  }
242
- const langChainHandler = await registerLangChainHandler(resolvedConfig, client, frameworks);
243
- const wrappedLangChainTools = await wrapLangChainTools(resolvedConfig, client, frameworks);
244
- const vercelAiSdkPatched = await patchDetectedVercelAiSdk(client, frameworks, resolvedConfig.agentId);
245
- const openAIAgentsPatched = await patchDetectedOpenAIAgents(client, frameworks);
246
- const langGraphPatched = await patchDetectedLangGraph(frameworks, resolvedConfig.agentId);
247
- const mastraPatched = await patchDetectedMastra(frameworks, resolvedConfig.agentId);
367
+ const patches = await applyFrameworkPatches(resolvedConfig, client, frameworks);
248
368
  return {
249
- activeAdapters: [
250
- ...new Set([
251
- ...adapters.map((adapter) => adapter.id),
252
- ...(langChainHandler ? ["langchain-js"] : []),
253
- ...(wrappedLangChainTools.length > 0 ? ["langchain-js"] : []),
254
- ...(vercelAiSdkPatched ? ["vercel-ai-sdk"] : []),
255
- ...(openAIAgentsPatched ? ["openai-agents"] : []),
256
- ...(langGraphPatched ? ["langgraph-js"] : []),
257
- ...(mastraPatched ? ["mastra"] : [])
258
- ])
259
- ],
260
- ...(resolvedConfig.parentAgentId !== undefined && { parentAgentId: resolvedConfig.parentAgentId }),
369
+ activeAdapters: buildActiveAdapters(adapters, patches),
370
+ ...(resolvedConfig.parentAgentId !== undefined && {
371
+ parentAgentId: resolvedConfig.parentAgentId
372
+ }),
261
373
  ...(resolvedConfig.teamId !== undefined && { teamId: resolvedConfig.teamId }),
262
- ...(resolvedConfig.delegationReason !== undefined && { delegationReason: resolvedConfig.delegationReason }),
263
- ...(resolvedConfig.spawnedByTool !== undefined && { spawnedByTool: resolvedConfig.spawnedByTool }),
264
- ...(resolvedConfig.enforcementMode !== undefined && { enforcementMode: resolvedConfig.enforcementMode }),
374
+ ...(resolvedConfig.delegationReason !== undefined && {
375
+ delegationReason: resolvedConfig.delegationReason
376
+ }),
377
+ ...(resolvedConfig.spawnedByTool !== undefined && {
378
+ spawnedByTool: resolvedConfig.spawnedByTool
379
+ }),
380
+ ...(resolvedConfig.enforcementMode !== undefined && {
381
+ enforcementMode: resolvedConfig.enforcementMode
382
+ }),
265
383
  shutdown: async () => {
266
384
  for (const adapter of adapters) {
267
385
  await adapter.shutdown?.();
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createNoopGatewayClient = createNoopGatewayClient;
4
+ exports.createNativeGatewayClient = createNativeGatewayClient;
4
5
  function createNoopGatewayClient(mode, httpBaseUrl) {
5
6
  return {
6
7
  mode,
7
- ...(httpBaseUrl !== undefined ? { httpBaseUrl } : {}),
8
+ ...(httpBaseUrl === undefined ? {} : { httpBaseUrl }),
8
9
  start: async () => undefined,
9
10
  close: async () => undefined,
10
11
  check: async () => ({ denied: false, pending: false }),
@@ -14,3 +15,64 @@ function createNoopGatewayClient(mode, httpBaseUrl) {
14
15
  scanPrompts: async () => undefined
15
16
  };
16
17
  }
18
+ /**
19
+ * Translate a governance check request into the native `queryPolicy` query
20
+ * shape (AAASM-3047). The runtime reads `agent_id`, `action_type`, and — for
21
+ * tool calls — `tool_name` / `args`.
22
+ */
23
+ function toNativeQuery(request, agentId) {
24
+ const query = {
25
+ agent_id: agentId ?? "",
26
+ action_type: request.action
27
+ };
28
+ if (request.toolName !== undefined) {
29
+ query.tool_name = request.toolName;
30
+ }
31
+ if (request.args !== undefined) {
32
+ query.args = request.args;
33
+ }
34
+ return query;
35
+ }
36
+ /**
37
+ * Gateway client backed by the in-process native runtime (AAASM-3050).
38
+ *
39
+ * `check()` asks a reachable `aa-runtime` for an authoritative verdict via the
40
+ * native `queryPolicy` primitive and maps it onto a `GatewayDecision`:
41
+ * - `deny` → `{ denied: true }` (the wrapper throws `PolicyViolationError`)
42
+ * - `pending` → `{ pending: true }` (routes to the approval path)
43
+ * - allow / redact / unspecified → `{ denied: false }`
44
+ *
45
+ * **Fail-open (security-critical):** the SDK is advisory, not a security
46
+ * boundary. The native primitive already returns `allow` when the runtime is
47
+ * unreachable or too slow; on top of that, any local fault while querying is
48
+ * swallowed here and resolves neutral, so a missing or degraded runtime never
49
+ * blocks the agent. The proxy / eBPF layers remain authoritative.
50
+ */
51
+ function createNativeGatewayClient(mode, nativeClient, agentId, httpBaseUrl) {
52
+ return {
53
+ mode,
54
+ ...(httpBaseUrl === undefined ? {} : { httpBaseUrl }),
55
+ start: async () => undefined,
56
+ close: async () => {
57
+ await nativeClient.close();
58
+ },
59
+ check: async (request) => {
60
+ try {
61
+ const verdict = await nativeClient.queryPolicy(toNativeQuery(request, agentId));
62
+ return {
63
+ denied: verdict.denied ?? false,
64
+ pending: verdict.pending ?? false,
65
+ ...(verdict.reason === undefined ? {} : { reason: verdict.reason })
66
+ };
67
+ }
68
+ catch {
69
+ // Fail open: a local fault talking to the runtime must never block.
70
+ return { denied: false, pending: false };
71
+ }
72
+ },
73
+ waitForApproval: async () => ({ denied: false }),
74
+ record: async () => undefined,
75
+ recordResult: async () => undefined,
76
+ scanPrompts: async () => undefined
77
+ };
78
+ }
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createNoopGatewayClient = void 0;
3
+ exports.createNoopGatewayClient = exports.createNativeGatewayClient = void 0;
4
4
  var client_js_1 = require("./client.js");
5
+ Object.defineProperty(exports, "createNativeGatewayClient", { enumerable: true, get: function () { return client_js_1.createNativeGatewayClient; } });
5
6
  Object.defineProperty(exports, "createNoopGatewayClient", { enumerable: true, get: function () { return client_js_1.createNoopGatewayClient; } });
@@ -52,9 +52,7 @@ function captureOriginalToolFactory(module) {
52
52
  if (typeof candidate !== "function") {
53
53
  return undefined;
54
54
  }
55
- if (!exports.vercelAiSdkPatchState.originalToolFactory) {
56
- exports.vercelAiSdkPatchState.originalToolFactory = candidate;
57
- }
55
+ exports.vercelAiSdkPatchState.originalToolFactory ??= candidate;
58
56
  return exports.vercelAiSdkPatchState.originalToolFactory;
59
57
  }
60
58
  function recordToolResultNonBlocking(gatewayClient, runId, output) {
@@ -76,7 +74,7 @@ function createWrappedExecute(originalExecute, description, gatewayClient, optio
76
74
  decision = await gatewayClient.check({
77
75
  action: "tool_call",
78
76
  toolName: description,
79
- args: args,
77
+ args,
80
78
  runId
81
79
  });
82
80
  }
@@ -140,7 +138,7 @@ async function patchVercelAiSdk(options) {
140
138
  module.tool = createPatchedToolFactory(originalToolFactory, options.gatewayClient, {
141
139
  approvalTimeoutMs: options.approvalTimeoutMs ?? 30_000,
142
140
  fallbackRunId: options.fallbackRunId ?? "vercel-ai-sdk",
143
- ...(options.agentId !== undefined ? { agentId: options.agentId } : {})
141
+ ...(options.agentId === undefined ? {} : { agentId: options.agentId })
144
142
  });
145
143
  exports.vercelAiSdkPatchState.isPatched = true;
146
144
  exports.vercelAiSdkPatchState.patchedModule = module;
@@ -1,9 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.patchLangChain = patchLangChain;
4
+ /**
5
+ * Intentional no-op stub: native-transport LangChain patching is not
6
+ * implemented. LangChain enforcement is performed in the SDK's callback layer
7
+ * (post-execution redaction) and wrapper layer (pre-execution deny) wired by
8
+ * `initAssembly`, not through this native hook — so there is nothing to patch
9
+ * here yet. Returns `false` (nothing patched) for every mode.
10
+ *
11
+ * The `client` parameter is retained to keep the adapter-registry hook
12
+ * signature (and the public `patchLangChain` export) uniform with the other
13
+ * `patch*` hooks; it is deliberately unused until native patching lands.
14
+ */
15
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- stub param kept for signature stability; see TSDoc above
4
16
  async function patchLangChain(client) {
5
- if (client.mode === "grpc-sidecar" || client.mode === "napi-inprocess") {
6
- return false;
7
- }
8
17
  return false;
9
18
  }
@@ -75,15 +75,18 @@ async function patchMastra(options) {
75
75
  exports.mastraPatchState.originalGenerate = originalGenerate;
76
76
  exports.mastraPatchState.patchedAgentClass = module.Agent;
77
77
  module.Agent.prototype.generate = function patchedGenerate(...args) {
78
- let result;
78
+ // The try guards only the *synchronous* setup (entering the async-context
79
+ // store and invoking the original); the returned promise is handed to the
80
+ // caller, which awaits it, so its rejection is the caller's to handle.
81
+ // Returning it directly (rather than via a local inside the try) avoids an
82
+ // unhandled-rejection footgun without changing timing or call count.
79
83
  try {
80
- result = (0, agent_context_store_js_1.runWithAgentId)(agentId, () => originalGenerate.apply(this, args));
84
+ return (0, agent_context_store_js_1.runWithAgentId)(agentId, () => originalGenerate.apply(this, args));
81
85
  }
82
86
  catch (e) {
83
87
  console.warn("[assembly] Mastra lineage patch error on generate; falling back:", e);
84
88
  return originalGenerate.apply(this, args);
85
89
  }
86
- return result;
87
90
  };
88
91
  // Wrap Workflow.prototype.execute if present
89
92
  if (module.Workflow?.prototype?.execute) {
@@ -91,15 +94,16 @@ async function patchMastra(options) {
91
94
  exports.mastraPatchState.originalExecute = originalExecute;
92
95
  exports.mastraPatchState.patchedWorkflowClass = module.Workflow;
93
96
  module.Workflow.prototype.execute = function patchedExecute(...args) {
94
- let result;
97
+ // See patchedGenerate above: the try guards only the synchronous setup;
98
+ // the returned promise is awaited by the caller, so returning it directly
99
+ // (not via a local) handles its rejection without altering behaviour.
95
100
  try {
96
- result = (0, agent_context_store_js_1.runWithAgentId)(agentId, () => originalExecute.apply(this, args));
101
+ return (0, agent_context_store_js_1.runWithAgentId)(agentId, () => originalExecute.apply(this, args));
97
102
  }
98
103
  catch (e) {
99
104
  console.warn("[assembly] Mastra lineage patch error on execute; falling back:", e);
100
105
  return originalExecute.apply(this, args);
101
106
  }
102
- return result;
103
107
  };
104
108
  }
105
109
  exports.mastraPatchState.isPatched = true;
@@ -54,9 +54,7 @@ function captureOriginalRunTool(agentClass) {
54
54
  if (!candidate) {
55
55
  return undefined;
56
56
  }
57
- if (!exports.openAIAgentsPatchState.originalRunTool) {
58
- exports.openAIAgentsPatchState.originalRunTool = candidate;
59
- }
57
+ exports.openAIAgentsPatchState.originalRunTool ??= candidate;
60
58
  return exports.openAIAgentsPatchState.originalRunTool;
61
59
  }
62
60
  function parseToolCallArguments(toolCall) {