@linkshell/gateway 0.4.55 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/Dockerfile +11 -3
  2. package/dist/gateway/src/embedded.d.ts +5 -0
  3. package/dist/gateway/src/embedded.js +23 -25
  4. package/dist/gateway/src/embedded.js.map +1 -1
  5. package/dist/gateway/src/index.js +47 -27
  6. package/dist/gateway/src/index.js.map +1 -1
  7. package/dist/gateway/src/relay.js +0 -13
  8. package/dist/gateway/src/relay.js.map +1 -1
  9. package/dist/gateway/tsconfig.tsbuildinfo +1 -1
  10. package/dist/gateway-v2/src/accounts.d.ts +6 -0
  11. package/dist/gateway-v2/src/accounts.js +32 -0
  12. package/dist/gateway-v2/src/accounts.js.map +1 -0
  13. package/dist/gateway-v2/src/index.d.ts +3 -0
  14. package/dist/gateway-v2/src/index.js +4 -0
  15. package/dist/gateway-v2/src/index.js.map +1 -0
  16. package/dist/gateway-v2/src/main.d.ts +1 -0
  17. package/dist/gateway-v2/src/main.js +17 -0
  18. package/dist/gateway-v2/src/main.js.map +1 -0
  19. package/dist/gateway-v2/src/server.d.ts +69 -0
  20. package/dist/gateway-v2/src/server.js +387 -0
  21. package/dist/gateway-v2/src/server.js.map +1 -0
  22. package/dist/gateway-v2/src/store.d.ts +30 -0
  23. package/dist/gateway-v2/src/store.js +102 -0
  24. package/dist/gateway-v2/src/store.js.map +1 -0
  25. package/dist/shared-protocol/src/index.d.ts +1495 -1339
  26. package/dist/shared-protocol/src/index.js +6 -0
  27. package/dist/shared-protocol/src/index.js.map +1 -1
  28. package/dist/wire/src/crypto.d.ts +65 -0
  29. package/dist/wire/src/crypto.js +253 -0
  30. package/dist/wire/src/crypto.js.map +1 -0
  31. package/dist/wire/src/index.d.ts +8 -0
  32. package/dist/wire/src/index.js +9 -0
  33. package/dist/wire/src/index.js.map +1 -0
  34. package/dist/wire/src/model.d.ts +751 -0
  35. package/dist/wire/src/model.js +176 -0
  36. package/dist/wire/src/model.js.map +1 -0
  37. package/dist/wire/src/peer.d.ts +42 -0
  38. package/dist/wire/src/peer.js +143 -0
  39. package/dist/wire/src/peer.js.map +1 -0
  40. package/dist/wire/src/relay-client.d.ts +82 -0
  41. package/dist/wire/src/relay-client.js +259 -0
  42. package/dist/wire/src/relay-client.js.map +1 -0
  43. package/dist/wire/src/relay.d.ts +481 -0
  44. package/dist/wire/src/relay.js +129 -0
  45. package/dist/wire/src/relay.js.map +1 -0
  46. package/dist/wire/src/rpc.d.ts +4835 -0
  47. package/dist/wire/src/rpc.js +357 -0
  48. package/dist/wire/src/rpc.js.map +1 -0
  49. package/dist/wire/src/shell.d.ts +2 -0
  50. package/dist/wire/src/shell.js +62 -0
  51. package/dist/wire/src/shell.js.map +1 -0
  52. package/dist/wire/src/updates.d.ts +3325 -0
  53. package/dist/wire/src/updates.js +210 -0
  54. package/dist/wire/src/updates.js.map +1 -0
  55. package/package.json +7 -2
  56. package/src/embedded.ts +31 -1
  57. package/src/index.ts +45 -2
  58. package/dist/gateway/src/agent-permission-http.d.ts +0 -119
  59. package/dist/gateway/src/agent-permission-http.js +0 -234
  60. package/dist/gateway/src/agent-permission-http.js.map +0 -1
@@ -0,0 +1,210 @@
1
+ import { z } from "zod";
2
+ import { permissionOptionSchema, sessionDriverSchema, sessionStateSchema, toolKindSchema } from "./model.js";
3
+ // Session updates mirror ACP's `SessionUpdate` shapes (discriminated by
4
+ // `sessionUpdate`) so one renderer works for every agent. LinkShell-specific
5
+ // updates use the `ls_` prefix.
6
+ export const contentBlockSchema = z.discriminatedUnion("type", [
7
+ z.object({ type: z.literal("text"), text: z.string() }),
8
+ z.object({
9
+ type: z.literal("image"),
10
+ mimeType: z.string(),
11
+ data: z.string().optional(),
12
+ uri: z.string().optional(),
13
+ }),
14
+ z.object({
15
+ type: z.literal("resource_link"),
16
+ uri: z.string(),
17
+ name: z.string(),
18
+ /** What the link names: a file (default), a skill the user invoked, or another agent. */
19
+ kind: z.enum(["file", "skill", "agent"]).optional(),
20
+ }),
21
+ ]);
22
+ export const toolCallStatusSchema = z.enum(["pending", "in_progress", "completed", "failed"]);
23
+ export const toolCallContentSchema = z.discriminatedUnion("type", [
24
+ z.object({ type: z.literal("content"), content: contentBlockSchema }),
25
+ /** ACP diff: full before/after text. */
26
+ z.object({
27
+ type: z.literal("diff"),
28
+ path: z.string(),
29
+ oldText: z.string().nullable().optional(),
30
+ newText: z.string(),
31
+ }),
32
+ /** Unified diff, for agents (Codex) that report patches rather than file text. */
33
+ z.object({
34
+ type: z.literal("patch"),
35
+ path: z.string(),
36
+ change: z.enum(["add", "delete", "update"]),
37
+ movePath: z.string().optional(),
38
+ diff: z.string(),
39
+ }),
40
+ z.object({ type: z.literal("terminal"), terminalId: z.string() }),
41
+ ]);
42
+ export const toolCallLocationSchema = z.object({
43
+ path: z.string(),
44
+ line: z.number().int().optional(),
45
+ });
46
+ /**
47
+ * What a tool call is, when the agent tells us more than ACP's coarse `kind`.
48
+ * Host drivers fill it from each agent's own tool names; clients render it the
49
+ * same way for every agent and fall back to `title` + `kind` without it.
50
+ */
51
+ export const toolDetailSchema = z.discriminatedUnion("type", [
52
+ z.object({ type: z.literal("mcp"), server: z.string(), tool: z.string() }),
53
+ z.object({
54
+ type: z.literal("subagent"),
55
+ /** spawn starts a sub-agent (its work nests under this call); the rest talk to one. */
56
+ action: z.enum(["spawn", "message", "wait", "stop", "resume", "list"]).default("spawn"),
57
+ task: z.string().optional(),
58
+ /** The sub-agent's role or type, e.g. "Explore". */
59
+ agentType: z.string().optional(),
60
+ model: z.string().optional(),
61
+ }),
62
+ z.object({ type: z.literal("skill"), name: z.string(), description: z.string().optional() }),
63
+ z.object({ type: z.literal("web_search"), query: z.string().optional() }),
64
+ z.object({ type: z.literal("image_generation"), prompt: z.string().optional() }),
65
+ z.object({ type: z.literal("compaction") }),
66
+ z.object({ type: z.literal("review"), phase: z.enum(["started", "finished"]) }),
67
+ ]);
68
+ export const planEntrySchema = z.object({
69
+ content: z.string(),
70
+ priority: z.enum(["high", "medium", "low"]).default("medium"),
71
+ status: z.enum(["pending", "in_progress", "completed"]),
72
+ });
73
+ export const stopReasonSchema = z.enum(["end_turn", "cancelled", "error", "max_tokens", "refusal"]);
74
+ export const sessionConfigOptionSchema = z.object({
75
+ /** Agent-defined id; "model" and "mode" are the common ones. */
76
+ id: z.string(),
77
+ name: z.string(),
78
+ category: z.enum(["model", "mode", "effort", "other"]).default("other"),
79
+ current: z.string(),
80
+ values: z.array(z.object({ value: z.string(), name: z.string(), description: z.string().optional() })),
81
+ });
82
+ export const sessionUpdateSchema = z.discriminatedUnion("sessionUpdate", [
83
+ z.object({
84
+ sessionUpdate: z.literal("user_message_chunk"),
85
+ messageId: z.string().optional(),
86
+ content: contentBlockSchema,
87
+ }),
88
+ z.object({
89
+ sessionUpdate: z.literal("agent_message_chunk"),
90
+ parentToolCallId: z.string().optional(),
91
+ messageId: z.string(),
92
+ content: contentBlockSchema,
93
+ }),
94
+ z.object({
95
+ sessionUpdate: z.literal("agent_thought_chunk"),
96
+ parentToolCallId: z.string().optional(),
97
+ messageId: z.string(),
98
+ content: contentBlockSchema,
99
+ }),
100
+ z.object({
101
+ sessionUpdate: z.literal("tool_call"),
102
+ toolCallId: z.string(),
103
+ /** Set when a sub-agent made this call: the sub-agent's own (spawn) tool call. */
104
+ parentToolCallId: z.string().optional(),
105
+ title: z.string(),
106
+ kind: toolKindSchema,
107
+ status: toolCallStatusSchema,
108
+ rawInput: z.unknown().optional(),
109
+ content: z.array(toolCallContentSchema).optional(),
110
+ locations: z.array(toolCallLocationSchema).optional(),
111
+ detail: toolDetailSchema.optional(),
112
+ }),
113
+ z.object({
114
+ sessionUpdate: z.literal("tool_call_update"),
115
+ toolCallId: z.string(),
116
+ parentToolCallId: z.string().optional(),
117
+ status: toolCallStatusSchema.optional(),
118
+ title: z.string().optional(),
119
+ detail: toolDetailSchema.optional(),
120
+ /** Replaces the tool call's content when present. */
121
+ content: z.array(toolCallContentSchema).optional(),
122
+ /** Streamed output appended to the tool call's running output. */
123
+ appendOutput: z.string().optional(),
124
+ rawOutput: z.unknown().optional(),
125
+ }),
126
+ z.object({
127
+ sessionUpdate: z.literal("plan"),
128
+ entries: z.array(planEntrySchema),
129
+ }),
130
+ z.object({
131
+ sessionUpdate: z.literal("available_commands_update"),
132
+ availableCommands: z.array(z.object({ name: z.string(), description: z.string(), hint: z.string().optional() })),
133
+ }),
134
+ z.object({
135
+ sessionUpdate: z.literal("current_mode_update"),
136
+ currentModeId: z.string(),
137
+ }),
138
+ z.object({
139
+ sessionUpdate: z.literal("session_info_update"),
140
+ title: z.string().optional(),
141
+ model: z.string().optional(),
142
+ }),
143
+ z.object({
144
+ sessionUpdate: z.literal("usage_update"),
145
+ usedTokens: z.number().optional(),
146
+ contextWindow: z.number().optional(),
147
+ }),
148
+ // ── LinkShell extensions ──
149
+ z.object({
150
+ sessionUpdate: z.literal("ls_message_done"),
151
+ parentToolCallId: z.string().optional(),
152
+ messageId: z.string(),
153
+ role: z.enum(["agent", "thought", "user"]),
154
+ }),
155
+ /** Selectable session settings (model, permission mode, effort…) as the agent reports them. */
156
+ z.object({
157
+ sessionUpdate: z.literal("ls_config"),
158
+ options: z.array(sessionConfigOptionSchema),
159
+ }),
160
+ z.object({
161
+ sessionUpdate: z.literal("ls_notice"),
162
+ level: z.enum(["info", "warning"]),
163
+ title: z.string(),
164
+ detail: z.string().optional(),
165
+ }),
166
+ z.object({
167
+ sessionUpdate: z.literal("ls_turn"),
168
+ /** A sub-agent's turn: whether the sub-agent under this call is working. */
169
+ parentToolCallId: z.string().optional(),
170
+ state: z.enum(["started", "ended"]),
171
+ turnId: z.string().optional(),
172
+ stopReason: stopReasonSchema.optional(),
173
+ }),
174
+ z.object({
175
+ sessionUpdate: z.literal("ls_permission"),
176
+ requestId: z.string(),
177
+ toolCallId: z.string().optional(),
178
+ title: z.string(),
179
+ detail: z.string().optional(),
180
+ options: z.array(permissionOptionSchema).min(1),
181
+ }),
182
+ z.object({
183
+ sessionUpdate: z.literal("ls_permission_resolved"),
184
+ requestId: z.string(),
185
+ optionId: z.string().optional(),
186
+ }),
187
+ z.object({
188
+ sessionUpdate: z.literal("ls_driver"),
189
+ driver: sessionDriverSchema,
190
+ deviceId: z.string().optional(),
191
+ }),
192
+ z.object({
193
+ sessionUpdate: z.literal("ls_status"),
194
+ state: sessionStateSchema,
195
+ }),
196
+ z.object({
197
+ sessionUpdate: z.literal("ls_error"),
198
+ code: z.string(),
199
+ message: z.string(),
200
+ hint: z.string().optional(),
201
+ }),
202
+ ]);
203
+ /** One entry in a session's append-only event log. */
204
+ export const sessionEventSchema = z.object({
205
+ sessionId: z.string(),
206
+ seq: z.number().int().positive(),
207
+ ts: z.number(),
208
+ update: sessionUpdateSchema,
209
+ });
210
+ //# sourceMappingURL=updates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updates.js","sourceRoot":"","sources":["../../../../wire/src/updates.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linkshell/gateway",
3
- "version": "0.4.55",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "main": "dist/gateway/src/index.js",
6
6
  "exports": {
@@ -28,13 +28,18 @@
28
28
  "dependencies": {
29
29
  "zod": "^3.24.3",
30
30
  "ws": "^8.18.1",
31
- "@linkshell/protocol": "0.4.10"
31
+ "@linkshell/protocol": "0.4.10",
32
+ "@linkshell/wire": "0.1.1",
33
+ "@linkshell/gateway-v2": "0.1.1"
32
34
  },
33
35
  "devDependencies": {
34
36
  "@types/ws": "^8.5.14",
35
37
  "tsx": "^4.19.3",
36
38
  "vitest": "^3.2.1"
37
39
  },
40
+ "engines": {
41
+ "node": ">=22.13"
42
+ },
38
43
  "scripts": {
39
44
  "build": "tsc -p tsconfig.json",
40
45
  "typecheck": "tsc -p tsconfig.json --noEmit",
package/src/embedded.ts CHANGED
@@ -28,6 +28,11 @@ export interface EmbeddedGatewayOptions {
28
28
  port?: number;
29
29
  logLevel?: "debug" | "info" | "warn" | "error";
30
30
  silent?: boolean;
31
+ /**
32
+ * Also serve the v2 relay (end-to-end encrypted, at /v2/connect), keeping its
33
+ * pairings in SQLite here. Needs Node.js 22.13+ (node:sqlite).
34
+ */
35
+ relayDataPath?: string;
31
36
  }
32
37
 
33
38
  export interface EmbeddedGateway {
@@ -91,7 +96,7 @@ function extractBearerToken(req: IncomingMessage): string | null {
91
96
  * Start an embedded gateway. Returns a handle to get URLs and close it.
92
97
  * Used by CLI when no external --gateway is provided.
93
98
  */
94
- export function startEmbeddedGateway(
99
+ export async function startEmbeddedGateway(
95
100
  options: EmbeddedGatewayOptions = {},
96
101
  ): Promise<EmbeddedGateway> {
97
102
  const targetPort = options.port ?? 0; // 0 = random available port
@@ -272,9 +277,17 @@ export function startEmbeddedGateway(
272
277
  maxPayload: MAX_WS_MESSAGE_SIZE,
273
278
  });
274
279
 
280
+ // Loaded on demand: v1 users on older Node never touch node:sqlite.
281
+ const relay = options.relayDataPath ? await startRelay(options.relayDataPath, log) : undefined;
282
+
275
283
  server.on("upgrade", (request, socket, head) => {
276
284
  const url = new URL(request.url ?? "/", `http://${request.headers.host}`);
277
285
 
286
+ if (relay && url.pathname === relay.path) {
287
+ relay.gateway.handleUpgrade(request, socket, head);
288
+ return;
289
+ }
290
+
278
291
  // Tunnel WebSocket upgrade (for HMR etc.)
279
292
  const tunnelParsed = parseTunnelPath(url.pathname);
280
293
  if (tunnelParsed) {
@@ -488,9 +501,26 @@ export function startEmbeddedGateway(
488
501
  sessionManager.destroy();
489
502
  pairingManager.destroy();
490
503
  tokenManager.destroy();
504
+ void relay?.gateway.stop();
491
505
  server.close(() => res());
492
506
  }),
493
507
  });
494
508
  });
495
509
  });
496
510
  }
511
+
512
+ async function startRelay(databasePath: string, log: (level: "debug" | "info" | "warn" | "error", message: string) => void) {
513
+ const [major = 0, minor = 0] = process.versions.node.split(".").map(Number);
514
+ if (major < 22 || (major === 22 && minor < 13)) {
515
+ log("warn", `v2 relay needs Node.js 22.13+ (this is ${process.version}); serving v1 only`);
516
+ return undefined;
517
+ }
518
+ const { mkdirSync } = await import("node:fs");
519
+ const { dirname } = await import("node:path");
520
+ const [{ Gateway }, { RELAY_PATH }] = await Promise.all([import("@linkshell/gateway-v2"), import("@linkshell/wire")]);
521
+ mkdirSync(dirname(databasePath), { recursive: true });
522
+ const gateway = new Gateway({ port: 0, databasePath, log: (message) => log("info", `[relay] ${message}`) });
523
+ gateway.attach();
524
+ log("info", `v2 relay on ${RELAY_PATH}`);
525
+ return { gateway, path: RELAY_PATH };
526
+ }
package/src/index.ts CHANGED
@@ -24,7 +24,11 @@ import {
24
24
  handleTunnelRequest,
25
25
  cleanupSessionTunnels,
26
26
  } from "./tunnel.js";
27
- import { AUTH_REQUIRED, requireAuth, checkWsAuth, validateRequest, checkSubscriptionByUserId, canReadSessionDetail } from "./auth-middleware.js";
27
+ import { AUTH_REQUIRED, SUPABASE_ANON_KEY, SUPABASE_URL, requireAuth, checkWsAuth, validateRequest, checkSubscriptionByUserId, canReadSessionDetail } from "./auth-middleware.js";
28
+ import { mkdirSync } from "node:fs";
29
+ import { dirname } from "node:path";
30
+ import { Gateway as RelayGateway, supabaseVerifier } from "@linkshell/gateway-v2";
31
+ import { RELAY_PATH } from "@linkshell/wire";
28
32
  import { setCors } from "./cors.js";
29
33
  import { serveWeb, serveWebAsset, webEnabled, webDistPath } from "./static-web.js";
30
34
 
@@ -282,7 +286,7 @@ async function handleRequest(
282
286
 
283
287
  // Health check
284
288
  if (method === "GET" && url.pathname === "/healthz") {
285
- json(res, 200, { ok: true, version: GATEWAY_VERSION });
289
+ json(res, 200, { ok: true, version: GATEWAY_VERSION, relay: relay.connected });
286
290
  return;
287
291
  }
288
292
 
@@ -508,9 +512,47 @@ const wss = new WebSocketServer({
508
512
  maxPayload: MAX_WS_MESSAGE_SIZE,
509
513
  });
510
514
 
515
+ // ── v2 relay (end-to-end encrypted, routes ciphertext only) ─────────
516
+ // Served on the same port at RELAY_PATH. Pairings and machine/device keys are
517
+ // kept in SQLite at RELAY_DATA_PATH (a persistent volume in production).
518
+ const relayDataPath = process.env.RELAY_DATA_PATH ?? "./data/relay.db";
519
+ mkdirSync(dirname(relayDataPath), { recursive: true });
520
+ const relay = new RelayGateway({
521
+ port: 0,
522
+ databasePath: relayDataPath,
523
+ verifyToken: SUPABASE_URL && SUPABASE_ANON_KEY ? supabaseVerifier(SUPABASE_URL, SUPABASE_ANON_KEY) : undefined,
524
+ log: (message) => log("info", `[relay] ${message}`),
525
+ // The official gateway is a subscription service: a computer needs an active
526
+ // Pro account to use it (as with v1). Devices only reach computers they're
527
+ // paired with or share an account with, so they need nothing more.
528
+ admit: AUTH_REQUIRED
529
+ ? async ({ role, userId }) => {
530
+ if (role !== "machine") return undefined;
531
+ if (!userId) return "官方网关需要登录 Pro 账号:在电脑上运行 linkshell login";
532
+ const subscription = await checkSubscriptionByUserId(userId);
533
+ // A lookup failure isn't a lapsed subscription; don't lock people out over it.
534
+ if (subscription.status === "inactive") return "官方网关需要 Pro 订阅:https://itool.tech";
535
+ if (subscription.status === "unknown") log("warn", `[relay] subscription check unavailable (${subscription.reason}); admitting ${userId}`);
536
+ return undefined;
537
+ }
538
+ : undefined,
539
+ });
540
+ relay.attach();
541
+
511
542
  server.on("upgrade", (request, socket, head) => {
512
543
  const url = new URL(request.url ?? "/", `http://${request.headers.host}`);
513
544
 
545
+ if (url.pathname === RELAY_PATH) {
546
+ const ip = getClientIp(request);
547
+ if (!isRateLimitBypassed(ip) && !wsConnectLimiter.allow(ip)) {
548
+ socket.write("HTTP/1.1 429 Too Many Requests\r\n\r\n");
549
+ socket.destroy();
550
+ return;
551
+ }
552
+ relay.handleUpgrade(request, socket, head);
553
+ return;
554
+ }
555
+
514
556
  // Tunnel WebSocket upgrade (for HMR etc.)
515
557
  const tunnelParsed = parseTunnelPath(url.pathname);
516
558
  if (tunnelParsed) {
@@ -851,6 +893,7 @@ function shutdown() {
851
893
  pairingManager.destroy();
852
894
  tokenManager.destroy();
853
895
  hostAuthManager.destroy();
896
+ void relay.stop();
854
897
  server.close(() => {
855
898
  process.stdout.write("[gateway] stopped\n");
856
899
  process.exit(0);
@@ -1,119 +0,0 @@
1
- import { z } from "zod";
2
- import type { SessionManager } from "./sessions.js";
3
- import type { TokenManager } from "./tokens.js";
4
- export declare const agentPermissionHttpBodySchema: z.ZodDiscriminatedUnion<"protocol", [z.ZodObject<{
5
- protocol: z.ZodLiteral<"v2">;
6
- sessionId: z.ZodString;
7
- conversationId: z.ZodString;
8
- requestId: z.ZodString;
9
- outcome: z.ZodEnum<["allow", "deny", "cancelled"]>;
10
- optionId: z.ZodOptional<z.ZodString>;
11
- terminalId: z.ZodOptional<z.ZodString>;
12
- agentSessionId: z.ZodOptional<z.ZodString>;
13
- }, "strip", z.ZodTypeAny, {
14
- protocol: "v2";
15
- sessionId: string;
16
- conversationId: string;
17
- requestId: string;
18
- outcome: "allow" | "deny" | "cancelled";
19
- optionId?: string | undefined;
20
- terminalId?: string | undefined;
21
- agentSessionId?: string | undefined;
22
- }, {
23
- protocol: "v2";
24
- sessionId: string;
25
- conversationId: string;
26
- requestId: string;
27
- outcome: "allow" | "deny" | "cancelled";
28
- optionId?: string | undefined;
29
- terminalId?: string | undefined;
30
- agentSessionId?: string | undefined;
31
- }>, z.ZodObject<{
32
- protocol: z.ZodLiteral<"legacy">;
33
- sessionId: z.ZodString;
34
- conversationId: z.ZodOptional<z.ZodString>;
35
- agentSessionId: z.ZodOptional<z.ZodString>;
36
- requestId: z.ZodString;
37
- outcome: z.ZodEnum<["allow", "deny", "cancelled"]>;
38
- optionId: z.ZodOptional<z.ZodString>;
39
- terminalId: z.ZodOptional<z.ZodString>;
40
- }, "strip", z.ZodTypeAny, {
41
- protocol: "legacy";
42
- sessionId: string;
43
- requestId: string;
44
- outcome: "allow" | "deny" | "cancelled";
45
- conversationId?: string | undefined;
46
- optionId?: string | undefined;
47
- terminalId?: string | undefined;
48
- agentSessionId?: string | undefined;
49
- }, {
50
- protocol: "legacy";
51
- sessionId: string;
52
- requestId: string;
53
- outcome: "allow" | "deny" | "cancelled";
54
- conversationId?: string | undefined;
55
- optionId?: string | undefined;
56
- terminalId?: string | undefined;
57
- agentSessionId?: string | undefined;
58
- }>, z.ZodObject<{
59
- protocol: z.ZodLiteral<"terminal">;
60
- sessionId: z.ZodString;
61
- conversationId: z.ZodOptional<z.ZodString>;
62
- requestId: z.ZodString;
63
- outcome: z.ZodEnum<["allow", "deny", "cancelled"]>;
64
- optionId: z.ZodOptional<z.ZodString>;
65
- terminalId: z.ZodOptional<z.ZodString>;
66
- agentSessionId: z.ZodOptional<z.ZodString>;
67
- }, "strip", z.ZodTypeAny, {
68
- protocol: "terminal";
69
- sessionId: string;
70
- requestId: string;
71
- outcome: "allow" | "deny" | "cancelled";
72
- conversationId?: string | undefined;
73
- optionId?: string | undefined;
74
- terminalId?: string | undefined;
75
- agentSessionId?: string | undefined;
76
- }, {
77
- protocol: "terminal";
78
- sessionId: string;
79
- requestId: string;
80
- outcome: "allow" | "deny" | "cancelled";
81
- conversationId?: string | undefined;
82
- optionId?: string | undefined;
83
- terminalId?: string | undefined;
84
- agentSessionId?: string | undefined;
85
- }>]>;
86
- export type AgentPermissionHttpBody = z.infer<typeof agentPermissionHttpBodySchema>;
87
- export type AgentPermissionHttpResult = {
88
- status: number;
89
- forwarded?: Array<{
90
- type: string;
91
- terminalId?: string;
92
- }>;
93
- ack?: AgentPermissionAck;
94
- body: {
95
- ok?: true;
96
- error?: "unauthorized" | "session_not_found" | "host_not_connected" | "invalid_payload" | "permission_not_delivered" | "permission_ack_timeout";
97
- message?: string;
98
- resolved?: boolean;
99
- delivered?: boolean;
100
- };
101
- };
102
- export type AgentPermissionAck = {
103
- requestId: string;
104
- decision: "allow" | "deny";
105
- resolved: boolean;
106
- delivered: boolean;
107
- source?: string;
108
- message?: string;
109
- };
110
- export declare function resolveAgentPermissionHttpAck(input: {
111
- sessionId: string;
112
- ack: AgentPermissionAck;
113
- }): boolean;
114
- export declare function forwardAgentPermissionHttp(input: {
115
- token: string | null;
116
- body: AgentPermissionHttpBody;
117
- sessionManager: SessionManager;
118
- tokenManager: TokenManager;
119
- }): Promise<AgentPermissionHttpResult>;