@agent-assembly/sdk 0.0.1-beta.4 → 0.0.1-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +36 -23
  2. package/dist/cjs/core/init-assembly.js +5 -1
  3. package/dist/cjs/core/redact.js +63 -0
  4. package/dist/cjs/hooks/ai-sdk.js +43 -5
  5. package/dist/cjs/index.js +9 -1
  6. package/dist/cjs/native/client.js +25 -1
  7. package/dist/cjs/op-control.js +109 -21
  8. package/dist/cjs/wrappers/with-assembly.js +89 -32
  9. package/dist/esm/core/init-assembly.js +5 -1
  10. package/dist/esm/core/init-assembly.js.map +1 -1
  11. package/dist/esm/core/redact.js +59 -0
  12. package/dist/esm/core/redact.js.map +1 -0
  13. package/dist/esm/hooks/ai-sdk.js +43 -5
  14. package/dist/esm/hooks/ai-sdk.js.map +1 -1
  15. package/dist/esm/index.js +6 -0
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/native/client.js +24 -1
  18. package/dist/esm/native/client.js.map +1 -1
  19. package/dist/esm/op-control.js +73 -18
  20. package/dist/esm/op-control.js.map +1 -1
  21. package/dist/esm/wrappers/with-assembly.js +89 -32
  22. package/dist/esm/wrappers/with-assembly.js.map +1 -1
  23. package/dist/types/core/init-assembly.d.ts.map +1 -1
  24. package/dist/types/core/redact.d.ts +28 -0
  25. package/dist/types/core/redact.d.ts.map +1 -0
  26. package/dist/types/hooks/ai-sdk.d.ts +13 -0
  27. package/dist/types/hooks/ai-sdk.d.ts.map +1 -1
  28. package/dist/types/index.d.ts +4 -1
  29. package/dist/types/index.d.ts.map +1 -1
  30. package/dist/types/native/client.d.ts +11 -0
  31. package/dist/types/native/client.d.ts.map +1 -1
  32. package/dist/types/op-control.d.ts +43 -6
  33. package/dist/types/op-control.d.ts.map +1 -1
  34. package/dist/types/wrappers/index.d.ts +1 -1
  35. package/dist/types/wrappers/index.d.ts.map +1 -1
  36. package/dist/types/wrappers/with-assembly.d.ts +25 -0
  37. package/dist/types/wrappers/with-assembly.d.ts.map +1 -1
  38. package/native/aa-ffi-node/index.d.ts +8 -1
  39. package/package.json +18 -5
package/README.md CHANGED
@@ -122,6 +122,20 @@ system. The matrix is enforced by `.github/workflows/test-matrix.yml`:
122
122
  Older Node.js lines (≤ 16) are unsupported because the napi-rs ABI used by the native
123
123
  binding requires Node 18.18 or newer.
124
124
 
125
+ ## Framework compatibility
126
+
127
+ `initAssembly()` auto-detects and governs five optional framework integrations
128
+ (LangChain.js, LangGraph.js, Vercel AI SDK, Mastra, OpenAI Agents). The full table —
129
+ each framework's optional peer dependency, supported version range, and current status
130
+ (including the [known Vercel AI SDK caveat](https://lightning-dust-mite.atlassian.net/browse/AAASM-3532)) —
131
+ is the **authoritative** reference and lives on the docs site:
132
+ [Framework compatibility](https://ai-agent-assembly.github.io/node-sdk/compatibility-versioning/compatibility).
133
+
134
+ For the product-wide, cross-SDK index/hub that links every language SDK's matrix, see the
135
+ core documentation:
136
+ [Framework compatibility index](https://ai-agent-assembly.github.io/agent-assembly/stable/reference/framework-compatibility.html)
137
+ (the `/stable/` link goes live at GA).
138
+
125
139
  ## How it works
126
140
 
127
141
  The SDK is a thin TypeScript wrapper around the Agent Assembly Rust runtime. It reaches
@@ -136,16 +150,16 @@ call is checked against policy before it runs.
136
150
 
137
151
  ## What the package exports
138
152
 
139
- | Export | Purpose |
140
- | ------ | ------- |
141
- | `initAssembly(config)` | Set up governance and auto-wire detected frameworks. The main entrypoint. |
142
- | `withAssembly(tools, options)` | Lower-level wrapper to govern a tool map when you manage the gateway client yourself. |
143
- | `createNoopGatewayClient(mode)` | Build an allow-all `GatewayClient` for offline demos and tests, or as a base to wrap. |
144
- | `PolicyViolationError` | Thrown by a governed tool when the gateway client denies the call. |
145
- | `currentAgentId()`, `runWithAgentId()` | Read and set the active agent id in the async-context lineage store. |
146
- | `encodeAuditEvent()` / `decodeAuditEvent()` (and the call-stack codecs) | Encode and decode audit events to and from their wire shape. |
147
- | `findAasmBinary()`, `INSTALL_HINT` | Locate the bundled `aasm` runtime binary and the install hint shown when it is missing. |
148
- | `ENFORCEMENT_MODES` | The allowed `enforcementMode` values. |
153
+ | Export | Purpose |
154
+ | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
155
+ | `initAssembly(config)` | Set up governance and auto-wire detected frameworks. The main entrypoint. |
156
+ | `withAssembly(tools, options)` | Lower-level wrapper to govern a tool map when you manage the gateway client yourself. |
157
+ | `createNoopGatewayClient(mode)` | Build an allow-all `GatewayClient` for offline demos and tests, or as a base to wrap. |
158
+ | `PolicyViolationError` | Thrown by a governed tool when the gateway client denies the call. |
159
+ | `currentAgentId()`, `runWithAgentId()` | Read and set the active agent id in the async-context lineage store. |
160
+ | `encodeAuditEvent()` / `decodeAuditEvent()` (and the call-stack codecs) | Encode and decode audit events to and from their wire shape. |
161
+ | `findAasmBinary()`, `INSTALL_HINT` | Locate the bundled `aasm` runtime binary and the install hint shown when it is missing. |
162
+ | `ENFORCEMENT_MODES` | The allowed `enforcementMode` values. |
149
163
 
150
164
  Type-only exports (`AssemblyConfig`, `AssemblyContext`, `AssemblyMode`, `EnforcementMode`,
151
165
  `ToolMap`, `GatewayClient`, the `Gateway*` governance types, and friends) are documented in
@@ -157,11 +171,7 @@ the [API reference](https://ai-agent-assembly.github.io/node-sdk/api-reference).
157
171
  in-process policies you can build one yourself — no running gateway required:
158
172
 
159
173
  ```ts
160
- import {
161
- createNoopGatewayClient,
162
- withAssembly,
163
- type GatewayClient
164
- } from "@agent-assembly/sdk";
174
+ import { createNoopGatewayClient, withAssembly, type GatewayClient } from "@agent-assembly/sdk";
165
175
 
166
176
  // Allow-all client — handy for offline smoke tests:
167
177
  withAssembly(
@@ -285,15 +295,15 @@ and is re-published on every push to `master` via the `publish-docs.yml` workflo
285
295
  decisions it enforces are made by the core Rust runtime; the protocol it speaks is shared
286
296
  across all SDKs.
287
297
 
288
- | Project | What it is |
289
- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
290
- | [agent-assembly](https://github.com/ai-agent-assembly/agent-assembly) | Core Rust runtime — gateway, policy engine, proxy, eBPF, CLI (`aasm`). The protocol specification lives here. |
291
- | [Documentation site](https://ai-agent-assembly.github.io/agent-assembly-docs/) | Canonical, cross-repo documentation for the whole platform. |
292
- | [python-sdk](https://github.com/ai-agent-assembly/python-sdk) | Sibling SDK for Python. |
293
- | [go-sdk](https://github.com/ai-agent-assembly/go-sdk) | Sibling SDK for Go. |
298
+ | Project | What it is |
299
+ | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
300
+ | [agent-assembly](https://github.com/ai-agent-assembly/agent-assembly) | Core Rust runtime — gateway, policy engine, proxy, eBPF, CLI (`aasm`). The protocol specification lives here. |
301
+ | [Documentation site](https://ai-agent-assembly.github.io/agent-assembly-docs/) | Canonical, cross-repo documentation for the whole platform. |
302
+ | [python-sdk](https://github.com/ai-agent-assembly/python-sdk) | Sibling SDK for Python. |
303
+ | [go-sdk](https://github.com/ai-agent-assembly/go-sdk) | Sibling SDK for Go. |
294
304
  | [agent-assembly-examples](https://github.com/ai-agent-assembly/agent-assembly-examples) | Runnable examples — learn by running small, framework-specific Node.js/TypeScript (and Python/Go) samples for policy enforcement, approvals, audit, trace, and runtime workflows. |
295
- | [Release notes](https://github.com/ai-agent-assembly/node-sdk/releases) | Per-version changelog for this package. |
296
- | [Organization profile](https://github.com/ai-agent-assembly) | Index of every Agent Assembly repository and its status. |
305
+ | [Release notes](https://github.com/ai-agent-assembly/node-sdk/releases) | Per-version changelog for this package. |
306
+ | [Organization profile](https://github.com/ai-agent-assembly) | Index of every Agent Assembly repository and its status. |
297
307
 
298
308
  ## Support & security
299
309
 
@@ -304,5 +314,8 @@ across all SDKs.
304
314
  via the repository's
305
315
  [security advisories](https://github.com/ai-agent-assembly/node-sdk/security/advisories)
306
316
  page so a fix can be coordinated before disclosure.
317
+ - **Canonical package names + verifying your install** — see [SECURITY.md](./SECURITY.md)
318
+ for the authoritative `@agent-assembly/*` package list (to spot typosquats) and how to
319
+ verify npm provenance (`npm audit signatures`) and the per-release CycloneDX SBOM.
307
320
  - **Contributing** — see [CONTRIBUTING.md](./CONTRIBUTING.md) for environment setup, the
308
321
  adapter-authoring guide, and the test/commit conventions.
@@ -54,6 +54,7 @@ const openai_agents_detection_js_1 = require("../hooks/openai-agents-detection.j
54
54
  const openai_agents_js_1 = require("../hooks/openai-agents.js");
55
55
  const index_js_2 = require("../lineage/index.js");
56
56
  const gateway_resolver_js_1 = require("./gateway-resolver.js");
57
+ const redact_js_1 = require("./redact.js");
57
58
  const requireFromCwd = (0, node_module_1.createRequire)(`${process.cwd()}/`);
58
59
  /** Env-var fallback for ``gatewayUrl`` read at ``initAssembly`` entry. */
59
60
  exports.ENV_GATEWAY_URL = "AA_GATEWAY_URL";
@@ -358,7 +359,10 @@ async function initAssembly(config = {}) {
358
359
  await nativeClient.register(buildRegisterOptions(resolvedConfig, frameworks));
359
360
  }
360
361
  catch (error) {
361
- console.warn(`[agent-assembly] agent registration failed; proceeding unregistered: ${String(error)}`);
362
+ // Redact any Bearer/auth credential the error message might carry before
363
+ // it reaches the console — the apiKey/credentialToken must never be logged
364
+ // (AAASM-3645).
365
+ console.warn(`[agent-assembly] agent registration failed; proceeding unregistered: ${(0, redact_js_1.redactErrorMessage)(error)}`);
362
366
  }
363
367
  // Topology lineage metadata still flows as an audit event (parent / team /
364
368
  // delegation), which `register` does not carry.
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ /**
3
+ * Secret-redaction helpers for diagnostic / log output (AAASM-3645).
4
+ *
5
+ * The resolved `apiKey` and the proto `credentialToken` must never reach
6
+ * `console.*` or an accidental `JSON.stringify` dump. These helpers give the
7
+ * SDK a single, audited way to render config/diagnostics for logging with the
8
+ * credential fields stripped.
9
+ *
10
+ * NOTE: the generated `CheckActionRequest.toJSON()` (src/proto/generated) is
11
+ * wire-only — it serializes `credentialToken` for transport and must never be
12
+ * passed to a logger. Use {@link redactSecrets} on any object you intend to log.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.REDACTED = void 0;
16
+ exports.redactSecrets = redactSecrets;
17
+ exports.redactErrorMessage = redactErrorMessage;
18
+ /**
19
+ * Object keys (lower-cased) whose values are credentials and must never be
20
+ * logged. Matching is case-insensitive, so list the lower-case form only —
21
+ * `apiKey`, `apikey`, `API_KEY` all match `"apikey"`.
22
+ */
23
+ const SECRET_KEYS = new Set([
24
+ "apikey",
25
+ "api_key",
26
+ "credentialtoken",
27
+ "credential_token",
28
+ "authorization",
29
+ "token"
30
+ ]);
31
+ /** Placeholder substituted for any redacted credential value. */
32
+ exports.REDACTED = "<redacted>";
33
+ /**
34
+ * Return a deep copy of `value` with every credential-bearing field replaced by
35
+ * {@link REDACTED}, safe to pass to `console.*` / `JSON.stringify`. Matching is
36
+ * case-insensitive on the key name. Non-object inputs are returned unchanged.
37
+ */
38
+ function redactSecrets(value) {
39
+ if (Array.isArray(value)) {
40
+ return value.map((item) => redactSecrets(item));
41
+ }
42
+ if (value !== null && typeof value === "object") {
43
+ const out = {};
44
+ for (const [key, val] of Object.entries(value)) {
45
+ out[key] = SECRET_KEYS.has(key.toLowerCase()) ? exports.REDACTED : redactSecrets(val);
46
+ }
47
+ return out;
48
+ }
49
+ return value;
50
+ }
51
+ /**
52
+ * Render an unknown error for a log message with any `Bearer <token>` / API-key
53
+ * substring scrubbed. Defends the registration-failure warning path: a wrapped
54
+ * transport error could in principle carry an auth header in its message, so we
55
+ * strip the bearer credential before it reaches `console.*` (AAASM-3645).
56
+ */
57
+ function redactErrorMessage(error) {
58
+ const raw = String(error);
59
+ // Replace the credential that follows a `Bearer ` / `Authorization:` marker.
60
+ return raw
61
+ .replace(/(Bearer\s+)[\w.\-+/=]+/gi, `$1${exports.REDACTED}`)
62
+ .replace(/(Authorization\s*[:=]\s*)\S+/gi, `$1${exports.REDACTED}`);
63
+ }
@@ -45,7 +45,8 @@ const agent_context_store_js_1 = require("../lineage/agent-context-store.js");
45
45
  exports.vercelAiSdkPatchState = {
46
46
  isPatched: false,
47
47
  originalToolFactory: undefined,
48
- patchedModule: undefined
48
+ patchedModule: undefined,
49
+ mutatedOriginal: false
49
50
  };
50
51
  function captureOriginalToolFactory(module) {
51
52
  const candidate = module.tool;
@@ -112,6 +113,35 @@ function createPatchedToolFactory(originalToolFactory, gatewayClient, options) {
112
113
  };
113
114
  };
114
115
  }
116
+ /**
117
+ * Install `governed` as the module's `tool` factory without ever assigning to a
118
+ * frozen ESM namespace.
119
+ *
120
+ * A real `ai` package loaded via `import()` is an ES module: its namespace is an
121
+ * exotic object whose named exports are non-writable, so `module.tool = …` throws
122
+ * `Cannot assign to read only property 'tool'` (AAASM-3532). We therefore attempt
123
+ * the in-place assignment only as a fast path for writable plain objects (the
124
+ * shape used by the unit suite's `loadModule` fakes) and fall back to a mutable
125
+ * **shim copy** for the frozen-namespace case — the same `{ tool: aiModule.tool }`
126
+ * shim the AAASM-3525 integration driver proved works. The returned module is what
127
+ * downstream consumers read the governed factory from (`patchedModule.tool`).
128
+ */
129
+ function applyGovernedToolFactory(module, governed) {
130
+ if (Object.isExtensible(module)) {
131
+ try {
132
+ module.tool = governed;
133
+ return { patchedModule: module, mutatedOriginal: true };
134
+ }
135
+ catch {
136
+ // Some non-extensible-but-reported-extensible exotic objects still reject
137
+ // the write; fall through to the shim copy below.
138
+ }
139
+ }
140
+ return {
141
+ patchedModule: { ...module, tool: governed },
142
+ mutatedOriginal: false
143
+ };
144
+ }
115
145
  async function loadVercelAiSdkModule() {
116
146
  try {
117
147
  const moduleName = "ai";
@@ -135,13 +165,15 @@ async function patchVercelAiSdk(options) {
135
165
  if (!originalToolFactory) {
136
166
  return false;
137
167
  }
138
- module.tool = createPatchedToolFactory(originalToolFactory, options.gatewayClient, {
168
+ const governed = createPatchedToolFactory(originalToolFactory, options.gatewayClient, {
139
169
  approvalTimeoutMs: options.approvalTimeoutMs ?? 30_000,
140
170
  fallbackRunId: options.fallbackRunId ?? "vercel-ai-sdk",
141
171
  ...(options.agentId === undefined ? {} : { agentId: options.agentId })
142
172
  });
173
+ const { patchedModule, mutatedOriginal } = applyGovernedToolFactory(module, governed);
143
174
  exports.vercelAiSdkPatchState.isPatched = true;
144
- exports.vercelAiSdkPatchState.patchedModule = module;
175
+ exports.vercelAiSdkPatchState.patchedModule = patchedModule;
176
+ exports.vercelAiSdkPatchState.mutatedOriginal = mutatedOriginal;
145
177
  return true;
146
178
  }
147
179
  function unpatchVercelAiSdk() {
@@ -154,9 +186,15 @@ function unpatchVercelAiSdk() {
154
186
  if (!exports.vercelAiSdkPatchState.originalToolFactory) {
155
187
  return false;
156
188
  }
157
- exports.vercelAiSdkPatchState.patchedModule.tool =
158
- exports.vercelAiSdkPatchState.originalToolFactory;
189
+ // Only restore when we mutated a writable module in place. For the frozen-ESM
190
+ // shim path the original `ai` namespace was never touched, so there is nothing
191
+ // to write back — and attempting it would re-throw the AAASM-3532 crash.
192
+ if (exports.vercelAiSdkPatchState.mutatedOriginal) {
193
+ exports.vercelAiSdkPatchState.patchedModule.tool =
194
+ exports.vercelAiSdkPatchState.originalToolFactory;
195
+ }
159
196
  exports.vercelAiSdkPatchState.isPatched = false;
160
197
  exports.vercelAiSdkPatchState.patchedModule = undefined;
198
+ exports.vercelAiSdkPatchState.mutatedOriginal = false;
161
199
  return true;
162
200
  }
package/dist/cjs/index.js CHANGED
@@ -14,11 +14,19 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.PolicyViolationError = exports.createNoopGatewayClient = exports.findAasmBinary = exports.INSTALL_HINT = exports.runWithAgentId = exports.currentAgentId = exports.encodeCallStackNode = exports.encodeAuditEvent = exports.decodeCallStackNode = exports.decodeAuditEvent = exports.ENFORCEMENT_MODES = exports.withAssembly = exports.initAssembly = void 0;
17
+ exports.PolicyViolationError = exports.createNoopGatewayClient = exports.findAasmBinary = exports.INSTALL_HINT = exports.runWithAgentId = exports.currentAgentId = exports.encodeCallStackNode = exports.encodeAuditEvent = exports.decodeCallStackNode = exports.decodeAuditEvent = exports.ENFORCEMENT_MODES = exports.OpTerminatedError = exports.OpControlSubscriber = exports.withAssembly = exports.initAssembly = void 0;
18
18
  var init_assembly_js_1 = require("./core/init-assembly.js");
19
19
  Object.defineProperty(exports, "initAssembly", { enumerable: true, get: function () { return init_assembly_js_1.initAssembly; } });
20
20
  var index_js_1 = require("./wrappers/index.js");
21
21
  Object.defineProperty(exports, "withAssembly", { enumerable: true, get: function () { return index_js_1.withAssembly; } });
22
+ // Live op-control consumer (AAASM-3491). Subscribes to the gateway's
23
+ // OpControlStream and exposes per-op cooperative-pause / fast-fail-terminate;
24
+ // pass the subscriber as `withAssembly(..., { opControl })` so an operator
25
+ // terminate/pause reaches a running tool through the SDK tool path.
26
+ var op_control_js_1 = require("./op-control.js");
27
+ Object.defineProperty(exports, "OpControlSubscriber", { enumerable: true, get: function () { return op_control_js_1.OpControlSubscriber; } });
28
+ var op_terminated_error_js_1 = require("./errors/op-terminated-error.js");
29
+ Object.defineProperty(exports, "OpTerminatedError", { enumerable: true, get: function () { return op_terminated_error_js_1.OpTerminatedError; } });
22
30
  var index_js_2 = require("./types/index.js");
23
31
  Object.defineProperty(exports, "ENFORCEMENT_MODES", { enumerable: true, get: function () { return index_js_2.ENFORCEMENT_MODES; } });
24
32
  var index_js_3 = require("./audit/index.js");
@@ -4,9 +4,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.NativeDisconnectError = exports.NativeRegisterError = exports.NativeQueryPolicyError = exports.NativeSendEventError = exports.NativeConnectError = void 0;
7
+ exports.resolveSdkVersion = resolveSdkVersion;
7
8
  exports.createNativeClient = createNativeClient;
8
9
  const node_module_1 = require("node:module");
9
10
  const node_path_1 = __importDefault(require("node:path"));
11
+ /**
12
+ * Resolve the installed `@agent-assembly/sdk` package version, or `undefined`.
13
+ *
14
+ * Forwarded into the native `connect` so the user-facing npm package version —
15
+ * not the shared `aa-sdk-client` crate version — is what gets signed into the
16
+ * runtime handshake, giving accurate downgrade detection (AAASM-3683).
17
+ * `undefined` lets the native shim fall back to the crate version (no
18
+ * regression vs AAASM-3666). Uses `createRequire(<cwd>/package.json)`, the same
19
+ * ESM/CJS-safe pattern as the native-binding and runtime-binary resolvers.
20
+ */
21
+ function resolveSdkVersion() {
22
+ try {
23
+ const requireFromHere = (0, node_module_1.createRequire)(node_path_1.default.resolve(process.cwd(), "package.json"));
24
+ const pkg = requireFromHere("@agent-assembly/sdk/package.json");
25
+ return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : undefined;
26
+ }
27
+ catch {
28
+ return undefined;
29
+ }
30
+ }
10
31
  const NATIVE_BINDING_SINGLETON_KEY = Symbol.for("@agent-assembly/sdk/native-binding");
11
32
  const ERROR_CONNECT = "AA_ERR_CONNECT";
12
33
  const ERROR_SEND_EVENT = "AA_ERR_SEND_EVENT";
@@ -122,9 +143,12 @@ function createNativeClient(options) {
122
143
  let handlePromise;
123
144
  let activeHandle;
124
145
  let pendingSendError;
146
+ const sdkVersion = resolveSdkVersion();
125
147
  const getHandle = async () => {
126
148
  handlePromise ??= binding
127
- .connect(socketPath)
149
+ // Forward the npm package version so it is signed into the handshake
150
+ // (AAASM-3683); agent id is wired at register time, not connect.
151
+ .connect(socketPath, undefined, sdkVersion)
128
152
  .then((handle) => {
129
153
  activeHandle = handle;
130
154
  return handle;
@@ -20,13 +20,57 @@
20
20
  * - Auto-wiring into the existing `GatewayClient` / adapter hooks
21
21
  * (separate sub-task when the adapter surface is stable).
22
22
  */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || (function () {
40
+ var ownKeys = function(o) {
41
+ ownKeys = Object.getOwnPropertyNames || function (o) {
42
+ var ar = [];
43
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
44
+ return ar;
45
+ };
46
+ return ownKeys(o);
47
+ };
48
+ return function (mod) {
49
+ if (mod && mod.__esModule) return mod;
50
+ var result = {};
51
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
52
+ __setModuleDefault(result, mod);
53
+ return result;
54
+ };
55
+ })();
23
56
  Object.defineProperty(exports, "__esModule", { value: true });
24
57
  exports.OpControlSubscriber = void 0;
25
58
  exports.gatewayHostOf = gatewayHostOf;
26
59
  exports.resolveOpControlCredentials = resolveOpControlCredentials;
27
- const grpc_js_1 = require("@grpc/grpc-js");
28
60
  const op_terminated_error_js_1 = require("./errors/op-terminated-error.js");
29
- const policy_js_1 = require("./proto/generated/policy.js");
61
+ /**
62
+ * Numeric `OpControlSignal` values, inlined to keep this module grpc-free at load.
63
+ *
64
+ * `OpControlSignal` lives in `./proto/generated/policy.js`, which imports
65
+ * `@grpc/grpc-js` at module scope; importing the enum as a *value* would defeat
66
+ * the lazy-load. These are the stable protobuf wire numbers (UNSPECIFIED=0,
67
+ * PAUSE=1, RESUME=2, TERMINATE=3) — `msg.signal` is compared against them
68
+ * numerically in {@link OpControlSubscriber.dispatch}. The `OpControlSignal`
69
+ * type is still imported (type-only) for signatures.
70
+ */
71
+ const SIGNAL_PAUSE = 1;
72
+ const SIGNAL_RESUME = 2;
73
+ const SIGNAL_TERMINATE = 3;
30
74
  /**
31
75
  * Hosts treated as loopback for the secure-by-default transport decision.
32
76
  * A loopback gateway is the local dev-mode CP, where plaintext gRPC is the
@@ -68,45 +112,86 @@ function isLoopbackTarget(gatewayUrl) {
68
112
  * target gets plaintext (local dev gateway), a remote target gets TLS, and a
69
113
  * remote target is only allowed plaintext when the caller sets `allowInsecure`.
70
114
  *
115
+ * `grpcCredentials` is injected (rather than imported at module scope) so this
116
+ * module does not eagerly load `@grpc/grpc-js` — see the module header. The
117
+ * real-connect path passes the lazily-imported `credentials` namespace.
118
+ *
71
119
  * @throws never — returns the chosen {@link ChannelCredentials}.
72
120
  */
73
- function resolveOpControlCredentials(gatewayUrl, opts) {
121
+ function resolveOpControlCredentials(gatewayUrl, opts, grpcCredentials) {
74
122
  if (opts.credentials)
75
123
  return opts.credentials;
76
124
  if (isLoopbackTarget(gatewayUrl))
77
- return grpc_js_1.credentials.createInsecure();
125
+ return grpcCredentials.createInsecure();
78
126
  if (opts.allowInsecure)
79
- return grpc_js_1.credentials.createInsecure();
80
- return grpc_js_1.credentials.createSsl();
127
+ return grpcCredentials.createInsecure();
128
+ return grpcCredentials.createSsl();
81
129
  }
82
130
  class OpControlSubscriber {
83
- client;
131
+ /**
132
+ * `null` until the channel is opened. On the test-seam (`clientFactory`) path
133
+ * it is set synchronously in {@link connect}; on the real-connect path it is
134
+ * set asynchronously once `@grpc/grpc-js` + `PolicyServiceClient` have been
135
+ * lazily imported (see {@link openRealChannel}).
136
+ */
137
+ client = null;
84
138
  agent;
85
139
  ops = new Map();
86
140
  call = null;
87
141
  alive = true;
88
- constructor(client, agent) {
89
- this.client = client;
142
+ /** Set once {@link close} is called before the async channel finishes opening. */
143
+ closed = false;
144
+ constructor(agent) {
90
145
  this.agent = agent;
91
146
  }
92
- /** Open the gRPC channel + subscription stream and start the reader. */
147
+ /**
148
+ * Open the gRPC channel + subscription stream and start the reader.
149
+ *
150
+ * Returns synchronously. On the real-connect path the channel is opened
151
+ * asynchronously — `@grpc/grpc-js` and `PolicyServiceClient` are loaded lazily
152
+ * (`await import`) so that importing this module never eagerly pulls grpc (see
153
+ * the module header). The test seam (`clientFactory`) opens synchronously and
154
+ * never touches grpc.
155
+ */
93
156
  static connect(gatewayUrl, opts) {
94
157
  const agent = {
95
158
  orgId: opts.orgId,
96
159
  teamId: opts.teamId,
97
- agentId: opts.agentId,
160
+ agentId: opts.agentId
98
161
  };
99
- const client = opts.clientFactory
100
- ? opts.clientFactory()
101
- : new policy_js_1.PolicyServiceClient(gatewayUrl, resolveOpControlCredentials(gatewayUrl, opts));
102
- const subscriber = new OpControlSubscriber(client, agent);
103
- subscriber.start();
162
+ const subscriber = new OpControlSubscriber(agent);
163
+ if (opts.clientFactory) {
164
+ subscriber.client = opts.clientFactory();
165
+ subscriber.start();
166
+ }
167
+ else {
168
+ // Real channel: defer grpc loading to the dynamic-import path. Errors
169
+ // surface as a dead stream so callers see `streamAlive() === false`.
170
+ void subscriber.openRealChannel(gatewayUrl, opts).catch(() => {
171
+ subscriber.markStreamDead();
172
+ });
173
+ }
104
174
  return subscriber;
105
175
  }
176
+ /**
177
+ * Lazily import grpc + the policy client, build the real client, and start the
178
+ * reader. Kept off the module's import graph so `import '@agent-assembly/sdk'`
179
+ * stays grpc-free until a subscriber actually opens a live channel.
180
+ */
181
+ async openRealChannel(gatewayUrl, opts) {
182
+ const { credentials } = await Promise.resolve().then(() => __importStar(require("@grpc/grpc-js")));
183
+ const { PolicyServiceClient } = await Promise.resolve().then(() => __importStar(require("./proto/generated/policy.js")));
184
+ if (this.closed)
185
+ return; // close() raced ahead of the async open.
186
+ this.client = new PolicyServiceClient(gatewayUrl, resolveOpControlCredentials(gatewayUrl, opts, credentials));
187
+ this.start();
188
+ }
106
189
  /** Open the stream and wire reader handlers. Public so tests can call
107
190
  * directly after constructing with a hand-rolled client.
108
191
  */
109
192
  start() {
193
+ if (!this.client)
194
+ return;
110
195
  this.call = this.client.opControlStream({ agentId: this.agent });
111
196
  this.call.on("data", (msg) => this.dispatch(msg));
112
197
  this.call.on("error", () => this.markStreamDead());
@@ -115,14 +200,14 @@ class OpControlSubscriber {
115
200
  dispatch(msg) {
116
201
  const state = this.slot(msg.opId);
117
202
  switch (msg.signal) {
118
- case policy_js_1.OpControlSignal.OP_CONTROL_SIGNAL_PAUSE:
203
+ case SIGNAL_PAUSE:
119
204
  state.paused = true;
120
205
  break;
121
- case policy_js_1.OpControlSignal.OP_CONTROL_SIGNAL_RESUME:
206
+ case SIGNAL_RESUME:
122
207
  state.paused = false;
123
208
  this.flushResolvers(state);
124
209
  break;
125
- case policy_js_1.OpControlSignal.OP_CONTROL_SIGNAL_TERMINATE:
210
+ case SIGNAL_TERMINATE:
126
211
  state.terminated = true;
127
212
  this.flushResolvers(state);
128
213
  break;
@@ -194,10 +279,13 @@ class OpControlSubscriber {
194
279
  streamAlive() {
195
280
  return this.alive;
196
281
  }
197
- /** Cancel the stream and clean up. */
282
+ /** Cancel the stream and clean up. Safe to call before the async real-channel
283
+ * open has completed — it flags `closed` so the pending open bails out.
284
+ */
198
285
  close() {
286
+ this.closed = true;
199
287
  this.call?.cancel();
200
- this.client.close?.();
288
+ this.client?.close?.();
201
289
  this.markStreamDead();
202
290
  }
203
291
  }