airlock-bot 0.2.37 → 0.2.38
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 +26 -8
- package/dist/activity/stream.d.ts +29 -0
- package/dist/activity/stream.d.ts.map +1 -0
- package/dist/activity/stream.js +36 -0
- package/dist/activity/stream.js.map +1 -0
- package/dist/airlock/context.d.ts +12 -0
- package/dist/airlock/context.d.ts.map +1 -0
- package/dist/airlock/context.js +32 -0
- package/dist/airlock/context.js.map +1 -0
- package/dist/airlock/tools.d.ts +7 -0
- package/dist/airlock/tools.d.ts.map +1 -0
- package/dist/airlock/tools.js +13 -0
- package/dist/airlock/tools.js.map +1 -0
- package/dist/allowlist/engine.d.ts +12 -0
- package/dist/allowlist/engine.d.ts.map +1 -1
- package/dist/allowlist/engine.js +40 -15
- package/dist/allowlist/engine.js.map +1 -1
- package/dist/audit/api.d.ts +2 -0
- package/dist/audit/api.d.ts.map +1 -1
- package/dist/audit/api.js.map +1 -1
- package/dist/audit/db.d.ts +16 -0
- package/dist/audit/db.d.ts.map +1 -1
- package/dist/audit/db.js +73 -0
- package/dist/audit/db.js.map +1 -1
- package/dist/audit/logger.d.ts +8 -1
- package/dist/audit/logger.d.ts.map +1 -1
- package/dist/audit/logger.js +21 -0
- package/dist/audit/logger.js.map +1 -1
- package/dist/backend/airlock-adapter.d.ts +22 -0
- package/dist/backend/airlock-adapter.d.ts.map +1 -0
- package/dist/backend/airlock-adapter.js +136 -0
- package/dist/backend/airlock-adapter.js.map +1 -0
- package/dist/backend/factory.d.ts +5 -1
- package/dist/backend/factory.d.ts.map +1 -1
- package/dist/backend/factory.js +5 -5
- package/dist/backend/factory.js.map +1 -1
- package/dist/backend/mcp-adapter.d.ts.map +1 -1
- package/dist/backend/mcp-adapter.js +28 -4
- package/dist/backend/mcp-adapter.js.map +1 -1
- package/dist/backend/openapi/adapter.d.ts.map +1 -1
- package/dist/backend/openapi/adapter.js +33 -13
- package/dist/backend/openapi/adapter.js.map +1 -1
- package/dist/config/loader.d.ts +12 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +799 -23
- package/dist/config/loader.js.map +1 -1
- package/dist/config/profiles.d.ts +44 -3
- package/dist/config/profiles.d.ts.map +1 -1
- package/dist/config/profiles.js +314 -3
- package/dist/config/profiles.js.map +1 -1
- package/dist/config/schema.d.ts +3300 -201
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +272 -71
- package/dist/config/schema.js.map +1 -1
- package/dist/configure-web/cli.d.ts +1 -0
- package/dist/configure-web/cli.d.ts.map +1 -1
- package/dist/configure-web/cli.js +238 -47
- package/dist/configure-web/cli.js.map +1 -1
- package/dist/gateway.d.ts +16 -1
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +263 -57
- package/dist/gateway.js.map +1 -1
- package/dist/hitl/api.d.ts +2 -0
- package/dist/hitl/api.d.ts.map +1 -1
- package/dist/hitl/api.js +8 -0
- package/dist/hitl/api.js.map +1 -1
- package/dist/hitl/approval-dashboard.d.ts +5 -9
- package/dist/hitl/approval-dashboard.d.ts.map +1 -1
- package/dist/hitl/approval-dashboard.js +8 -55
- package/dist/hitl/approval-dashboard.js.map +1 -1
- package/dist/hitl/approval-stream.d.ts +34 -0
- package/dist/hitl/approval-stream.d.ts.map +1 -0
- package/dist/hitl/approval-stream.js +111 -0
- package/dist/hitl/approval-stream.js.map +1 -0
- package/dist/hitl/engine.d.ts +16 -7
- package/dist/hitl/engine.d.ts.map +1 -1
- package/dist/hitl/engine.js +109 -35
- package/dist/hitl/engine.js.map +1 -1
- package/dist/hitl/formatter.d.ts.map +1 -1
- package/dist/hitl/formatter.js +2 -0
- package/dist/hitl/formatter.js.map +1 -1
- package/dist/hitl/provider-factory.d.ts +8 -2
- package/dist/hitl/provider-factory.d.ts.map +1 -1
- package/dist/hitl/provider-factory.js +23 -2
- package/dist/hitl/provider-factory.js.map +1 -1
- package/dist/hitl/providers/composite.d.ts +9 -0
- package/dist/hitl/providers/composite.d.ts.map +1 -1
- package/dist/hitl/providers/composite.js +14 -0
- package/dist/hitl/providers/composite.js.map +1 -1
- package/dist/hitl/providers/dashboard.d.ts +12 -2
- package/dist/hitl/providers/dashboard.d.ts.map +1 -1
- package/dist/hitl/providers/dashboard.js +11 -5
- package/dist/hitl/providers/dashboard.js.map +1 -1
- package/dist/hitl/providers/ios.d.ts +23 -0
- package/dist/hitl/providers/ios.d.ts.map +1 -0
- package/dist/hitl/providers/ios.js +183 -0
- package/dist/hitl/providers/ios.js.map +1 -0
- package/dist/hitl/providers/macos.d.ts.map +1 -1
- package/dist/hitl/providers/macos.js +8 -4
- package/dist/hitl/providers/macos.js.map +1 -1
- package/dist/hitl/providers/openclaw.js +2 -2
- package/dist/hitl/providers/openclaw.js.map +1 -1
- package/dist/hitl/providers/stdio.js +2 -2
- package/dist/hitl/providers/stdio.js.map +1 -1
- package/dist/hitl/providers/telegram.js +2 -2
- package/dist/hitl/providers/telegram.js.map +1 -1
- package/dist/hitl/providers/tui.d.ts.map +1 -1
- package/dist/hitl/providers/tui.js +8 -2
- package/dist/hitl/providers/tui.js.map +1 -1
- package/dist/hitl/providers/types.d.ts +14 -0
- package/dist/hitl/providers/types.d.ts.map +1 -1
- package/dist/hook/api.d.ts +2 -0
- package/dist/hook/api.d.ts.map +1 -1
- package/dist/hook/api.js.map +1 -1
- package/dist/hook/normalizer.js +2 -2
- package/dist/hook/normalizer.js.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/introspect/cli.d.ts +31 -0
- package/dist/introspect/cli.d.ts.map +1 -0
- package/dist/introspect/cli.js +756 -0
- package/dist/introspect/cli.js.map +1 -0
- package/dist/middleware/chain-builder.d.ts +1 -1
- package/dist/middleware/chain-builder.d.ts.map +1 -1
- package/dist/middleware/chain-builder.js +6 -4
- package/dist/middleware/chain-builder.js.map +1 -1
- package/dist/middleware/core/allowlist.d.ts.map +1 -1
- package/dist/middleware/core/allowlist.js +14 -0
- package/dist/middleware/core/allowlist.js.map +1 -1
- package/dist/middleware/core/arg-policy.d.ts +2 -1
- package/dist/middleware/core/arg-policy.d.ts.map +1 -1
- package/dist/middleware/core/arg-policy.js +156 -46
- package/dist/middleware/core/arg-policy.js.map +1 -1
- package/dist/middleware/core/exec-policy.d.ts.map +1 -1
- package/dist/middleware/core/exec-policy.js +16 -1
- package/dist/middleware/core/exec-policy.js.map +1 -1
- package/dist/middleware/core/execute.d.ts.map +1 -1
- package/dist/middleware/core/execute.js +6 -2
- package/dist/middleware/core/execute.js.map +1 -1
- package/dist/middleware/core/hitl-gate.d.ts.map +1 -1
- package/dist/middleware/core/hitl-gate.js +15 -4
- package/dist/middleware/core/hitl-gate.js.map +1 -1
- package/dist/middleware/core/schema-validator.d.ts.map +1 -1
- package/dist/middleware/core/schema-validator.js +7 -3
- package/dist/middleware/core/schema-validator.js.map +1 -1
- package/dist/middleware/post/canary-token-injector.d.ts +4 -1
- package/dist/middleware/post/canary-token-injector.d.ts.map +1 -1
- package/dist/middleware/post/canary-token-injector.js +5 -1
- package/dist/middleware/post/canary-token-injector.js.map +1 -1
- package/dist/middleware/post/untrusted-envelope.d.ts.map +1 -1
- package/dist/middleware/post/untrusted-envelope.js +7 -1
- package/dist/middleware/post/untrusted-envelope.js.map +1 -1
- package/dist/mobile/api.d.ts +20 -0
- package/dist/mobile/api.d.ts.map +1 -0
- package/dist/mobile/api.js +255 -0
- package/dist/mobile/api.js.map +1 -0
- package/dist/mobile/apns.d.ts +68 -0
- package/dist/mobile/apns.d.ts.map +1 -0
- package/dist/mobile/apns.js +321 -0
- package/dist/mobile/apns.js.map +1 -0
- package/dist/pool/http-client.d.ts +3 -1
- package/dist/pool/http-client.d.ts.map +1 -1
- package/dist/pool/http-client.js +5 -2
- package/dist/pool/http-client.js.map +1 -1
- package/dist/pool/oauth-provider.d.ts +2 -0
- package/dist/pool/oauth-provider.d.ts.map +1 -1
- package/dist/pool/oauth-provider.js +23 -4
- package/dist/pool/oauth-provider.js.map +1 -1
- package/dist/pool/pool.d.ts +2 -1
- package/dist/pool/pool.d.ts.map +1 -1
- package/dist/pool/pool.js +2 -2
- package/dist/pool/pool.js.map +1 -1
- package/dist/pool/sse-client.d.ts +3 -1
- package/dist/pool/sse-client.d.ts.map +1 -1
- package/dist/pool/sse-client.js +5 -2
- package/dist/pool/sse-client.js.map +1 -1
- package/dist/pool/stdio-client.d.ts +3 -1
- package/dist/pool/stdio-client.d.ts.map +1 -1
- package/dist/pool/stdio-client.js +5 -2
- package/dist/pool/stdio-client.js.map +1 -1
- package/dist/registry/airlock-policy.d.ts +3 -0
- package/dist/registry/airlock-policy.d.ts.map +1 -0
- package/dist/registry/airlock-policy.js +50 -0
- package/dist/registry/airlock-policy.js.map +1 -0
- package/dist/registry/registry.d.ts +2 -0
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +19 -13
- package/dist/registry/registry.js.map +1 -1
- package/dist/security/blocked-hosts.d.ts +2 -0
- package/dist/security/blocked-hosts.d.ts.map +1 -1
- package/dist/security/blocked-hosts.js +61 -7
- package/dist/security/blocked-hosts.js.map +1 -1
- package/dist/security/request.d.ts +2 -0
- package/dist/security/request.d.ts.map +1 -1
- package/dist/security/request.js +6 -2
- package/dist/security/request.js.map +1 -1
- package/dist/security/secrets.d.ts +12 -0
- package/dist/security/secrets.d.ts.map +1 -0
- package/dist/security/secrets.js +138 -0
- package/dist/security/secrets.js.map +1 -0
- package/dist/setup-openclaw/cli.js +1 -1
- package/dist/stdio-mode.d.ts.map +1 -1
- package/dist/stdio-mode.js +125 -24
- package/dist/stdio-mode.js.map +1 -1
- package/dist/tools/api.d.ts +4 -1
- package/dist/tools/api.d.ts.map +1 -1
- package/dist/tools/api.js +58 -7
- package/dist/tools/api.js.map +1 -1
- package/dist/tools/exec.d.ts +2 -1
- package/dist/tools/exec.d.ts.map +1 -1
- package/dist/tools/exec.js +4 -3
- package/dist/tools/exec.js.map +1 -1
- package/dist/tools/http.d.ts.map +1 -1
- package/dist/tools/http.js +4 -7
- package/dist/tools/http.js.map +1 -1
- package/dist/transport/agent-server.d.ts +4 -0
- package/dist/transport/agent-server.d.ts.map +1 -1
- package/dist/transport/agent-server.js +23 -11
- package/dist/transport/agent-server.js.map +1 -1
- package/dist/transport/http-server.d.ts +2 -0
- package/dist/transport/http-server.d.ts.map +1 -1
- package/dist/transport/http-server.js +10 -6
- package/dist/transport/http-server.js.map +1 -1
- package/dist/transport/sse-server.d.ts +2 -0
- package/dist/transport/sse-server.d.ts.map +1 -1
- package/dist/transport/sse-server.js +10 -6
- package/dist/transport/sse-server.js.map +1 -1
- package/dist/transport/stdio-server.d.ts.map +1 -1
- package/dist/transport/stdio-server.js +4 -0
- package/dist/transport/stdio-server.js.map +1 -1
- package/dist/util/id.d.ts +1 -1
- package/dist/util/id.d.ts.map +1 -1
- package/dist/util/id.js +3 -3
- package/dist/util/id.js.map +1 -1
- package/examples/claude-code-setup.md +0 -2
- package/examples/docker-compose.yaml +4 -3
- package/examples/docker-gateway.yaml +8 -3
- package/examples/gateway.yaml +8 -5
- package/examples/profiles.yaml +0 -2
- package/extensions/openclaw/index.ts +9 -6
- package/extensions/openclaw/openclaw.plugin.json +5 -5
- package/package.json +3 -1
- package/schema.json +634 -28
|
@@ -6,13 +6,10 @@ let latestVersionCache = null;
|
|
|
6
6
|
const CACHE_TTL_MS = 60 * 60 * 1000;
|
|
7
7
|
export class ApprovalDashboardRoutes {
|
|
8
8
|
approvalApi;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
constructor(approvalApi) {
|
|
9
|
+
stream;
|
|
10
|
+
constructor(approvalApi, stream) {
|
|
12
11
|
this.approvalApi = approvalApi;
|
|
13
|
-
|
|
14
|
-
init() {
|
|
15
|
-
return Promise.resolve();
|
|
12
|
+
this.stream = stream;
|
|
16
13
|
}
|
|
17
14
|
registerRoutes(app, configPath) {
|
|
18
15
|
app.get('/events', (request, reply) => this.handleEvents(request, reply));
|
|
@@ -21,44 +18,15 @@ export class ApprovalDashboardRoutes {
|
|
|
21
18
|
app.get('/version', () => ({ version: VERSION }));
|
|
22
19
|
app.get('/version/latest', async (_request, reply) => this.handleLatestVersion(reply));
|
|
23
20
|
}
|
|
24
|
-
notify(requests) {
|
|
25
|
-
for (const request of requests) {
|
|
26
|
-
this.pending.set(request.code, request);
|
|
27
|
-
this.broadcast({ type: 'new', request });
|
|
28
|
-
}
|
|
29
|
-
return Promise.resolve();
|
|
30
|
-
}
|
|
31
|
-
stop() {
|
|
32
|
-
for (const client of this.clients) {
|
|
33
|
-
try {
|
|
34
|
-
client.end();
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
/* swallow */
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
this.clients.clear();
|
|
41
|
-
return Promise.resolve();
|
|
42
|
-
}
|
|
43
21
|
handleEvents(request, reply) {
|
|
44
|
-
|
|
45
|
-
reply.raw.writeHead(200, {
|
|
46
|
-
'Content-Type': 'text/event-stream',
|
|
47
|
-
'Cache-Control': 'no-cache',
|
|
48
|
-
Connection: 'keep-alive',
|
|
49
|
-
});
|
|
50
|
-
this.clients.add(reply.raw);
|
|
51
|
-
for (const pendingRequest of this.pending.values()) {
|
|
52
|
-
reply.raw.write(`data: ${JSON.stringify({ type: 'new', request: pendingRequest })}\n\n`);
|
|
53
|
-
}
|
|
54
|
-
request.raw.on('close', () => this.clients.delete(reply.raw));
|
|
22
|
+
this.stream.addClient(request, reply);
|
|
55
23
|
}
|
|
56
|
-
|
|
24
|
+
handleApprove(request, reply, configPath) {
|
|
57
25
|
const query = request.query;
|
|
58
26
|
const code = typeof query.code === 'string' ? query.code : '';
|
|
59
27
|
if (!code)
|
|
60
28
|
return { ok: true };
|
|
61
|
-
const pendingRequest = this.
|
|
29
|
+
const pendingRequest = this.stream.getPendingByCode(code);
|
|
62
30
|
const remember = typeof query.remember === 'string' ? query.remember : undefined;
|
|
63
31
|
if (remember) {
|
|
64
32
|
const result = this.rememberDecision(configPath, pendingRequest, remember, query.duration_ms);
|
|
@@ -67,9 +35,7 @@ export class ApprovalDashboardRoutes {
|
|
|
67
35
|
return { error: result.error };
|
|
68
36
|
}
|
|
69
37
|
}
|
|
70
|
-
this.
|
|
71
|
-
this.approvalApi.approve(code);
|
|
72
|
-
this.broadcast({ type: 'resolved', code, action: 'approved' });
|
|
38
|
+
this.approvalApi.approveByCode(code);
|
|
73
39
|
log.info({ code }, 'Approved via dashboard');
|
|
74
40
|
return { ok: true };
|
|
75
41
|
}
|
|
@@ -77,9 +43,7 @@ export class ApprovalDashboardRoutes {
|
|
|
77
43
|
const query = request.query;
|
|
78
44
|
const code = typeof query.code === 'string' ? query.code : '';
|
|
79
45
|
if (code) {
|
|
80
|
-
this.
|
|
81
|
-
this.approvalApi.deny(code, 'Denied via dashboard');
|
|
82
|
-
this.broadcast({ type: 'resolved', code, action: 'denied' });
|
|
46
|
+
this.approvalApi.denyByCode(code, 'Denied via dashboard');
|
|
83
47
|
log.info({ code }, 'Denied via dashboard');
|
|
84
48
|
}
|
|
85
49
|
return { ok: true };
|
|
@@ -133,16 +97,5 @@ export class ApprovalDashboardRoutes {
|
|
|
133
97
|
return { status: 500, error: 'Failed to update config' };
|
|
134
98
|
}
|
|
135
99
|
}
|
|
136
|
-
broadcast(data) {
|
|
137
|
-
const msg = `data: ${JSON.stringify(data)}\n\n`;
|
|
138
|
-
for (const client of this.clients) {
|
|
139
|
-
try {
|
|
140
|
-
client.write(msg);
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
/* swallow */
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
100
|
}
|
|
148
101
|
//# sourceMappingURL=approval-dashboard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-dashboard.js","sourceRoot":"","sources":["../../src/hitl/approval-dashboard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"approval-dashboard.js","sourceRoot":"","sources":["../../src/hitl/approval-dashboard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { FastifyReply, FastifyRequest } from 'fastify';
|
|
2
|
+
import type { AirlockActivityEvent } from '../activity/stream.js';
|
|
3
|
+
import type { HitlNotification, HitlProvider } from './providers/types.js';
|
|
4
|
+
type ApprovalStatus = Parameters<NonNullable<HitlProvider['updateApprovalStatus']>>[0];
|
|
5
|
+
interface ActivityReplaySource {
|
|
6
|
+
recent(): AirlockActivityEvent[];
|
|
7
|
+
}
|
|
8
|
+
export interface ApprovalStreamClient {
|
|
9
|
+
addClient(request: FastifyRequest, reply: FastifyReply): void;
|
|
10
|
+
}
|
|
11
|
+
export declare class ApprovalStreamHub implements ApprovalStreamClient, HitlProvider {
|
|
12
|
+
private options;
|
|
13
|
+
private clients;
|
|
14
|
+
private pending;
|
|
15
|
+
private pendingCodeIndex;
|
|
16
|
+
constructor(options?: {
|
|
17
|
+
activityStream?: ActivityReplaySource;
|
|
18
|
+
});
|
|
19
|
+
init(): Promise<void>;
|
|
20
|
+
notify(requests: HitlNotification[]): Promise<void>;
|
|
21
|
+
updateApprovalStatus(status: ApprovalStatus): Promise<void>;
|
|
22
|
+
notifyActivity(event: AirlockActivityEvent): Promise<void>;
|
|
23
|
+
addClient(request: FastifyRequest, reply: FastifyReply): void;
|
|
24
|
+
getPendingById(id: string): HitlNotification | undefined;
|
|
25
|
+
getPendingByCode(code: string): HitlNotification | undefined;
|
|
26
|
+
pendingCount(): number;
|
|
27
|
+
stop(): Promise<void>;
|
|
28
|
+
private broadcast;
|
|
29
|
+
private writeEvent;
|
|
30
|
+
private write;
|
|
31
|
+
private removeClient;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=approval-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-stream.d.ts","sourceRoot":"","sources":["../../src/hitl/approval-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAI3E,KAAK,cAAc,GAAG,UAAU,CAAC,WAAW,CAAC,YAAY,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEvF,UAAU,oBAAoB;IAC5B,MAAM,IAAI,oBAAoB,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CAC/D;AAED,qBAAa,iBAAkB,YAAW,oBAAoB,EAAE,YAAY;IAK9D,OAAO,CAAC,OAAO;IAJ3B,OAAO,CAAC,OAAO,CAA6C;IAC5D,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,gBAAgB,CAA6B;gBAEjC,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,oBAAoB,CAAA;KAAO;IAE3E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IASnD,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAc3D,cAAc,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1D,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;IA4B7D,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAIxD,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAK5D,YAAY,IAAI,MAAM;IAItB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAerB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,KAAK;IAQb,OAAO,CAAC,YAAY;CAKrB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const KEEPALIVE_MS = 25_000;
|
|
2
|
+
export class ApprovalStreamHub {
|
|
3
|
+
options;
|
|
4
|
+
clients = new Map();
|
|
5
|
+
pending = new Map();
|
|
6
|
+
pendingCodeIndex = new Map();
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
10
|
+
init() {
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
}
|
|
13
|
+
notify(requests) {
|
|
14
|
+
for (const request of requests) {
|
|
15
|
+
this.pending.set(request.id, request);
|
|
16
|
+
this.pendingCodeIndex.set(request.code, request.id);
|
|
17
|
+
this.broadcast({ type: 'new', request });
|
|
18
|
+
}
|
|
19
|
+
return Promise.resolve();
|
|
20
|
+
}
|
|
21
|
+
updateApprovalStatus(status) {
|
|
22
|
+
this.pending.delete(status.id);
|
|
23
|
+
this.pendingCodeIndex.delete(status.code);
|
|
24
|
+
this.broadcast({
|
|
25
|
+
type: 'resolved',
|
|
26
|
+
id: status.id,
|
|
27
|
+
code: status.code,
|
|
28
|
+
action: status.result,
|
|
29
|
+
result: status.result,
|
|
30
|
+
badgeCount: status.badgeCount,
|
|
31
|
+
});
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}
|
|
34
|
+
notifyActivity(event) {
|
|
35
|
+
this.broadcast({ type: 'activity', event });
|
|
36
|
+
return Promise.resolve();
|
|
37
|
+
}
|
|
38
|
+
addClient(request, reply) {
|
|
39
|
+
reply.hijack();
|
|
40
|
+
reply.raw.writeHead(200, {
|
|
41
|
+
'Content-Type': 'text/event-stream',
|
|
42
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
43
|
+
Connection: 'keep-alive',
|
|
44
|
+
'X-Accel-Buffering': 'no',
|
|
45
|
+
});
|
|
46
|
+
const keepalive = setInterval(() => {
|
|
47
|
+
this.write(reply.raw, ': ping\n\n');
|
|
48
|
+
}, KEEPALIVE_MS);
|
|
49
|
+
keepalive.unref?.();
|
|
50
|
+
this.clients.set(reply.raw, keepalive);
|
|
51
|
+
this.write(reply.raw, ': connected\n\n');
|
|
52
|
+
for (const pendingRequest of this.pending.values()) {
|
|
53
|
+
this.writeEvent(reply.raw, { type: 'new', request: pendingRequest });
|
|
54
|
+
}
|
|
55
|
+
for (const event of [...(this.options.activityStream?.recent() ?? [])].reverse()) {
|
|
56
|
+
this.writeEvent(reply.raw, { type: 'activity', event });
|
|
57
|
+
}
|
|
58
|
+
const cleanup = () => this.removeClient(reply.raw);
|
|
59
|
+
request.raw.on('close', cleanup);
|
|
60
|
+
reply.raw.on('error', cleanup);
|
|
61
|
+
}
|
|
62
|
+
getPendingById(id) {
|
|
63
|
+
return this.pending.get(id);
|
|
64
|
+
}
|
|
65
|
+
getPendingByCode(code) {
|
|
66
|
+
const id = this.pendingCodeIndex.get(code);
|
|
67
|
+
return id ? this.pending.get(id) : undefined;
|
|
68
|
+
}
|
|
69
|
+
pendingCount() {
|
|
70
|
+
return this.pending.size;
|
|
71
|
+
}
|
|
72
|
+
stop() {
|
|
73
|
+
for (const client of this.clients.keys()) {
|
|
74
|
+
try {
|
|
75
|
+
client.end();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* swallow */
|
|
79
|
+
}
|
|
80
|
+
this.removeClient(client);
|
|
81
|
+
}
|
|
82
|
+
this.clients.clear();
|
|
83
|
+
this.pending.clear();
|
|
84
|
+
this.pendingCodeIndex.clear();
|
|
85
|
+
return Promise.resolve();
|
|
86
|
+
}
|
|
87
|
+
broadcast(data) {
|
|
88
|
+
const msg = `data: ${JSON.stringify(data)}\n\n`;
|
|
89
|
+
for (const client of this.clients.keys()) {
|
|
90
|
+
this.write(client, msg);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
writeEvent(client, data) {
|
|
94
|
+
this.write(client, `data: ${JSON.stringify(data)}\n\n`);
|
|
95
|
+
}
|
|
96
|
+
write(client, message) {
|
|
97
|
+
try {
|
|
98
|
+
client.write(message);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
this.removeClient(client);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
removeClient(client) {
|
|
105
|
+
const keepalive = this.clients.get(client);
|
|
106
|
+
if (keepalive)
|
|
107
|
+
clearInterval(keepalive);
|
|
108
|
+
this.clients.delete(client);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=approval-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-stream.js","sourceRoot":"","sources":["../../src/hitl/approval-stream.ts"],"names":[],"mappings":"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"}
|
package/dist/hitl/engine.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AuditLogger } from '../audit/logger.js';
|
|
2
2
|
import type { SandboxDisplayInfo } from '../sandbox/index.js';
|
|
3
|
+
import type { AirlockCallContext } from '../airlock/context.js';
|
|
3
4
|
import type { HitlProvider, ApprovalApi } from './providers/types.js';
|
|
4
5
|
export type HitlResult = 'approved' | 'denied' | 'timeout' | 'cancelled';
|
|
5
6
|
export interface HitlTicket {
|
|
@@ -10,31 +11,39 @@ export interface HitlTicket {
|
|
|
10
11
|
export declare class HitlEngine implements ApprovalApi {
|
|
11
12
|
private auditLogger;
|
|
12
13
|
private provider;
|
|
13
|
-
|
|
14
|
+
private _timeoutMs;
|
|
14
15
|
private pending;
|
|
15
16
|
private byCode;
|
|
16
|
-
constructor(auditLogger: AuditLogger, provider: HitlProvider,
|
|
17
|
+
constructor(auditLogger: AuditLogger, provider: HitlProvider, _timeoutMs: number);
|
|
18
|
+
get timeoutMs(): number;
|
|
19
|
+
setTimeoutMs(timeoutMs: number): void;
|
|
17
20
|
create(params: {
|
|
18
21
|
agentId: string;
|
|
19
22
|
tool: string;
|
|
20
23
|
args: Record<string, unknown>;
|
|
24
|
+
context?: AirlockCallContext;
|
|
21
25
|
sandbox?: SandboxDisplayInfo;
|
|
22
26
|
}): HitlTicket;
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
approve(id: string): void;
|
|
28
|
+
approveByCode(code: string): void;
|
|
25
29
|
/** Cancel a pending request (e.g. transport disconnected). */
|
|
26
30
|
cancel(id: string): void;
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
deny(id: string, reason?: string): void;
|
|
32
|
+
denyByCode(code: string, reason?: string): void;
|
|
29
33
|
getPending(): Array<{
|
|
30
34
|
id: string;
|
|
31
35
|
code: string;
|
|
32
36
|
agentId: string;
|
|
33
37
|
tool: string;
|
|
34
38
|
args: Record<string, unknown>;
|
|
39
|
+
context?: AirlockCallContext;
|
|
35
40
|
}>;
|
|
36
|
-
|
|
41
|
+
hasPending(id: string): boolean;
|
|
42
|
+
private resolveRequestByCode;
|
|
43
|
+
private approveRequest;
|
|
44
|
+
private denyRequest;
|
|
37
45
|
/** Recover pending requests from DB on startup */
|
|
38
46
|
recoverPending(): Promise<void>;
|
|
47
|
+
private updateApprovalStatus;
|
|
39
48
|
}
|
|
40
49
|
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/hitl/engine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAKtE,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/hitl/engine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAKtE,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAC7B;AAyBD,qBAAa,UAAW,YAAW,WAAW;IAK1C,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,UAAU;IANpB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,MAAM,CAA6B;gBAGjC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,YAAY,EACtB,UAAU,EAAE,MAAM;IAG5B,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,MAAM,CAAC,MAAM,EAAE;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,OAAO,CAAC,EAAE,kBAAkB,CAAC;QAC7B,OAAO,CAAC,EAAE,kBAAkB,CAAC;KAC9B,GAAG,UAAU;IAuCd,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IASzB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IASjC,8DAA8D;IAC9D,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAYxB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IASvC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAS/C,UAAU,IAAI,KAAK,CAAC;QAClB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,OAAO,CAAC,EAAE,kBAAkB,CAAC;KAC9B,CAAC;IAYF,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI/B,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,WAAW;IAUnB,kDAAkD;IAE5C,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IA+ErC,OAAO,CAAC,oBAAoB;CAa7B"}
|
package/dist/hitl/engine.js
CHANGED
|
@@ -1,32 +1,42 @@
|
|
|
1
1
|
import { generateId, generateApprovalCode } from '../util/id.js';
|
|
2
2
|
import { childLogger } from '../util/logger.js';
|
|
3
3
|
const log = childLogger('hitl-engine');
|
|
4
|
+
function redactApprovalArgs(auditLogger, args) {
|
|
5
|
+
return (auditLogger.redactArgs?.(args) ?? args);
|
|
6
|
+
}
|
|
4
7
|
export class HitlEngine {
|
|
5
8
|
auditLogger;
|
|
6
9
|
provider;
|
|
7
|
-
|
|
10
|
+
_timeoutMs;
|
|
8
11
|
pending = new Map(); // id → request
|
|
9
12
|
byCode = new Map(); // code → id
|
|
10
|
-
constructor(auditLogger, provider,
|
|
13
|
+
constructor(auditLogger, provider, _timeoutMs) {
|
|
11
14
|
this.auditLogger = auditLogger;
|
|
12
15
|
this.provider = provider;
|
|
13
|
-
this.
|
|
16
|
+
this._timeoutMs = _timeoutMs;
|
|
17
|
+
}
|
|
18
|
+
get timeoutMs() {
|
|
19
|
+
return this._timeoutMs;
|
|
20
|
+
}
|
|
21
|
+
setTimeoutMs(timeoutMs) {
|
|
22
|
+
this._timeoutMs = timeoutMs;
|
|
14
23
|
}
|
|
15
24
|
create(params) {
|
|
16
25
|
const id = generateId();
|
|
17
26
|
const code = generateApprovalCode();
|
|
27
|
+
const approvalArgs = redactApprovalArgs(this.auditLogger, params.args);
|
|
18
28
|
// Persist to DB synchronously before returning
|
|
19
29
|
this.auditLogger.insertHitl({
|
|
20
30
|
id,
|
|
21
31
|
code,
|
|
22
32
|
agent_id: params.agentId,
|
|
23
33
|
tool: params.tool,
|
|
24
|
-
args: JSON.stringify(params.
|
|
34
|
+
args: JSON.stringify(withContext(approvalArgs, params.context)),
|
|
25
35
|
status: 'pending',
|
|
26
36
|
created_at: new Date().toISOString(),
|
|
27
37
|
});
|
|
28
38
|
const result = new Promise((resolve) => {
|
|
29
|
-
const req = { id, code, ...params, resolve };
|
|
39
|
+
const req = { id, code, ...params, args: approvalArgs, resolve };
|
|
30
40
|
this.pending.set(id, req);
|
|
31
41
|
this.byCode.set(code, id);
|
|
32
42
|
if (this.timeoutMs > 0) {
|
|
@@ -35,6 +45,7 @@ export class HitlEngine {
|
|
|
35
45
|
this.byCode.delete(code);
|
|
36
46
|
this.auditLogger.updateHitlStatus(id, 'timeout');
|
|
37
47
|
log.warn({ id, code, tool: params.tool }, 'HITL request timed out');
|
|
48
|
+
this.updateApprovalStatus(req, 'timeout');
|
|
38
49
|
resolve('timeout');
|
|
39
50
|
}, this.timeoutMs);
|
|
40
51
|
timer.unref();
|
|
@@ -44,20 +55,21 @@ export class HitlEngine {
|
|
|
44
55
|
log.info({ id, code, agent: params.agentId, tool: params.tool }, 'HITL request created');
|
|
45
56
|
return { id, code, result };
|
|
46
57
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const req = this.resolveRequest(codeOrId);
|
|
58
|
+
approve(id) {
|
|
59
|
+
const req = this.pending.get(id);
|
|
50
60
|
if (!req) {
|
|
51
|
-
log.warn({
|
|
61
|
+
log.warn({ id }, 'No pending HITL request found');
|
|
52
62
|
return;
|
|
53
63
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
this.approveRequest(req);
|
|
65
|
+
}
|
|
66
|
+
approveByCode(code) {
|
|
67
|
+
const req = this.resolveRequestByCode(code);
|
|
68
|
+
if (!req) {
|
|
69
|
+
log.warn({ code }, 'No pending HITL request found for approval code');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.approveRequest(req);
|
|
61
73
|
}
|
|
62
74
|
/** Cancel a pending request (e.g. transport disconnected). */
|
|
63
75
|
cancel(id) {
|
|
@@ -70,22 +82,24 @@ export class HitlEngine {
|
|
|
70
82
|
this.byCode.delete(req.code);
|
|
71
83
|
this.auditLogger.updateHitlStatus(req.id, 'cancelled');
|
|
72
84
|
log.info({ id: req.id, code: req.code }, 'HITL cancelled (session disconnected)');
|
|
85
|
+
this.updateApprovalStatus(req, 'cancelled');
|
|
73
86
|
req.resolve('cancelled');
|
|
74
87
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const req = this.resolveRequest(codeOrId);
|
|
88
|
+
deny(id, reason) {
|
|
89
|
+
const req = this.pending.get(id);
|
|
78
90
|
if (!req) {
|
|
79
|
-
log.warn({
|
|
91
|
+
log.warn({ id }, 'No pending HITL request found');
|
|
80
92
|
return;
|
|
81
93
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
this.denyRequest(req, reason);
|
|
95
|
+
}
|
|
96
|
+
denyByCode(code, reason) {
|
|
97
|
+
const req = this.resolveRequestByCode(code);
|
|
98
|
+
if (!req) {
|
|
99
|
+
log.warn({ code }, 'No pending HITL request found for approval code');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
this.denyRequest(req, reason);
|
|
89
103
|
}
|
|
90
104
|
getPending() {
|
|
91
105
|
return Array.from(this.pending.values()).map((r) => ({
|
|
@@ -94,19 +108,39 @@ export class HitlEngine {
|
|
|
94
108
|
agentId: r.agentId,
|
|
95
109
|
tool: r.tool,
|
|
96
110
|
args: r.args,
|
|
111
|
+
...(r.context ? { context: r.context } : {}),
|
|
97
112
|
...(r.sandbox ? { sandbox: r.sandbox } : {}),
|
|
98
113
|
}));
|
|
99
114
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const id = this.byCode.get(codeOrId);
|
|
115
|
+
hasPending(id) {
|
|
116
|
+
return this.pending.has(id);
|
|
117
|
+
}
|
|
118
|
+
resolveRequestByCode(code) {
|
|
119
|
+
const id = this.byCode.get(code);
|
|
106
120
|
if (id)
|
|
107
121
|
return this.pending.get(id);
|
|
108
122
|
return undefined;
|
|
109
123
|
}
|
|
124
|
+
approveRequest(req) {
|
|
125
|
+
if (req.timer)
|
|
126
|
+
clearTimeout(req.timer);
|
|
127
|
+
this.pending.delete(req.id);
|
|
128
|
+
this.byCode.delete(req.code);
|
|
129
|
+
this.auditLogger.updateHitlStatus(req.id, 'approved');
|
|
130
|
+
log.info({ id: req.id, code: req.code }, 'HITL approved');
|
|
131
|
+
this.updateApprovalStatus(req, 'approved');
|
|
132
|
+
req.resolve('approved');
|
|
133
|
+
}
|
|
134
|
+
denyRequest(req, reason) {
|
|
135
|
+
if (req.timer)
|
|
136
|
+
clearTimeout(req.timer);
|
|
137
|
+
this.pending.delete(req.id);
|
|
138
|
+
this.byCode.delete(req.code);
|
|
139
|
+
this.auditLogger.updateHitlStatus(req.id, 'denied', reason);
|
|
140
|
+
log.info({ id: req.id, code: req.code, reason }, 'HITL denied');
|
|
141
|
+
this.updateApprovalStatus(req, 'denied');
|
|
142
|
+
req.resolve('denied');
|
|
143
|
+
}
|
|
110
144
|
/** Recover pending requests from DB on startup */
|
|
111
145
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
112
146
|
async recoverPending() {
|
|
@@ -128,12 +162,15 @@ export class HitlEngine {
|
|
|
128
162
|
remaining = this.timeoutMs - elapsed;
|
|
129
163
|
if (remaining <= 0) {
|
|
130
164
|
this.auditLogger.updateHitlStatus(row.id, 'timeout');
|
|
165
|
+
this.updateApprovalStatus({ id: row.id, code: row.code }, 'timeout');
|
|
131
166
|
continue;
|
|
132
167
|
}
|
|
133
168
|
}
|
|
134
169
|
else {
|
|
135
170
|
remaining = 0; // no timeout
|
|
136
171
|
}
|
|
172
|
+
const context = readContext(args);
|
|
173
|
+
const cleanArgs = stripStoredContext(args);
|
|
137
174
|
// Re-arm without re-notifying (provider may not be ready yet)
|
|
138
175
|
const promise = new Promise((resolve) => {
|
|
139
176
|
const req = {
|
|
@@ -141,7 +178,8 @@ export class HitlEngine {
|
|
|
141
178
|
code: row.code,
|
|
142
179
|
agentId: row.agent_id,
|
|
143
180
|
tool: row.tool,
|
|
144
|
-
args,
|
|
181
|
+
args: cleanArgs,
|
|
182
|
+
...(context ? { context } : {}),
|
|
145
183
|
resolve,
|
|
146
184
|
};
|
|
147
185
|
this.pending.set(row.id, req);
|
|
@@ -151,6 +189,7 @@ export class HitlEngine {
|
|
|
151
189
|
this.pending.delete(row.id);
|
|
152
190
|
this.byCode.delete(row.code);
|
|
153
191
|
this.auditLogger.updateHitlStatus(row.id, 'timeout');
|
|
192
|
+
this.updateApprovalStatus(req, 'timeout');
|
|
154
193
|
resolve('timeout');
|
|
155
194
|
}, remaining);
|
|
156
195
|
timer.unref();
|
|
@@ -165,14 +204,49 @@ export class HitlEngine {
|
|
|
165
204
|
code: row.code,
|
|
166
205
|
agentId: row.agent_id,
|
|
167
206
|
tool: row.tool,
|
|
168
|
-
args,
|
|
207
|
+
args: cleanArgs,
|
|
208
|
+
...(context ? { context } : {}),
|
|
169
209
|
sandbox: undefined,
|
|
170
210
|
timeoutMs: remaining,
|
|
211
|
+
badgeCount: this.getPending().length,
|
|
171
212
|
},
|
|
172
213
|
])
|
|
173
214
|
.catch((err) => log.warn({ err }, 'Failed to re-notify recovered HITL request'));
|
|
174
215
|
void promise; // tracked in pending map
|
|
175
216
|
}
|
|
176
217
|
}
|
|
218
|
+
updateApprovalStatus(req, result) {
|
|
219
|
+
const badgeCount = this.getPending().length;
|
|
220
|
+
if (this.provider.updateApprovalStatus) {
|
|
221
|
+
void this.provider
|
|
222
|
+
.updateApprovalStatus({ id: req.id, code: req.code, result, badgeCount })
|
|
223
|
+
.catch((err) => log.warn({ err }, 'Failed to update approval status'));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
void this.provider
|
|
227
|
+
.updateBadge?.(badgeCount)
|
|
228
|
+
.catch((err) => log.warn({ err }, 'Failed to update approval badge count'));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function withContext(args, context) {
|
|
232
|
+
if (!context)
|
|
233
|
+
return args;
|
|
234
|
+
return { ...args, _airlock: context };
|
|
235
|
+
}
|
|
236
|
+
function readContext(args) {
|
|
237
|
+
const raw = args._airlock;
|
|
238
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
239
|
+
return undefined;
|
|
240
|
+
const source = raw;
|
|
241
|
+
const context = {};
|
|
242
|
+
if (typeof source.reason === 'string')
|
|
243
|
+
context.reason = source.reason;
|
|
244
|
+
if (typeof source.note === 'string')
|
|
245
|
+
context.note = source.note;
|
|
246
|
+
return context.reason || context.note ? context : undefined;
|
|
247
|
+
}
|
|
248
|
+
function stripStoredContext(args) {
|
|
249
|
+
const { _airlock: _ignored, ...cleanArgs } = args;
|
|
250
|
+
return cleanArgs;
|
|
177
251
|
}
|
|
178
252
|
//# sourceMappingURL=engine.js.map
|