@agent-assembly/sdk 0.0.1-beta.4 → 0.0.1-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -23
- package/dist/cjs/core/init-assembly.js +5 -1
- package/dist/cjs/core/redact.js +63 -0
- package/dist/cjs/hooks/ai-sdk.js +43 -5
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/native/client.js +25 -1
- package/dist/cjs/op-control.js +109 -21
- package/dist/cjs/wrappers/with-assembly.js +89 -32
- package/dist/esm/core/init-assembly.js +5 -1
- package/dist/esm/core/init-assembly.js.map +1 -1
- package/dist/esm/core/redact.js +59 -0
- package/dist/esm/core/redact.js.map +1 -0
- package/dist/esm/hooks/ai-sdk.js +43 -5
- package/dist/esm/hooks/ai-sdk.js.map +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/native/client.js +24 -1
- package/dist/esm/native/client.js.map +1 -1
- package/dist/esm/op-control.js +73 -18
- package/dist/esm/op-control.js.map +1 -1
- package/dist/esm/wrappers/with-assembly.js +89 -32
- package/dist/esm/wrappers/with-assembly.js.map +1 -1
- package/dist/types/core/init-assembly.d.ts.map +1 -1
- package/dist/types/core/redact.d.ts +28 -0
- package/dist/types/core/redact.d.ts.map +1 -0
- package/dist/types/hooks/ai-sdk.d.ts +13 -0
- package/dist/types/hooks/ai-sdk.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/native/client.d.ts +11 -0
- package/dist/types/native/client.d.ts.map +1 -1
- package/dist/types/op-control.d.ts +43 -6
- package/dist/types/op-control.d.ts.map +1 -1
- package/dist/types/wrappers/index.d.ts +1 -1
- package/dist/types/wrappers/index.d.ts.map +1 -1
- package/dist/types/wrappers/with-assembly.d.ts +25 -0
- package/dist/types/wrappers/with-assembly.d.ts.map +1 -1
- package/native/aa-ffi-node/index.d.ts +8 -1
- 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
|
|
140
|
-
|
|
|
141
|
-
| `initAssembly(config)`
|
|
142
|
-
| `withAssembly(tools, options)`
|
|
143
|
-
| `createNoopGatewayClient(mode)`
|
|
144
|
-
| `PolicyViolationError`
|
|
145
|
-
| `currentAgentId()`, `runWithAgentId()`
|
|
146
|
-
| `encodeAuditEvent()` / `decodeAuditEvent()` (and the call-stack codecs) | Encode and decode audit events to and from their wire shape.
|
|
147
|
-
| `findAasmBinary()`, `INSTALL_HINT`
|
|
148
|
-
| `ENFORCEMENT_MODES`
|
|
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
|
|
289
|
-
|
|
|
290
|
-
| [agent-assembly](https://github.com/ai-agent-assembly/agent-assembly)
|
|
291
|
-
| [Documentation site](https://ai-agent-assembly.github.io/agent-assembly-docs/)
|
|
292
|
-
| [python-sdk](https://github.com/ai-agent-assembly/python-sdk)
|
|
293
|
-
| [go-sdk](https://github.com/ai-agent-assembly/go-sdk)
|
|
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)
|
|
296
|
-
| [Organization profile](https://github.com/ai-agent-assembly)
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/cjs/hooks/ai-sdk.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
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;
|
package/dist/cjs/op-control.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
125
|
+
return grpcCredentials.createInsecure();
|
|
78
126
|
if (opts.allowInsecure)
|
|
79
|
-
return
|
|
80
|
-
return
|
|
127
|
+
return grpcCredentials.createInsecure();
|
|
128
|
+
return grpcCredentials.createSsl();
|
|
81
129
|
}
|
|
82
130
|
class OpControlSubscriber {
|
|
83
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
/**
|
|
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
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
203
|
+
case SIGNAL_PAUSE:
|
|
119
204
|
state.paused = true;
|
|
120
205
|
break;
|
|
121
|
-
case
|
|
206
|
+
case SIGNAL_RESUME:
|
|
122
207
|
state.paused = false;
|
|
123
208
|
this.flushResolvers(state);
|
|
124
209
|
break;
|
|
125
|
-
case
|
|
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
|
|
288
|
+
this.client?.close?.();
|
|
201
289
|
this.markStreamDead();
|
|
202
290
|
}
|
|
203
291
|
}
|