@haikeilabs/agentware 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,6 +44,50 @@ Keep channel ingress, identity verification, delegated credentials, and
44
44
  domain-specific authorization in the host application. Agentware is not a
45
45
  policy engine; a policy gate can wrap this same boundary later.
46
46
 
47
+ ## kei-proxy policy evaluator
48
+
49
+ `KeiProxyEvaluator` runs `kei-proxy authorize` before each tool call and fails
50
+ closed on anything other than an explicit `allow`/`permit` with exit 0.
51
+ `evaluate` is asynchronous: the event loop keeps serving other requests while
52
+ kei-proxy answers. Await the decision before running the tool.
53
+
54
+ `KeiProxyAuthorizeClient` spawns the binary by absolute path (a bare name is
55
+ resolved once against the parent `PATH`) with an allowlisted environment only:
56
+ `KEI_RUNTIME_TOKEN`, `KEI_RUNTIME_CONTROL_PLANE_URL`, `KEI_RUNTIME_VERSION` and
57
+ the other `AUTHORIZE_CHILD_ENV_ALLOWLIST` names. No `PATH`, no `HOME`, and the
58
+ token never goes in argv. The child runs in its own process group, and a
59
+ timeout SIGKILLs the whole group. Set `expectedSha256` to pin the binary: before
60
+ every spawn it is opened with `O_NOFOLLOW` and re-hashed, and any drift denies
61
+ with `kei-proxy pin_mismatch`.
62
+
63
+ ```typescript
64
+ import { Action, KeiProxyAuthorizeClient, KeiProxyEvaluator } from "@haikeilabs/agentware";
65
+
66
+ const evaluator = new KeiProxyEvaluator(
67
+ new KeiProxyAuthorizeClient({
68
+ executable: "/usr/local/bin/kei-proxy",
69
+ expectedSha256: process.env.KEI_PROXY_SHA256, // optional pin
70
+ timeoutMs: 10_000,
71
+ }),
72
+ );
73
+
74
+ const decision = await evaluator.evaluate(
75
+ "github.get_issue",
76
+ { owner: "acme", repo: "pipe" },
77
+ caller,
78
+ );
79
+ if (decision.enrollment) {
80
+ // deny + enrollment claim link: reply to the user privately; never log it.
81
+ }
82
+ if (decision.action !== Action.ALLOW) return;
83
+ ```
84
+
85
+ Reasons read `kei-proxy <class>[: <detail>]` with a class from
86
+ `KEI_PROXY_REASON_CLASSES`. Because `evaluate` returns a `Promise`, the
87
+ evaluator is not a synchronous `PolicyEvaluator` and cannot be passed to
88
+ `MiddlewareImpl.withPolicy`. The behaviour matches the Python and Go ports
89
+ case for case; see `docs/kei-proxy-evaluator-parity.md`.
90
+
47
91
  ## Maintainer releases
48
92
 
49
93
  Releases are deliberately manual and use npm staged publishing. Do not run
@@ -0,0 +1,55 @@
1
+ import { type KeiProxyAuthorizationClient, type KeiProxyAuthorizeRequest } from "./evaluator.js";
2
+ /**
3
+ * Parent variables the kei-proxy child may inherit. PATH and HOME are
4
+ * deliberately absent, as are KEI_PROXY_* identity variables: identity
5
+ * travels as flags.
6
+ */
7
+ export declare const AUTHORIZE_CHILD_ENV_ALLOWLIST: readonly ["TZ", "KEI_RUNTIME_TOKEN", "KEI_RUNTIME_CONTROL_PLANE_URL", "KEI_RUNTIME_VERSION", "KEI_API_URL", "KEI_PROXY_REGISTRY", "KEI_PROXY_AUDIT", "KEI_PROXY_AUDIT_MAX_BYTES", "KEI_PROXY_PROVIDER_USER", "KEI_CREDENTIAL_STORE_INSTALLATION_ID", "KEI_AWS_SECRETS_MANAGER_ENDPOINT"];
8
+ export declare const DEFAULT_AUTHORIZE_TIMEOUT_MS = 10000;
9
+ export interface KeiProxyAuthorizeClientOptions {
10
+ /**
11
+ * Path or name of the kei-proxy binary. Default `kei-proxy`. A bare name is
12
+ * resolved once against the parent PATH (`env.PATH`, else `process.env.PATH`);
13
+ * the child is always spawned by absolute path.
14
+ */
15
+ executable?: string;
16
+ /**
17
+ * Expected SHA-256 of the binary (64 hex digits). When set, `executable` must
18
+ * be an absolute, symlink-free path, and the binary is re-hashed before
19
+ * every spawn; a mismatch denies with `pin_mismatch`.
20
+ */
21
+ expectedSha256?: string;
22
+ /** Milliseconds to wait for one authorize call. Default 10s. */
23
+ timeoutMs?: number;
24
+ /** Parent environment to draw allowlisted variables from. Default `process.env`. */
25
+ env?: Readonly<Record<string, string | undefined>>;
26
+ /** Variables passed to the child verbatim, for settings outside the allowlist. */
27
+ extraEnv?: Readonly<Record<string, string>>;
28
+ }
29
+ /**
30
+ * Runs `kei-proxy authorize` once per call without blocking the event loop.
31
+ * The runtime token reaches the child only through its environment, never argv.
32
+ */
33
+ export declare class KeiProxyAuthorizeClient implements KeiProxyAuthorizationClient {
34
+ /** Absolute path to spawn, or undefined when a bare name was not found. */
35
+ private readonly executable;
36
+ private readonly expectedDigest;
37
+ private readonly timeoutMs;
38
+ private readonly env?;
39
+ private readonly extraEnv;
40
+ constructor(options?: KeiProxyAuthorizeClientOptions);
41
+ /** The exact environment the kei-proxy child receives. */
42
+ childEnv(): Record<string, string>;
43
+ authorize(request: KeiProxyAuthorizeRequest): Promise<Record<string, unknown>>;
44
+ /** Spawn once in a new process group; resolve with stdout and exit code. */
45
+ private run;
46
+ }
47
+ /**
48
+ * The argv (after the executable) for one authorize call. `agentId` and
49
+ * `workspaceId` are not sent: kei-proxy (>= 0.1.11) resolves the agent from
50
+ * the runtime installation and workspace scope from the runtime token.
51
+ */
52
+ export declare function keiProxyAuthorizeArgv(request: KeiProxyAuthorizeRequest): string[];
53
+ /** Validate one authorize result against the exit-code contract. */
54
+ export declare function parseKeiProxyAuthorizeOutput(stdout: string, exitCode: number): Record<string, unknown>;
55
+ //# sourceMappingURL=authorizeClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorizeClient.d.ts","sourceRoot":"","sources":["../../src/kei/authorizeClient.ts"],"names":[],"mappings":"AAgCA,OAAO,EAGL,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC9B,MAAM,gBAAgB,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,0RAYhC,CAAC;AAEX,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAWnD,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IACnD,kFAAkF;IAClF,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,qBAAa,uBAAwB,YAAW,2BAA2B;IACzE,2EAA2E;IAC3E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAA+C;IACpE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;gBAEhD,OAAO,GAAE,8BAAmC;IAoBxD,0DAA0D;IAC1D,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAU5B,SAAS,CACb,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAyBnC,4EAA4E;IAC5E,OAAO,CAAC,GAAG;CAgGZ;AAyFD;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,wBAAwB,GAChC,MAAM,EAAE,CA6BV;AAED,oEAAoE;AACpE,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+CzB"}
@@ -0,0 +1,349 @@
1
+ /**
2
+ * `KeiProxyAuthorizeClient`: runs `kei-proxy authorize` for `KeiProxyEvaluator`.
3
+ *
4
+ * TypeScript port of `pedro_agentware.kei.authorize_client`. The kei-proxy
5
+ * contract (kei-connector-runtime `authorize.go`): stdout is one JSON object;
6
+ * exit 0 for allow / enrollment_required, exit 1 for deny, and exit 1 with
7
+ * nothing on stdout for every error.
8
+ *
9
+ * Security contract (`fixtures/kei/authorize-cases.v1.json`):
10
+ *
11
+ * - The child is spawned asynchronously, so the harness's event loop keeps
12
+ * running while kei-proxy answers.
13
+ * - The child environment is exactly the allowlisted parent variables plus
14
+ * explicit `extraEnv`: no PATH, no HOME. The executable is resolved to an
15
+ * absolute path in the parent, once, at construction.
16
+ * - With `expectedSha256` the binary is re-proved before every spawn: the
17
+ * path must equal its realpath, it is opened with O_NOFOLLOW, must be a
18
+ * regular file, and its SHA-256 is streamed from that descriptor and compared
19
+ * in constant time. Any drift denies (`pin_mismatch`) without spawning. What
20
+ * remains is the hash-then-exec race; close it by making the binary's
21
+ * directory and its ancestors writable only by the deploy owner.
22
+ * - The child runs in its own process group; a timeout or oversized stdout
23
+ * SIGKILLs the whole group.
24
+ * - Proxy stdout can carry a credential or an enrollment claim link and stderr
25
+ * is free-form, so stderr is never read and neither is ever logged or copied
26
+ * into an error message.
27
+ */
28
+ import { spawn } from "node:child_process";
29
+ import { createHash, timingSafeEqual } from "node:crypto";
30
+ import { accessSync, constants as fsConstants, statSync } from "node:fs";
31
+ import { open, realpath } from "node:fs/promises";
32
+ import { delimiter, isAbsolute, join, resolve } from "node:path";
33
+ import { KEI_PROXY_AFFIRMATIVE_DECISIONS, KeiProxyAuthorizeError, } from "./evaluator.js";
34
+ /**
35
+ * Parent variables the kei-proxy child may inherit. PATH and HOME are
36
+ * deliberately absent, as are KEI_PROXY_* identity variables: identity
37
+ * travels as flags.
38
+ */
39
+ export const AUTHORIZE_CHILD_ENV_ALLOWLIST = [
40
+ "TZ",
41
+ "KEI_RUNTIME_TOKEN",
42
+ "KEI_RUNTIME_CONTROL_PLANE_URL",
43
+ "KEI_RUNTIME_VERSION",
44
+ "KEI_API_URL",
45
+ "KEI_PROXY_REGISTRY",
46
+ "KEI_PROXY_AUDIT",
47
+ "KEI_PROXY_AUDIT_MAX_BYTES",
48
+ "KEI_PROXY_PROVIDER_USER",
49
+ "KEI_CREDENTIAL_STORE_INSTALLATION_ID",
50
+ "KEI_AWS_SECRETS_MANAGER_ENDPOINT",
51
+ ];
52
+ export const DEFAULT_AUTHORIZE_TIMEOUT_MS = 10_000;
53
+ /** Upper bound on captured stdout. */
54
+ const MAX_STDOUT_BYTES = 1024 * 1024;
55
+ /** Upper bound on the executable's size when digesting it. */
56
+ const MAX_EXECUTABLE_BYTES = 256 * 1024 * 1024;
57
+ const HASH_CHUNK_BYTES = 64 * 1024;
58
+ const SHA256_HEX = /^[0-9a-fA-F]{64}$/;
59
+ const RUNTIME_TOKEN_ENV = "KEI_RUNTIME_TOKEN";
60
+ /**
61
+ * Runs `kei-proxy authorize` once per call without blocking the event loop.
62
+ * The runtime token reaches the child only through its environment, never argv.
63
+ */
64
+ export class KeiProxyAuthorizeClient {
65
+ /** Absolute path to spawn, or undefined when a bare name was not found. */
66
+ executable;
67
+ expectedDigest;
68
+ timeoutMs;
69
+ env;
70
+ extraEnv;
71
+ constructor(options = {}) {
72
+ const executable = options.executable ?? "kei-proxy";
73
+ if (options.expectedSha256 !== undefined) {
74
+ if (!SHA256_HEX.test(options.expectedSha256)) {
75
+ throw new TypeError("expectedSha256 must be 64 hex digits");
76
+ }
77
+ if (!isAbsolute(executable)) {
78
+ throw new TypeError("a pinned executable must be an absolute path");
79
+ }
80
+ this.expectedDigest = Buffer.from(options.expectedSha256, "hex");
81
+ }
82
+ this.env = options.env;
83
+ this.executable = resolveExecutable(executable, (this.env ?? process.env).PATH);
84
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_AUTHORIZE_TIMEOUT_MS;
85
+ this.extraEnv = options.extraEnv ?? {};
86
+ }
87
+ /** The exact environment the kei-proxy child receives. */
88
+ childEnv() {
89
+ const source = this.env ?? process.env;
90
+ const out = {};
91
+ for (const key of AUTHORIZE_CHILD_ENV_ALLOWLIST) {
92
+ const value = source[key];
93
+ if (value !== undefined)
94
+ out[key] = value;
95
+ }
96
+ return { ...out, ...this.extraEnv };
97
+ }
98
+ async authorize(request) {
99
+ const env = this.childEnv();
100
+ if (!env[RUNTIME_TOKEN_ENV]) {
101
+ throw new KeiProxyAuthorizeError("missing_token", `${RUNTIME_TOKEN_ENV} is not set`);
102
+ }
103
+ if (this.executable === undefined) {
104
+ throw new KeiProxyAuthorizeError("proxy_unavailable", "kei-proxy was not found on PATH");
105
+ }
106
+ if (this.expectedDigest !== undefined) {
107
+ await proveExecutable(this.executable, this.expectedDigest);
108
+ }
109
+ const { stdout, exitCode } = await this.run(this.executable, keiProxyAuthorizeArgv(request), env);
110
+ return parseKeiProxyAuthorizeOutput(stdout, exitCode);
111
+ }
112
+ /** Spawn once in a new process group; resolve with stdout and exit code. */
113
+ run(executable, argv, env) {
114
+ return new Promise((resolvePromise, rejectPromise) => {
115
+ let child;
116
+ try {
117
+ child = spawn(executable, argv, {
118
+ env,
119
+ stdio: ["ignore", "pipe", "ignore"],
120
+ detached: process.platform !== "win32",
121
+ shell: false,
122
+ windowsHide: true,
123
+ });
124
+ }
125
+ catch (err) {
126
+ rejectPromise(unavailable(err));
127
+ return;
128
+ }
129
+ let settled = false;
130
+ const chunks = [];
131
+ let total = 0;
132
+ const killGroup = () => {
133
+ const pid = child.pid;
134
+ if (pid === undefined)
135
+ return;
136
+ try {
137
+ process.kill(process.platform === "win32" ? pid : -pid, "SIGKILL");
138
+ }
139
+ catch {
140
+ try {
141
+ child.kill("SIGKILL");
142
+ }
143
+ catch {
144
+ // Already gone.
145
+ }
146
+ }
147
+ };
148
+ const fail = (error, kill) => {
149
+ if (settled)
150
+ return;
151
+ settled = true;
152
+ clearTimeout(timer);
153
+ if (kill)
154
+ killGroup();
155
+ rejectPromise(error);
156
+ };
157
+ const timer = setTimeout(() => {
158
+ fail(new KeiProxyAuthorizeError("proxy_timeout", `no answer within ${this.timeoutMs}ms`), true);
159
+ }, this.timeoutMs);
160
+ child.on("error", (err) => {
161
+ if (child.pid === undefined) {
162
+ fail(unavailable(err), false);
163
+ }
164
+ else {
165
+ fail(new KeiProxyAuthorizeError("proxy_error", "kei-proxy failed"), true);
166
+ }
167
+ });
168
+ child.stdout?.on("data", (chunk) => {
169
+ if (settled)
170
+ return;
171
+ total += chunk.length;
172
+ if (total > MAX_STDOUT_BYTES) {
173
+ fail(new KeiProxyAuthorizeError("malformed_response", "stdout exceeded the size limit"), true);
174
+ return;
175
+ }
176
+ chunks.push(chunk);
177
+ });
178
+ child.on("close", (code, signal) => {
179
+ if (settled)
180
+ return;
181
+ settled = true;
182
+ clearTimeout(timer);
183
+ if (code === null) {
184
+ rejectPromise(new KeiProxyAuthorizeError("proxy_error", `kei-proxy terminated by ${signal ?? "signal"}`));
185
+ return;
186
+ }
187
+ resolvePromise({
188
+ stdout: Buffer.concat(chunks).toString("utf8"),
189
+ exitCode: code,
190
+ });
191
+ });
192
+ });
193
+ }
194
+ }
195
+ function unavailable(err) {
196
+ const code = err?.code;
197
+ return new KeiProxyAuthorizeError("proxy_unavailable", `could not start kei-proxy (${code ?? "error"})`);
198
+ }
199
+ /**
200
+ * An absolute path for `executable`. Names containing a separator resolve
201
+ * against the working directory; bare names are looked up in the absolute
202
+ * entries of `pathVar` (relative entries are skipped).
203
+ */
204
+ function resolveExecutable(executable, pathVar) {
205
+ if (isAbsolute(executable))
206
+ return executable;
207
+ if (executable.includes("/") || executable.includes("\\")) {
208
+ return resolve(executable);
209
+ }
210
+ for (const dir of (pathVar ?? "").split(delimiter)) {
211
+ if (!dir || !isAbsolute(dir))
212
+ continue;
213
+ const candidate = join(dir, executable);
214
+ try {
215
+ if (!statSync(candidate).isFile())
216
+ continue;
217
+ accessSync(candidate, fsConstants.X_OK);
218
+ return candidate;
219
+ }
220
+ catch {
221
+ // Not here; keep looking.
222
+ }
223
+ }
224
+ return undefined;
225
+ }
226
+ function pinMismatch(detail) {
227
+ return new KeiProxyAuthorizeError("pin_mismatch", detail);
228
+ }
229
+ /** Re-prove the pinned executable immediately before a spawn. */
230
+ async function proveExecutable(path, expected) {
231
+ let real;
232
+ try {
233
+ real = await realpath(path);
234
+ }
235
+ catch {
236
+ throw pinMismatch("executable is missing");
237
+ }
238
+ if (real !== path)
239
+ throw pinMismatch("executable path is not canonical");
240
+ const noFollow = fsConstants.O_NOFOLLOW;
241
+ if (noFollow === undefined)
242
+ throw pinMismatch("O_NOFOLLOW is unavailable");
243
+ let handle;
244
+ try {
245
+ handle = await open(path, fsConstants.O_RDONLY | noFollow);
246
+ }
247
+ catch {
248
+ throw pinMismatch("executable could not be opened without following links");
249
+ }
250
+ let actual;
251
+ try {
252
+ if (!(await handle.stat()).isFile()) {
253
+ throw pinMismatch("executable is not a regular file");
254
+ }
255
+ actual = await digestOfHandle(handle);
256
+ }
257
+ finally {
258
+ await handle.close().catch(() => undefined);
259
+ }
260
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) {
261
+ throw pinMismatch("executable digest does not match the pin");
262
+ }
263
+ }
264
+ /** SHA-256 streamed off an already-open descriptor by explicit position. */
265
+ async function digestOfHandle(handle) {
266
+ const hash = createHash("sha256");
267
+ const buffer = Buffer.allocUnsafe(HASH_CHUNK_BYTES);
268
+ let position = 0;
269
+ for (;;) {
270
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
271
+ if (bytesRead === 0)
272
+ break;
273
+ position += bytesRead;
274
+ if (position > MAX_EXECUTABLE_BYTES) {
275
+ throw pinMismatch("executable exceeds the size limit");
276
+ }
277
+ hash.update(buffer.subarray(0, bytesRead));
278
+ }
279
+ return hash.digest();
280
+ }
281
+ /**
282
+ * The argv (after the executable) for one authorize call. `agentId` and
283
+ * `workspaceId` are not sent: kei-proxy (>= 0.1.11) resolves the agent from
284
+ * the runtime installation and workspace scope from the runtime token.
285
+ */
286
+ export function keiProxyAuthorizeArgv(request) {
287
+ const argv = [
288
+ "authorize",
289
+ "--user",
290
+ request.userId,
291
+ "--tool",
292
+ request.tool,
293
+ "--action",
294
+ request.action,
295
+ "--resource",
296
+ request.resource,
297
+ ];
298
+ const optional = [
299
+ ["--span-id", request.spanId],
300
+ ["--invoking-subject", request.invokingSubject],
301
+ ["--parent-span", request.parentSpan],
302
+ [
303
+ "--delegation-depth",
304
+ request.delegationDepth > 0 ? String(request.delegationDepth) : "",
305
+ ],
306
+ ["--agent-version", request.agentVersion],
307
+ ["--framework", request.framework],
308
+ ["--tool-args-digest", request.toolArgsDigest],
309
+ ["--resources", request.resources.join(",")],
310
+ ];
311
+ for (const [flag, value] of optional) {
312
+ if (value)
313
+ argv.push(flag, value);
314
+ }
315
+ return argv;
316
+ }
317
+ /** Validate one authorize result against the exit-code contract. */
318
+ export function parseKeiProxyAuthorizeOutput(stdout, exitCode) {
319
+ if (exitCode !== 0 && exitCode !== 1) {
320
+ throw new KeiProxyAuthorizeError("proxy_error", `kei-proxy exited ${exitCode}`);
321
+ }
322
+ const text = stdout.trim();
323
+ if (!text) {
324
+ if (exitCode !== 0) {
325
+ throw new KeiProxyAuthorizeError("proxy_error", `kei-proxy exited ${exitCode}`);
326
+ }
327
+ throw new KeiProxyAuthorizeError("empty_response", "kei-proxy printed nothing");
328
+ }
329
+ let parsed;
330
+ try {
331
+ parsed = JSON.parse(text);
332
+ }
333
+ catch {
334
+ throw new KeiProxyAuthorizeError("malformed_response", "stdout is not JSON");
335
+ }
336
+ if (!isPlainObject(parsed)) {
337
+ throw new KeiProxyAuthorizeError("malformed_response", "stdout is not a JSON object");
338
+ }
339
+ const decision = parsed.decision;
340
+ if (exitCode !== 0 &&
341
+ typeof decision === "string" &&
342
+ KEI_PROXY_AFFIRMATIVE_DECISIONS.has(decision.trim().toLowerCase())) {
343
+ throw new KeiProxyAuthorizeError("exit_mismatch", `${decision} with exit ${exitCode}`);
344
+ }
345
+ return parsed;
346
+ }
347
+ function isPlainObject(value) {
348
+ return typeof value === "object" && value !== null && !Array.isArray(value);
349
+ }
@@ -0,0 +1,74 @@
1
+ import { type CallerContext, type Decision } from "../middleware/types.js";
2
+ /** kei-proxy's affirmative decisions. Everything else denies. */
3
+ export declare const KEI_PROXY_AFFIRMATIVE_DECISIONS: ReadonlySet<string>;
4
+ /** Every reason reads `kei-proxy <class>` or `kei-proxy <class>: <detail>`. */
5
+ export declare const KEI_PROXY_REASON_CLASSES: readonly ["allow", "deny", "enrollment_required", "unknown_decision", "no_decision", "malformed_response", "empty_response", "proxy_error", "exit_mismatch", "proxy_unavailable", "proxy_timeout", "missing_token", "pin_mismatch"];
6
+ export type KeiProxyReasonClass = (typeof KEI_PROXY_REASON_CLASSES)[number];
7
+ /**
8
+ * An authorize call that produced no usable answer. The message is built by
9
+ * this library and never contains proxy stdout or stderr.
10
+ */
11
+ export declare class KeiProxyAuthorizeError extends Error {
12
+ readonly reasonClass: KeiProxyReasonClass;
13
+ readonly detail: string;
14
+ constructor(reasonClass: KeiProxyReasonClass, detail: string);
15
+ }
16
+ /** The canonical audit context sent with one authorize call. */
17
+ export interface KeiProxyAuthorizeRequest {
18
+ userId: string;
19
+ tool: string;
20
+ action: string;
21
+ resource: string;
22
+ spanId: string;
23
+ invokingSubject: string;
24
+ parentSpan: string;
25
+ delegationDepth: number;
26
+ agentId: string;
27
+ agentVersion: string;
28
+ framework: string;
29
+ toolArgsDigest: string;
30
+ resources: string[];
31
+ /** Accepted for parity; kei-proxy derives workspace scope from the runtime token. */
32
+ workspaceId: string;
33
+ }
34
+ /**
35
+ * The single call the evaluator needs. Return (or resolve to) the parsed
36
+ * authorize object; throw or reject with `KeiProxyAuthorizeError` (or
37
+ * anything) to deny.
38
+ */
39
+ export interface KeiProxyAuthorizationClient {
40
+ authorize(request: KeiProxyAuthorizeRequest): unknown;
41
+ }
42
+ /**
43
+ * Derive `type:kind:id` resource identifiers from a tool call's arguments.
44
+ * Only resources named in the arguments are knowable before the call.
45
+ */
46
+ export declare function keiProxyResourcesTouched(toolName: string, args: Record<string, unknown>): string[];
47
+ /**
48
+ * SHA-256 of the args as compact, sorted-key, ASCII-escaped JSON: byte-for-byte
49
+ * Python's `json.dumps(args, sort_keys=True, separators=(",", ":"))` for
50
+ * strings, integers, booleans, null, arrays and objects.
51
+ */
52
+ export declare function keiProxyToolArgsDigest(args: Record<string, unknown>): string;
53
+ export type KeiProxyLogger = (level: "debug" | "warn", message: string) => void;
54
+ export interface KeiProxyEvaluatorOptions {
55
+ /** Action verb sent when a tool call names none in `args.action`. Default `execute`. */
56
+ defaultAction?: string;
57
+ /** Receives class-level diagnostics only. Default: `console.warn` for warnings. */
58
+ logger?: KeiProxyLogger;
59
+ }
60
+ /**
61
+ * A policy evaluator over kei-proxy. Translates the proxy's decision into an
62
+ * agentware Decision and fails closed on every path that is not an explicit
63
+ * affirmative. See `docs/kei-proxy-evaluator-parity.md` for the table.
64
+ */
65
+ export declare class KeiProxyEvaluator {
66
+ static readonly RULE = "kei-proxy";
67
+ private readonly client;
68
+ private readonly defaultAction;
69
+ private readonly logger;
70
+ constructor(client: KeiProxyAuthorizationClient, options?: KeiProxyEvaluatorOptions);
71
+ evaluate(toolName: string, args: Record<string, unknown>, caller: CallerContext): Promise<Decision>;
72
+ private decision;
73
+ }
74
+ //# sourceMappingURL=evaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../src/kei/evaluator.ts"],"names":[],"mappings":"AAiBA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,QAAQ,EACd,MAAM,wBAAwB,CAAC;AAEhC,iEAAiE;AACjE,eAAO,MAAM,+BAA+B,EAAE,WAAW,CAAC,MAAM,CAG9D,CAAC;AAEH,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,qOAc3B,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,WAAW,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM;CAM7D;AAED,gEAAgE;AAChE,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,qFAAqF;IACrF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC;CACvD;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,EAAE,CAoBV;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAE5E;AAwBD,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEhF,MAAM,WAAW,wBAAwB;IACvC,wFAAwF;IACxF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB;AAMD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,QAAQ,CAAC,IAAI,eAAe;IAEnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;gBAGtC,MAAM,EAAE,2BAA2B,EACnC,OAAO,GAAE,wBAA6B;IAOlC,QAAQ,CACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,QAAQ,CAAC;IA2FpB,OAAO,CAAC,QAAQ;CAejB"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Policy evaluation backed by `kei-proxy authorize`.
3
+ *
4
+ * TypeScript port of `pedro_agentware.kei.evaluator`. All three SDK languages
5
+ * are held to the parity table in `fixtures/kei/authorize-cases.v1.json`
6
+ * (see `docs/kei-proxy-evaluator-parity.md`): the same proxy output yields the
7
+ * same Decision, reason class and enrollment everywhere.
8
+ *
9
+ * Fail closed: only an explicit `allow`/`permit` with exit 0 allows. This
10
+ * module never spawns the proxy: `KeiProxyAuthorizeClient`
11
+ * (`authorizeClient.ts`) does, behind the `KeiProxyAuthorizationClient` seam.
12
+ *
13
+ * `evaluate` is asynchronous so a harness's event loop keeps running while
14
+ * kei-proxy answers. It therefore does not implement the synchronous
15
+ * `PolicyEvaluator`; await it before running the tool.
16
+ */
17
+ import { createHash } from "node:crypto";
18
+ import { Action, } from "../middleware/types.js";
19
+ /** kei-proxy's affirmative decisions. Everything else denies. */
20
+ export const KEI_PROXY_AFFIRMATIVE_DECISIONS = new Set([
21
+ "permit",
22
+ "allow",
23
+ ]);
24
+ /** Every reason reads `kei-proxy <class>` or `kei-proxy <class>: <detail>`. */
25
+ export const KEI_PROXY_REASON_CLASSES = [
26
+ "allow",
27
+ "deny",
28
+ "enrollment_required",
29
+ "unknown_decision",
30
+ "no_decision",
31
+ "malformed_response",
32
+ "empty_response",
33
+ "proxy_error",
34
+ "exit_mismatch",
35
+ "proxy_unavailable",
36
+ "proxy_timeout",
37
+ "missing_token",
38
+ "pin_mismatch",
39
+ ];
40
+ /**
41
+ * An authorize call that produced no usable answer. The message is built by
42
+ * this library and never contains proxy stdout or stderr.
43
+ */
44
+ export class KeiProxyAuthorizeError extends Error {
45
+ reasonClass;
46
+ detail;
47
+ constructor(reasonClass, detail) {
48
+ super(detail);
49
+ this.name = "KeiProxyAuthorizeError";
50
+ this.reasonClass = reasonClass;
51
+ this.detail = detail;
52
+ }
53
+ }
54
+ /**
55
+ * Derive `type:kind:id` resource identifiers from a tool call's arguments.
56
+ * Only resources named in the arguments are knowable before the call.
57
+ */
58
+ export function keiProxyResourcesTouched(toolName, args) {
59
+ const resourceType = toolName.split(".", 1)[0].split("_", 1)[0].toLowerCase();
60
+ if (!resourceType)
61
+ return [];
62
+ const touched = [];
63
+ const add = (kind, identifier) => {
64
+ if (identifier === undefined || identifier === null)
65
+ return;
66
+ const value = String(identifier).trim();
67
+ if (!value)
68
+ return;
69
+ const entry = `${resourceType}:${kind}:${value}`;
70
+ if (!touched.includes(entry))
71
+ touched.push(entry);
72
+ };
73
+ const owner = args.owner;
74
+ const repo = args.repo || args.repository;
75
+ if (owner && repo)
76
+ add("repo", `${String(owner)}/${String(repo)}`);
77
+ else if (repo)
78
+ add("repo", repo);
79
+ add("issue", args.issue_number);
80
+ add("pull_request", args.pull_number || args.pr_number);
81
+ add("branch", args.branch || args.ref);
82
+ add("file", args.path || args.file_path);
83
+ return touched;
84
+ }
85
+ /**
86
+ * SHA-256 of the args as compact, sorted-key, ASCII-escaped JSON: byte-for-byte
87
+ * Python's `json.dumps(args, sort_keys=True, separators=(",", ":"))` for
88
+ * strings, integers, booleans, null, arrays and objects.
89
+ */
90
+ export function keiProxyToolArgsDigest(args) {
91
+ return createHash("sha256").update(canonicalJson(args), "utf8").digest("hex");
92
+ }
93
+ function canonicalJson(value) {
94
+ if (Array.isArray(value))
95
+ return `[${value.map(canonicalJson).join(",")}]`;
96
+ if (isPlainObject(value)) {
97
+ const keys = Object.keys(value).sort();
98
+ return `{${keys.map((k) => `${asciiJson(k)}:${canonicalJson(value[k])}`).join(",")}}`;
99
+ }
100
+ if (typeof value === "string")
101
+ return asciiJson(value);
102
+ if (value === undefined)
103
+ return "null";
104
+ return JSON.stringify(value) ?? "null";
105
+ }
106
+ function asciiJson(text) {
107
+ return JSON.stringify(text).replace(/[\u0080-\uffff]/g, (ch) => `\\u${ch.charCodeAt(0).toString(16).padStart(4, "0")}`);
108
+ }
109
+ function isPlainObject(value) {
110
+ return typeof value === "object" && value !== null && !Array.isArray(value);
111
+ }
112
+ const defaultLogger = (level, message) => {
113
+ if (level === "warn")
114
+ console.warn(message);
115
+ };
116
+ /**
117
+ * A policy evaluator over kei-proxy. Translates the proxy's decision into an
118
+ * agentware Decision and fails closed on every path that is not an explicit
119
+ * affirmative. See `docs/kei-proxy-evaluator-parity.md` for the table.
120
+ */
121
+ export class KeiProxyEvaluator {
122
+ static RULE = "kei-proxy";
123
+ client;
124
+ defaultAction;
125
+ logger;
126
+ constructor(client, options = {}) {
127
+ this.client = client;
128
+ this.defaultAction = options.defaultAction ?? "execute";
129
+ this.logger = options.logger ?? defaultLogger;
130
+ }
131
+ async evaluate(toolName, args, caller) {
132
+ const resources = keiProxyResourcesTouched(toolName, args);
133
+ const userId = caller.invoking_subject || caller.user_id || "";
134
+ const meta = caller.metadata ?? {};
135
+ const request = {
136
+ userId,
137
+ tool: toolName,
138
+ action: String(args.action || this.defaultAction),
139
+ resource: resources[0] ?? toolName,
140
+ spanId: meta.span_id ?? "",
141
+ invokingSubject: userId,
142
+ parentSpan: caller.parent_span ?? "",
143
+ delegationDepth: caller.delegation_depth ?? 0,
144
+ agentId: meta.agent_id ?? "",
145
+ agentVersion: meta.agent_version ?? "",
146
+ framework: meta.framework || caller.source || "",
147
+ toolArgsDigest: keiProxyToolArgsDigest(args),
148
+ resources,
149
+ workspaceId: meta.workspace_id ?? "",
150
+ };
151
+ let raw;
152
+ try {
153
+ raw = await this.client.authorize(request);
154
+ }
155
+ catch (err) {
156
+ if (err instanceof KeiProxyAuthorizeError) {
157
+ this.logger("warn", `kei-proxy authorize for ${toolName} denied: ${err.reasonClass}`);
158
+ return this.decision(Action.DENY, reason(err.reasonClass, err.detail));
159
+ }
160
+ const message = err instanceof Error ? err.message : String(err);
161
+ this.logger("warn", `kei-proxy authorize failed for ${toolName}, denying: ${message}`);
162
+ return this.decision(Action.DENY, reason("proxy_error", `authorize failed: ${message}`));
163
+ }
164
+ const response = isPlainObject(raw) ? raw : {};
165
+ const rawDecision = response.decision;
166
+ const decision = rawDecision === undefined || rawDecision === null
167
+ ? ""
168
+ : String(rawDecision).trim().toLowerCase();
169
+ const proxyReason = optionalString(response.reason);
170
+ const policyId = optionalString(response.policy_id) ?? optionalString(response.policy);
171
+ if (KEI_PROXY_AFFIRMATIVE_DECISIONS.has(decision)) {
172
+ return this.decision(Action.ALLOW, reason("allow", proxyReason), policyId);
173
+ }
174
+ let reasonClass;
175
+ let detail;
176
+ let enrollment;
177
+ const carried = isPlainObject(response.enrollment)
178
+ ? response.enrollment
179
+ : undefined;
180
+ if (!decision) {
181
+ reasonClass = "no_decision";
182
+ }
183
+ else if (decision === "deny") {
184
+ reasonClass = "deny";
185
+ detail = proxyReason;
186
+ enrollment = carried;
187
+ }
188
+ else if (decision === "enrollment_required") {
189
+ reasonClass = "enrollment_required";
190
+ detail = proxyReason ?? "enrollment is required before this call";
191
+ enrollment = carried;
192
+ }
193
+ else {
194
+ reasonClass = "unknown_decision";
195
+ detail = `'${decision}'` + (proxyReason ? ` (${proxyReason})` : "");
196
+ }
197
+ this.logger("debug", `kei-proxy ${reasonClass} for ${toolName}`);
198
+ return this.decision(Action.DENY, reason(reasonClass, detail), policyId, enrollment);
199
+ }
200
+ decision(action, reasonText, policyId, enrollment) {
201
+ const out = {
202
+ action,
203
+ rule: policyId ?? KeiProxyEvaluator.RULE,
204
+ reason: reasonText,
205
+ timestamp: new Date(),
206
+ };
207
+ if (enrollment !== undefined)
208
+ out.enrollment = enrollment;
209
+ return out;
210
+ }
211
+ }
212
+ function reason(reasonClass, detail) {
213
+ return detail
214
+ ? `kei-proxy ${reasonClass}: ${detail}`
215
+ : `kei-proxy ${reasonClass}`;
216
+ }
217
+ function optionalString(value) {
218
+ if (value === undefined || value === null)
219
+ return undefined;
220
+ const text = String(value);
221
+ return text ? text : undefined;
222
+ }
@@ -1,3 +1,5 @@
1
1
  export * from "./runtimeLink.js";
2
2
  export * from "./runtimeLinkRuntime.js";
3
+ export * from "./evaluator.js";
4
+ export * from "./authorizeClient.js";
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/kei/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/kei/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC"}
package/dist/kei/index.js CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from "./runtimeLink.js";
2
2
  export * from "./runtimeLinkRuntime.js";
3
+ export * from "./evaluator.js";
4
+ export * from "./authorizeClient.js";
@@ -170,6 +170,19 @@ export interface RuntimeIdentity {
170
170
  status: string;
171
171
  bindingStatus: string;
172
172
  runtimeVersion: string;
173
+ /**
174
+ * The installation's default agent from whoami; undefined when the runtime
175
+ * reports none (older runtimes omit it). A harness reads its agent from
176
+ * here and must not require an agent-ID env var.
177
+ */
178
+ defaultAgentId?: string;
179
+ /** Agents assigned to the installation; empty when the runtime reports none. */
180
+ agents: AssignedAgent[];
181
+ }
182
+ /** One agent assigned to a runtime installation. */
183
+ export interface AssignedAgent {
184
+ agentId: string;
185
+ isDefault: boolean;
173
186
  }
174
187
  export type ChildLineErrorKind = "line_too_long" | "malformed" | "contract_mismatch";
175
188
  /** A child line the SDK must drop and count. */
@@ -194,6 +207,8 @@ export interface TerminalEvent {
194
207
  export type ChildEvent = {
195
208
  kind: "identity";
196
209
  identity: RuntimeIdentity;
210
+ /** Malformed agent entries dropped from the event; counted like dropped lines. */
211
+ droppedAgents: number;
197
212
  } | {
198
213
  kind: "beat";
199
214
  beat: BeatEvent;
@@ -1 +1 @@
1
- {"version":3,"file":"runtimeLink.d.ts","sourceRoot":"","sources":["../../src/kei/runtimeLink.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtimeLink.d.ts","sourceRoot":"","sources":["../../src/kei/runtimeLink.ts"],"names":[],"mappings":"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"}
@@ -409,6 +409,57 @@ class FieldReader {
409
409
  return v;
410
410
  }
411
411
  }
412
+ /**
413
+ * Fill identity.defaultAgentId and identity.agents from the optional agent_id
414
+ * and agents fields. Older runtimes omit both, so absence is not an error; a
415
+ * malformed agent_id, agents list, or entry is dropped and counted rather than
416
+ * failing the whole identity. When agent_id is absent, the first entry marked
417
+ * is_default supplies it. Returns the number of dropped values.
418
+ */
419
+ function parseAgents(raw, identity) {
420
+ let dropped = 0;
421
+ if (Object.hasOwn(raw, "agent_id")) {
422
+ const v = raw.agent_id;
423
+ if (typeof v === "string" && ID_RE.test(v))
424
+ identity.defaultAgentId = v;
425
+ else
426
+ dropped++;
427
+ }
428
+ if (!Object.hasOwn(raw, "agents"))
429
+ return dropped;
430
+ const entries = raw.agents;
431
+ if (!Array.isArray(entries))
432
+ return dropped + 1;
433
+ for (const entry of entries) {
434
+ const agent = parseAgentEntry(entry);
435
+ if (!agent) {
436
+ dropped++;
437
+ continue;
438
+ }
439
+ identity.agents.push(agent);
440
+ if (identity.defaultAgentId === undefined && agent.isDefault) {
441
+ identity.defaultAgentId = agent.agentId;
442
+ }
443
+ }
444
+ return dropped;
445
+ }
446
+ /** Read one {agent_id, is_default} entry; is_default is false when absent. */
447
+ function parseAgentEntry(entry) {
448
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
449
+ return undefined;
450
+ }
451
+ const e = entry;
452
+ const agentId = e.agent_id;
453
+ if (typeof agentId !== "string" || !ID_RE.test(agentId))
454
+ return undefined;
455
+ let isDefault = false;
456
+ if (Object.hasOwn(e, "is_default")) {
457
+ if (typeof e.is_default !== "boolean")
458
+ return undefined;
459
+ isDefault = e.is_default;
460
+ }
461
+ return { agentId, isDefault };
462
+ }
412
463
  const encoder = new TextEncoder();
413
464
  const decoder = new TextDecoder();
414
465
  /**
@@ -444,20 +495,21 @@ export function parseChildLine(line) {
444
495
  throw new ChildLineError("malformed", "event");
445
496
  const f = new FieldReader(obj);
446
497
  switch (obj.event) {
447
- case "identity":
448
- return {
449
- kind: "identity",
450
- identity: {
451
- runId: f.id("run_id", true),
452
- installationId: f.id("installation_id", true),
453
- orgId: f.id("org_id", true),
454
- workspaceId: f.id("workspace_id", false),
455
- platform: f.id("platform", false),
456
- status: f.id("status", false),
457
- bindingStatus: f.id("binding_status", false),
458
- runtimeVersion: f.id("runtime_version", false),
459
- },
498
+ case "identity": {
499
+ const identity = {
500
+ runId: f.id("run_id", true),
501
+ installationId: f.id("installation_id", true),
502
+ orgId: f.id("org_id", true),
503
+ workspaceId: f.id("workspace_id", false),
504
+ platform: f.id("platform", false),
505
+ status: f.id("status", false),
506
+ bindingStatus: f.id("binding_status", false),
507
+ runtimeVersion: f.id("runtime_version", false),
508
+ agents: [],
460
509
  };
510
+ const droppedAgents = parseAgents(obj, identity);
511
+ return { kind: "identity", identity, droppedAgents };
512
+ }
461
513
  case "beat": {
462
514
  const runId = f.id("run_id", true);
463
515
  const seq = f.integer("seq", false, 0, MAX_SEQ);
@@ -240,6 +240,7 @@ class RuntimeLinkImpl {
240
240
  }
241
241
  if (ev.kind === "identity") {
242
242
  this.identityValue = ev.identity;
243
+ this.consecutiveFails += ev.droppedAgents;
243
244
  this.runId = ev.identity.runId;
244
245
  identitySeen = true;
245
246
  heartbeatDeadline = Date.now() + this.config.beatTimeoutMs;
@@ -29,12 +29,24 @@ export interface CallerContext {
29
29
  source?: string;
30
30
  trusted: boolean;
31
31
  metadata?: Record<string, string>;
32
+ /** The human who originated the request; carried unchanged across delegation hops. */
33
+ invoking_subject?: string;
34
+ /** Span of the delegating caller, when this call was made by a subagent. */
35
+ parent_span?: string;
36
+ /** 0 for a direct call; incremented at each delegation hop. */
37
+ delegation_depth?: number;
32
38
  }
33
39
  export interface Decision {
34
40
  action: Action;
35
41
  rule: string;
36
42
  reason: string;
37
43
  redacted_args?: Record<string, unknown>;
44
+ /**
45
+ * Opaque enrollment object from kei-proxy on a DENY (provider,
46
+ * provider_user_id, org_id, workspace_id, and optionally url/expires_at).
47
+ * The url is a one-time claim link: surface it to the user, never log it.
48
+ */
49
+ enrollment?: Record<string, unknown>;
38
50
  timestamp: Date;
39
51
  }
40
52
  export declare function createAllowDecision(reason?: string): Decision;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/middleware/types.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IACrB,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,SAAS,cAAc;IACvB,WAAW,gBAAgB;IAC3B,SAAS,cAAc;IACvB,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,kBAAkB,uBAAuB;IACzC,WAAW,gBAAgB;IAC3B,eAAe,oBAAoB;IACnC,OAAO,YAAY;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,WAAW,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,oBAAY,MAAM;IAChB,KAAK,UAAU;IACf,IAAI,SAAS;IACb,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,SAAY,GAAG,QAAQ,CAOhE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAO3D"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/middleware/types.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IACrB,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,SAAS,cAAc;IACvB,WAAW,gBAAgB;IAC3B,SAAS,cAAc;IACvB,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,kBAAkB,uBAAuB;IACzC,WAAW,gBAAgB;IAC3B,eAAe,oBAAoB;IACnC,OAAO,YAAY;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,WAAW,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,oBAAY,MAAM;IAChB,KAAK,UAAU;IACf,IAAI,SAAS;IACb,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,SAAY,GAAG,QAAQ,CAOhE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAO3D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haikeilabs/agentware",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Agent middleware and tooling for LLM tool calling",
5
5
  "repository": {
6
6
  "type": "git",