@finalai/final-agent-provider 0.10.1 → 0.12.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.
Files changed (198) hide show
  1. package/HOSTING.md +334 -245
  2. package/README.md +125 -46
  3. package/WORKSPACE-EGRESS.md +11 -11
  4. package/dist/modules/catalog.d.mts +9 -0
  5. package/dist/modules/catalog.mjs +21 -0
  6. package/dist/modules/catalog.mjs.map +7 -0
  7. package/dist/modules/harnesses/acp-agent.d.mts +4 -0
  8. package/dist/modules/harnesses/acp-agent.mjs +35 -0
  9. package/dist/modules/harnesses/acp-agent.mjs.map +7 -0
  10. package/dist/modules/harnesses/adapters.d.mts +7 -0
  11. package/dist/modules/harnesses/adapters.mjs +74 -0
  12. package/dist/modules/harnesses/adapters.mjs.map +7 -0
  13. package/dist/modules/harnesses/claude-code.d.mts +14 -0
  14. package/dist/modules/harnesses/claude-code.mjs +79 -0
  15. package/dist/modules/harnesses/claude-code.mjs.map +7 -0
  16. package/dist/modules/harnesses/codex.d.mts +12 -0
  17. package/dist/modules/harnesses/codex.mjs +90 -0
  18. package/dist/modules/harnesses/codex.mjs.map +7 -0
  19. package/dist/modules/harnesses/config-fields.d.mts +98 -57
  20. package/dist/modules/harnesses/config-fields.mjs +58 -35
  21. package/dist/modules/harnesses/config-fields.mjs.map +3 -3
  22. package/dist/modules/harnesses/definitions.d.mts +3 -0
  23. package/dist/modules/harnesses/definitions.mjs +21 -0
  24. package/dist/modules/harnesses/definitions.mjs.map +7 -0
  25. package/dist/modules/harnesses/fidelity.d.mts +1 -1
  26. package/dist/modules/harnesses/fidelity.mjs +17 -14
  27. package/dist/modules/harnesses/fidelity.mjs.map +2 -2
  28. package/dist/modules/harnesses/final.d.mts +12 -0
  29. package/dist/modules/harnesses/final.mjs +70 -0
  30. package/dist/modules/harnesses/final.mjs.map +7 -0
  31. package/dist/modules/harnesses/index.d.mts +13 -12
  32. package/dist/modules/harnesses/index.mjs +86 -45
  33. package/dist/modules/harnesses/index.mjs.map +3 -3
  34. package/dist/modules/harnesses/names.d.mts +43 -0
  35. package/dist/modules/harnesses/names.mjs +59 -0
  36. package/dist/modules/harnesses/names.mjs.map +7 -0
  37. package/dist/modules/harnesses/nanocode-claude.d.mts +18 -0
  38. package/dist/modules/harnesses/nanocode-claude.mjs +101 -0
  39. package/dist/modules/harnesses/nanocode-claude.mjs.map +7 -0
  40. package/dist/modules/harnesses/nanocode-codex.d.mts +13 -0
  41. package/dist/modules/harnesses/nanocode-codex.mjs +64 -0
  42. package/dist/modules/harnesses/nanocode-codex.mjs.map +7 -0
  43. package/dist/modules/harnesses/pi.d.mts +12 -0
  44. package/dist/modules/harnesses/pi.mjs +71 -0
  45. package/dist/modules/harnesses/pi.mjs.map +7 -0
  46. package/dist/modules/integrations.d.mts +9 -0
  47. package/dist/modules/integrations.mjs +29 -0
  48. package/dist/modules/integrations.mjs.map +7 -0
  49. package/dist/modules/provider/general.bundle.d.mts +1 -0
  50. package/dist/modules/provider/general.bundle.mjs +5 -0
  51. package/dist/modules/provider/general.bundle.mjs.map +7 -0
  52. package/dist/modules/provider/general.d.mts +11 -0
  53. package/dist/modules/provider/general.mjs +130 -0
  54. package/dist/modules/provider/general.mjs.map +7 -0
  55. package/dist/modules/provider/index.d.mts +3 -0
  56. package/dist/modules/provider/index.mjs +13 -0
  57. package/dist/modules/provider/index.mjs.map +7 -0
  58. package/dist/modules/sandboxes/access.d.mts +34 -0
  59. package/dist/modules/sandboxes/access.mjs +15 -0
  60. package/dist/modules/sandboxes/access.mjs.map +7 -0
  61. package/dist/modules/sandboxes/computer.d.mts +24 -51
  62. package/dist/modules/sandboxes/computer.mjs +523 -888
  63. package/dist/modules/sandboxes/computer.mjs.map +3 -3
  64. package/dist/modules/sandboxes/computers-api.d.mts +3 -2
  65. package/dist/modules/sandboxes/computers-api.mjs +2 -4
  66. package/dist/modules/sandboxes/computers-api.mjs.map +2 -2
  67. package/dist/modules/sandboxes/device-api.d.mts +4 -3
  68. package/dist/modules/sandboxes/device-api.mjs +2 -4
  69. package/dist/modules/sandboxes/device-api.mjs.map +2 -2
  70. package/dist/modules/sandboxes/device.d.mts +6 -6
  71. package/dist/modules/sandboxes/device.mjs +126 -121
  72. package/dist/modules/sandboxes/device.mjs.map +3 -3
  73. package/dist/modules/sandboxes/egress.d.mts +10 -9
  74. package/dist/modules/sandboxes/egress.mjs +73 -56
  75. package/dist/modules/sandboxes/egress.mjs.map +2 -2
  76. package/dist/modules/sandboxes/environment.d.mts +35 -269
  77. package/dist/modules/sandboxes/environment.mjs +27 -104
  78. package/dist/modules/sandboxes/environment.mjs.map +3 -3
  79. package/dist/modules/sandboxes/index.d.mts +9 -69
  80. package/dist/modules/sandboxes/index.mjs +35 -95
  81. package/dist/modules/sandboxes/index.mjs.map +3 -3
  82. package/dist/modules/sandboxes/local.d.mts +29 -0
  83. package/dist/modules/sandboxes/local.mjs +555 -0
  84. package/dist/modules/sandboxes/local.mjs.map +7 -0
  85. package/dist/modules/sandboxes/session.d.mts +0 -3
  86. package/dist/modules/sandboxes/session.mjs +2 -22
  87. package/dist/modules/sandboxes/session.mjs.map +2 -2
  88. package/dist/modules/sandboxes/testing/fake-machine.mjs +0 -28
  89. package/dist/modules/sandboxes/testing/fake-machine.mjs.map +2 -2
  90. package/package.json +73 -137
  91. package/dist/modules/authoring-final-channel.d.mts +0 -3
  92. package/dist/modules/authoring-final-channel.mjs +0 -10
  93. package/dist/modules/authoring-final-channel.mjs.map +0 -7
  94. package/dist/modules/channel-factories.d.mts +0 -40
  95. package/dist/modules/channel-factories.mjs +0 -212
  96. package/dist/modules/channel-factories.mjs.map +0 -7
  97. package/dist/modules/channels/content.d.mts +0 -6
  98. package/dist/modules/channels/content.mjs +0 -31
  99. package/dist/modules/channels/content.mjs.map +0 -7
  100. package/dist/modules/channels/delivery.d.mts +0 -54
  101. package/dist/modules/channels/delivery.mjs +0 -243
  102. package/dist/modules/channels/delivery.mjs.map +0 -7
  103. package/dist/modules/channels/direct-messaging.d.mts +0 -5
  104. package/dist/modules/channels/direct-messaging.mjs +0 -8
  105. package/dist/modules/channels/direct-messaging.mjs.map +0 -7
  106. package/dist/modules/channels/fake-platform.d.mts +0 -36
  107. package/dist/modules/channels/fake-platform.mjs +0 -168
  108. package/dist/modules/channels/fake-platform.mjs.map +0 -7
  109. package/dist/modules/channels/group-messaging.d.mts +0 -5
  110. package/dist/modules/channels/group-messaging.mjs +0 -8
  111. package/dist/modules/channels/group-messaging.mjs.map +0 -7
  112. package/dist/modules/channels/inbound.d.mts +0 -52
  113. package/dist/modules/channels/inbound.mjs +0 -42
  114. package/dist/modules/channels/inbound.mjs.map +0 -7
  115. package/dist/modules/channels/index.d.mts +0 -9
  116. package/dist/modules/channels/index.mjs +0 -10
  117. package/dist/modules/channels/index.mjs.map +0 -7
  118. package/dist/modules/channels/messaging-channel.d.mts +0 -7
  119. package/dist/modules/channels/messaging-channel.mjs +0 -99
  120. package/dist/modules/channels/messaging-channel.mjs.map +0 -7
  121. package/dist/modules/channels/projection.d.mts +0 -36
  122. package/dist/modules/channels/projection.mjs +0 -233
  123. package/dist/modules/channels/projection.mjs.map +0 -7
  124. package/dist/modules/channels/prompt.d.mts +0 -72
  125. package/dist/modules/channels/prompt.mjs +0 -208
  126. package/dist/modules/channels/prompt.mjs.map +0 -7
  127. package/dist/modules/channels/routing.d.mts +0 -5
  128. package/dist/modules/channels/routing.mjs +0 -53
  129. package/dist/modules/channels/routing.mjs.map +0 -7
  130. package/dist/modules/connections.d.mts +0 -11
  131. package/dist/modules/connections.mjs +0 -16
  132. package/dist/modules/connections.mjs.map +0 -7
  133. package/dist/modules/harnesses/acp-describe.d.mts +0 -20
  134. package/dist/modules/harnesses/acp-describe.mjs +0 -110
  135. package/dist/modules/harnesses/acp-describe.mjs.map +0 -7
  136. package/dist/modules/harnesses/local-stdio.d.mts +0 -47
  137. package/dist/modules/harnesses/local-stdio.mjs +0 -41
  138. package/dist/modules/harnesses/local-stdio.mjs.map +0 -7
  139. package/dist/modules/harnesses/remote-acp.d.mts +0 -76
  140. package/dist/modules/harnesses/remote-acp.mjs +0 -83
  141. package/dist/modules/harnesses/remote-acp.mjs.map +0 -7
  142. package/dist/modules/harnesses/remote-claude-code.d.mts +0 -88
  143. package/dist/modules/harnesses/remote-claude-code.mjs +0 -113
  144. package/dist/modules/harnesses/remote-claude-code.mjs.map +0 -7
  145. package/dist/modules/harnesses/remote-client-tls.d.mts +0 -35
  146. package/dist/modules/harnesses/remote-client-tls.mjs +0 -37
  147. package/dist/modules/harnesses/remote-client-tls.mjs.map +0 -7
  148. package/dist/modules/harnesses/remote-codex.d.mts +0 -98
  149. package/dist/modules/harnesses/remote-codex.mjs +0 -128
  150. package/dist/modules/harnesses/remote-codex.mjs.map +0 -7
  151. package/dist/modules/harnesses/remote-pi.d.mts +0 -78
  152. package/dist/modules/harnesses/remote-pi.mjs +0 -104
  153. package/dist/modules/harnesses/remote-pi.mjs.map +0 -7
  154. package/dist/modules/harnesses/remote-url.d.mts +0 -5
  155. package/dist/modules/harnesses/remote-url.mjs +0 -19
  156. package/dist/modules/harnesses/remote-url.mjs.map +0 -7
  157. package/dist/modules/harnesses/serverless-final.d.mts +0 -63
  158. package/dist/modules/harnesses/serverless-final.mjs +0 -88
  159. package/dist/modules/harnesses/serverless-final.mjs.map +0 -7
  160. package/dist/modules/harnesses/serverless-nanocode-claude.d.mts +0 -64
  161. package/dist/modules/harnesses/serverless-nanocode-claude.mjs +0 -105
  162. package/dist/modules/harnesses/serverless-nanocode-claude.mjs.map +0 -7
  163. package/dist/modules/harnesses/serverless-nanocode-codex.d.mts +0 -66
  164. package/dist/modules/harnesses/serverless-nanocode-codex.mjs +0 -113
  165. package/dist/modules/harnesses/serverless-nanocode-codex.mjs.map +0 -7
  166. package/dist/modules/harnesses/serverless-pi.d.mts +0 -64
  167. package/dist/modules/harnesses/serverless-pi.mjs +0 -98
  168. package/dist/modules/harnesses/serverless-pi.mjs.map +0 -7
  169. package/dist/modules/runtime/index.d.mts +0 -7
  170. package/dist/modules/runtime/index.mjs +0 -18
  171. package/dist/modules/runtime/index.mjs.map +0 -7
  172. package/dist/modules/runtime/local-stdio.d.mts +0 -10
  173. package/dist/modules/runtime/local-stdio.mjs +0 -106
  174. package/dist/modules/runtime/local-stdio.mjs.map +0 -7
  175. package/dist/modules/runtime/remote-acp.d.mts +0 -16
  176. package/dist/modules/runtime/remote-acp.mjs +0 -152
  177. package/dist/modules/runtime/remote-acp.mjs.map +0 -7
  178. package/dist/modules/runtime/remote-claude-code.d.mts +0 -5
  179. package/dist/modules/runtime/remote-claude-code.mjs +0 -47
  180. package/dist/modules/runtime/remote-claude-code.mjs.map +0 -7
  181. package/dist/modules/runtime/remote-codex.d.mts +0 -5
  182. package/dist/modules/runtime/remote-codex.mjs +0 -47
  183. package/dist/modules/runtime/remote-codex.mjs.map +0 -7
  184. package/dist/modules/runtime/remote-host.d.mts +0 -127
  185. package/dist/modules/runtime/remote-host.mjs +0 -387
  186. package/dist/modules/runtime/remote-host.mjs.map +0 -7
  187. package/dist/modules/runtime/remote-pi.d.mts +0 -5
  188. package/dist/modules/runtime/remote-pi.mjs +0 -44
  189. package/dist/modules/runtime/remote-pi.mjs.map +0 -7
  190. package/dist/modules/sandboxes/guide.d.mts +0 -35
  191. package/dist/modules/sandboxes/guide.mjs +0 -119
  192. package/dist/modules/sandboxes/guide.mjs.map +0 -7
  193. package/dist/modules/sandboxes/workspace-setup.d.mts +0 -42
  194. package/dist/modules/sandboxes/workspace-setup.mjs +0 -242
  195. package/dist/modules/sandboxes/workspace-setup.mjs.map +0 -7
  196. package/dist/modules/testing/harness.d.mts +0 -9
  197. package/dist/modules/testing/harness.mjs +0 -123
  198. package/dist/modules/testing/harness.mjs.map +0 -7
@@ -1,6 +1,5 @@
1
- import { ProcessInspectionOperation } from "@finalai/agent-protocol/process-inspection";
1
+ import { Capability, FilesAccess, ScreenAccess, ShellAccess } from "@finalai/agent-protocol";
2
2
  import {
3
- SandboxAccess,
4
3
  toolDescriptorSchema
5
4
  } from "@finalai/agent-provider";
6
5
  import {
@@ -15,19 +14,18 @@ import {
15
14
  DeviceApiCapability,
16
15
  DeviceApiLeaseAccess
17
16
  } from "./device-api.mjs";
18
- import { computerOpenOptionsSchema, deviceEnvironmentSchema } from "./environment.mjs";
17
+ import { DEVICE_PROVIDER, deviceEnvironmentSchema } from "./environment.mjs";
19
18
  import { createMcpPipe } from "./mcp-pipe.mjs";
20
19
  import { sandboxPathsFor, SandboxPlatform } from "./paths.mjs";
21
- import { createTransportSession, sessionForAccess } from "./session.mjs";
20
+ import { createTransportSession, readOnlySession } from "./session.mjs";
22
21
  import { DEVICE_API_ACTING_USER_HEADER, DeviceApiClientErrorCode } from "./device-api.mjs";
23
- import { DEVICE_SANDBOX_SPECIFIER } from "./environment.mjs";
22
+ import { DEVICE_PROVIDER as DEVICE_PROVIDER2 } from "./environment.mjs";
24
23
  import { SandboxPlatform as SandboxPlatform2 } from "./paths.mjs";
25
24
  const deviceArtifactSchema = z.object({ version: z.literal(1), deviceId: z.string() });
26
25
  const deviceStateSchema = z.object({
27
- version: z.literal(1),
26
+ version: z.literal(2),
28
27
  deviceId: z.string(),
29
- generation: z.number().int().min(1),
30
- access: z.nativeEnum(SandboxAccess)
28
+ generation: z.number().int().min(0)
31
29
  });
32
30
  const DEVICE_COMPUTER_USE_SERVER = DEVICE_API_COMPUTER_USE_SERVER;
33
31
  const descriptorsOf = (tools) => tools.flatMap((tool) => {
@@ -43,6 +41,19 @@ const ONLINE_POLL_MAX_MS = 5e3;
43
41
  const MAX_LEASE_TTL_MS = 60 * 6e4;
44
42
  const MIN_LEASE_TTL_MS = 6e4;
45
43
  const platformOf = (platform) => platform === "windows" ? SandboxPlatform.Windows : SandboxPlatform.Posix;
44
+ const refuseSetup = (setup) => {
45
+ if (setup == null) return;
46
+ const parts = [
47
+ setup.repositories.length > 0 ? "repositories" : null,
48
+ Object.keys(setup.variables).length > 0 ? "variables" : null,
49
+ setup.steps.length > 0 ? "steps" : null,
50
+ setup.services.length > 0 ? "services" : null,
51
+ Object.keys(setup.commands).length > 0 ? "commands" : null,
52
+ setup.network != null ? "a network policy" : null
53
+ ].filter((part) => part != null);
54
+ if (parts.length > 0)
55
+ throw new Error(`a Final Device takes no sandbox setup (this one has ${parts.join(", ")})`);
56
+ };
46
57
  const createDeviceSandboxProvider = (config) => {
47
58
  const randomUuid = config.randomUuid ?? (() => crypto.randomUUID());
48
59
  const now = config.now ?? (() => Date.now());
@@ -67,43 +78,48 @@ const createDeviceSandboxProvider = (config) => {
67
78
  pollMs = Math.min(pollMs * 2, ONLINE_POLL_MAX_MS);
68
79
  }
69
80
  };
70
- const attach = async (args) => {
71
- const { ctx, access } = args;
72
- const caller = {
73
- organizationId: ctx.session.organizationId,
74
- credentials: ctx.credentials,
75
- actingUser: ctx.actingUser
76
- };
77
- const bindingId = ctx.session.bindingId;
81
+ const credentialsFor = (ctx) => ({
82
+ deviceAccess: async (args) => ({
83
+ baseUrl: config.deviceApiUrl,
84
+ token: (await ctx.identity({ audience: config.deviceApiUrl, refresh: args?.refresh === true })).token
85
+ })
86
+ });
87
+ const callerFor = (ctx) => ({
88
+ organizationId: ctx.organizationId,
89
+ credentials: credentialsFor(ctx),
90
+ actingUser: ctx.actingUser
91
+ });
92
+ const lease = async (args) => {
93
+ const { ctx } = args;
94
+ const caller = callerFor(ctx);
95
+ const bindingId = ctx.sandboxId;
78
96
  const device = await awaitOnline(caller, args.deviceId, onlineWaitMs);
79
- if (access !== SandboxAccess.Read && !device.writable) {
80
- throw new Error(`${device.displayName} does not permit writes`);
81
- }
82
- const leaseAccess = access === SandboxAccess.Read ? DeviceApiLeaseAccess.Read : DeviceApiLeaseAccess.Write;
83
- let current = args.passive === true ? await api.recoverLease({
97
+ const leaseAccess = device.writable ? DeviceApiLeaseAccess.Write : DeviceApiLeaseAccess.Read;
98
+ const afterGeneration = args.afterGeneration === 0 ? void 0 : args.afterGeneration;
99
+ let current = args.passive ? await api.recoverLease({
84
100
  caller,
85
101
  deviceId: device.id,
86
- sessionId: ctx.session.id,
102
+ sessionId: ctx.sandboxId,
87
103
  bindingId,
88
104
  access: leaseAccess
89
105
  }) : await api.lease({
90
106
  caller,
91
107
  deviceId: device.id,
92
- sessionId: ctx.session.id,
108
+ sessionId: ctx.sandboxId,
93
109
  bindingId,
94
110
  access: leaseAccess,
95
111
  ttlMs: leaseTtlMs,
96
- afterGeneration: args.afterGeneration ?? void 0
112
+ afterGeneration
97
113
  });
98
- if (args.passive === true && (current.deviceId !== device.id || current.access !== leaseAccess || current.generation < (args.afterGeneration ?? 0) || current.expiresAtMs <= now()))
114
+ if (args.passive && (current.deviceId !== device.id || current.generation < args.afterGeneration || current.expiresAtMs <= now()))
99
115
  throw new Error("The existing Device lease is unavailable");
100
116
  let renewing = null;
101
- const lease = async () => {
117
+ const leaseHandle = async () => {
102
118
  if (current.expiresAtMs - now() < LEASE_RENEW_MARGIN_MS) {
103
- renewing ??= (args.passive === true ? api.recoverLease({
119
+ renewing ??= (args.passive ? api.recoverLease({
104
120
  caller,
105
121
  deviceId: device.id,
106
- sessionId: ctx.session.id,
122
+ sessionId: ctx.sandboxId,
107
123
  bindingId,
108
124
  access: leaseAccess
109
125
  }) : api.renew({ caller, lease: current.lease, ttlMs: leaseTtlMs })).finally(() => {
@@ -115,7 +131,7 @@ const createDeviceSandboxProvider = (config) => {
115
131
  };
116
132
  let released = false;
117
133
  const release = async () => {
118
- if (released) return;
134
+ if (released || args.passive) return;
119
135
  released = true;
120
136
  await api.release({ caller, lease: current.lease });
121
137
  };
@@ -125,7 +141,7 @@ const createDeviceSandboxProvider = (config) => {
125
141
  id: `${device.id}:${bindingId}`,
126
142
  description: `${device.displayName} (Final Device)`,
127
143
  workspaceRoot: current.workspaceRoot,
128
- transport: api.transport({ caller, lease }),
144
+ transport: api.transport({ caller, lease: leaseHandle }),
129
145
  baseEnv: async () => ({}),
130
146
  randomUuid,
131
147
  paths,
@@ -147,27 +163,15 @@ const createDeviceSandboxProvider = (config) => {
147
163
  const computerUse = offered.has(DeviceApiCapability.ComputerUse);
148
164
  const capabilities = [SandboxCapability.Files];
149
165
  if (processes) capabilities.push(SandboxCapability.Process);
150
- if (processes && access !== SandboxAccess.Read)
151
- capabilities.push(SandboxCapability.DurableProcesses);
166
+ if (processes && !args.passive) capabilities.push(SandboxCapability.DurableProcesses);
152
167
  if (localMcp) capabilities.push(SandboxCapability.LocalMcp);
153
168
  if (computerUse) capabilities.push(SandboxCapability.ComputerUse);
154
169
  if (device.screenReady) capabilities.push(SandboxCapability.Screen);
155
- const channelEndpoint = (server) => ({
156
- name: server,
157
- connect: async () => {
158
- const pipe = openMcpPipe(server);
159
- return {
160
- send: (message, options) => pipe.send(message, options?.abortSignal),
161
- receive: (options) => pipe.receive(options?.abortSignal),
162
- close: () => pipe.close()
163
- };
164
- }
165
- });
166
170
  const openMcpPipe = (server) => {
167
171
  const clientId = randomUuid();
168
172
  const request = async (action, dataBase64, signal) => api.pipe({
169
173
  caller,
170
- lease: await lease(),
174
+ lease: await leaseHandle(),
171
175
  server,
172
176
  action,
173
177
  clientId,
@@ -189,13 +193,24 @@ const createDeviceSandboxProvider = (config) => {
189
193
  }
190
194
  });
191
195
  };
196
+ const channelEndpoint = (server) => ({
197
+ name: server,
198
+ connect: async () => {
199
+ const pipe = openMcpPipe(server);
200
+ return {
201
+ send: (message, options) => pipe.send(message, options?.abortSignal),
202
+ receive: (options) => pipe.receive(options?.abortSignal),
203
+ close: () => pipe.close()
204
+ };
205
+ }
206
+ });
192
207
  const sandbox = { ...session, capabilities };
193
208
  if (!processes) delete sandbox.attachProcess;
194
209
  if (computerUse) {
195
210
  sandbox.computerUse = async () => channelEndpoint(DEVICE_COMPUTER_USE_SERVER);
196
211
  }
197
212
  if (localMcp) {
198
- sandbox.localMcp = async () => (await api.localMcpServers({ caller, lease: await lease() })).map(channelEndpoint);
213
+ sandbox.localMcp = async () => (await api.localMcpServers({ caller, lease: await leaseHandle() })).map(channelEndpoint);
199
214
  }
200
215
  if (device.screenReady) {
201
216
  sandbox.screen = (options) => api.screen({
@@ -209,8 +224,8 @@ const createDeviceSandboxProvider = (config) => {
209
224
  return {
210
225
  generation: current.generation,
211
226
  handle: {
212
- // The Device enforces the read lease too; the view refuses before a round trip.
213
- sandbox: sessionForAccess(sandbox, access),
227
+ // A viewer's lease is the existing one: it changes nothing.
228
+ sandbox: args.passive ? readOnlySession(sandbox) : sandbox,
214
229
  onSandboxStop: release,
215
230
  onSandboxDelete: release,
216
231
  onRuntimeShutdown: async () => void 0
@@ -223,40 +238,33 @@ const createDeviceSandboxProvider = (config) => {
223
238
  const options = parsed.data;
224
239
  const resolveDeviceId = async (caller) => {
225
240
  if ("deviceId" in options) return options.deviceId;
226
- if ("pairing" in options) {
227
- const device = await api.resolvePairing({ caller, code: options.pairing });
228
- if (device == null) throw new Error("the Final Device pairing code is unknown or expired");
229
- return device.id;
230
- }
231
- const devices = await api.listDevices({ caller });
232
- const [only] = devices;
233
- if (devices.length !== 1 || only == null) {
234
- throw new Error(
235
- devices.length === 0 ? "the user this session acts for has no Final Device" : "the user this session acts for has several Final Devices; name one by id or pairing code"
236
- );
237
- }
238
- return only.id;
241
+ const device = await api.resolvePairing({ caller, code: options.pairing });
242
+ if (device == null) throw new Error("the Final Device pairing code is unknown or expired");
243
+ return device.id;
239
244
  };
240
245
  return {
241
- processes: async (ctx, rawState, request) => {
242
- const state = deviceStateSchema.parse(rawState);
243
- if (request.operation === ProcessInspectionOperation.Stop && state.access === SandboxAccess.Read)
244
- throw new Error("stopping processes requires execute access");
245
- return api.inspectProcesses({
246
- caller: ctx,
247
- deviceId: state.deviceId,
248
- bindingId: ctx.bindingId,
249
- request
250
- });
251
- },
252
246
  // What a lease on this Device grants, known from the registry without
253
- // leasing (the Device API's rule: processes, local MCP and computer use with
254
- // a write lease on a Device that allows processes). The computer-use
255
- // driver's readiness is checked when leased.
247
+ // leasing (the Device API's rule: processes and computer use with a write
248
+ // lease on a Device that allows processes).
256
249
  capabilities: async (ctx) => {
257
250
  const device = await api.getDevice({
258
- caller: ctx,
259
- deviceId: await resolveDeviceId(ctx)
251
+ caller: callerFor(ctx),
252
+ deviceId: await resolveDeviceId(callerFor(ctx))
253
+ });
254
+ if (device == null) return {};
255
+ const offer = {
256
+ [Capability.Files]: device.writable ? FilesAccess.Write : FilesAccess.Read
257
+ };
258
+ if (device.writable && device.allowProcesses) {
259
+ offer[Capability.Shell] = ShellAccess.Execute;
260
+ offer[Capability.ComputerUse] = ScreenAccess.Control;
261
+ } else if (device.screenReady) offer[Capability.ComputerUse] = ScreenAccess.View;
262
+ return offer;
263
+ },
264
+ features: async (ctx) => {
265
+ const device = await api.getDevice({
266
+ caller: callerFor(ctx),
267
+ deviceId: await resolveDeviceId(callerFor(ctx))
260
268
  });
261
269
  if (device == null) return null;
262
270
  const offered = [SandboxCapability.Files];
@@ -276,8 +284,8 @@ const createDeviceSandboxProvider = (config) => {
276
284
  // A Device the API has never seen list gives unknown; its first lease does.
277
285
  mcpCatalog: async (ctx) => {
278
286
  const catalog = await api.mcpCatalog({
279
- caller: ctx,
280
- deviceId: await resolveDeviceId(ctx)
287
+ caller: callerFor(ctx),
288
+ deviceId: await resolveDeviceId(callerFor(ctx))
281
289
  });
282
290
  const local = catalog.localServers;
283
291
  const localMcp = {};
@@ -291,78 +299,75 @@ const createDeviceSandboxProvider = (config) => {
291
299
  localMcp: localKnown ? localMcp : null
292
300
  };
293
301
  },
302
+ processes: async (ctx, rawState, request) => {
303
+ const state = deviceStateSchema.parse(rawState);
304
+ return api.inspectProcesses({
305
+ caller: callerFor(ctx),
306
+ deviceId: state.deviceId,
307
+ bindingId: ctx.sandboxId,
308
+ request
309
+ });
310
+ },
311
+ artifactKey: async (ctx) => `device:${await resolveDeviceId(callerFor(ctx))}`,
312
+ prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(callerFor(ctx)) }),
313
+ create: async (_ctx, { artifact, setup }) => {
314
+ refuseSetup(setup);
315
+ return { state: { version: 2, deviceId: artifact.deviceId, generation: 0 } };
316
+ },
294
317
  awaitRunning: async (ctx, rawState, { timeoutMs }) => {
295
318
  const { deviceId } = deviceStateSchema.pick({ deviceId: true }).parse(rawState);
296
- await awaitOnline(
297
- {
298
- organizationId: ctx.session.organizationId,
299
- credentials: ctx.credentials,
300
- actingUser: ctx.actingUser
301
- },
302
- deviceId,
303
- timeoutMs
304
- );
305
- },
306
- artifactKey: async (ctx) => `device:${await resolveDeviceId(ctx)}`,
307
- prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(ctx) }),
308
- start: async (ctx, openOptions, rawArtifact) => {
309
- const artifact = deviceArtifactSchema.parse(rawArtifact);
310
- const access = computerOpenOptionsSchema.parse(openOptions ?? {}).access;
311
- const { handle, generation } = await attach({
312
- ctx,
313
- deviceId: artifact.deviceId,
314
- afterGeneration: null,
315
- access
316
- });
317
- return { handle, state: { version: 1, deviceId: artifact.deviceId, generation, access } };
319
+ await awaitOnline(callerFor(ctx), deviceId, timeoutMs);
318
320
  },
319
321
  // A Device never takes a lease back at an old generation once it lapsed,
320
- // so every attach leases a later one.
321
- resume: async (ctx, _artifact, rawState) => {
322
+ // so every attach leases a later one, and no process from before survives.
323
+ attach: async (ctx, { state: rawState }) => {
322
324
  const state = deviceStateSchema.parse(rawState);
323
- return (await attach({
325
+ const { handle, generation } = await lease({
324
326
  ctx,
325
327
  deviceId: state.deviceId,
326
328
  afterGeneration: state.generation,
327
- access: state.access
328
- })).handle;
329
+ passive: false
330
+ });
331
+ return {
332
+ handle,
333
+ state: { ...state, generation },
334
+ fresh: state.generation === 0,
335
+ booted: true
336
+ };
329
337
  },
330
- resumePassive: async (ctx, _artifact, rawState) => {
338
+ attachPassive: async (ctx, { state: rawState }) => {
331
339
  const state = deviceStateSchema.parse(rawState);
332
- return (await attach({
340
+ return (await lease({
333
341
  ctx,
334
342
  deviceId: state.deviceId,
335
343
  afterGeneration: state.generation,
336
- access: state.access,
337
344
  passive: true
338
345
  })).handle;
339
346
  },
340
- onSessionEnd: async (ctx, _artifact, rawState) => {
347
+ // The lease lapses on its own; the next generation retires its processes.
348
+ detach: async (_ctx, { state }) => deviceStateSchema.parse(state),
349
+ park: async (_ctx, { state }) => deviceStateSchema.parse(state),
350
+ // The Device stays the person's; its workspace for this sandbox is let go.
351
+ delete: async (ctx, { state: rawState }) => {
341
352
  const state = deviceStateSchema.parse(rawState);
342
353
  await api.releaseWorkspaceLeases({
343
- caller: {
344
- organizationId: ctx.session.organizationId,
345
- credentials: ctx.credentials,
346
- actingUser: ctx.actingUser
347
- },
354
+ caller: callerFor(ctx),
348
355
  deviceId: state.deviceId,
349
- bindingId: ctx.session.bindingId
356
+ bindingId: ctx.sandboxId
350
357
  });
351
- },
352
- // The lease lapses on its own; the next generation retires its processes.
353
- park: async (_ctx, _artifact, rawState) => deviceStateSchema.parse(rawState)
358
+ }
354
359
  };
355
360
  };
356
361
  return defineSandboxProvider({
357
- name: "device",
358
- stateProtocolVersion: 1,
362
+ name: DEVICE_PROVIDER,
363
+ stateProtocolVersion: 2,
359
364
  environment
360
365
  });
361
366
  };
362
367
  export {
363
368
  DEVICE_API_ACTING_USER_HEADER,
364
369
  DEVICE_COMPUTER_USE_SERVER,
365
- DEVICE_SANDBOX_SPECIFIER,
370
+ DEVICE_PROVIDER2 as DEVICE_PROVIDER,
366
371
  DeviceApiClientErrorCode,
367
372
  SandboxPlatform2 as SandboxPlatform,
368
373
  createDeviceSandboxProvider
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/sandboxes/device.ts"],
4
- "sourcesContent": ["import { ProcessInspectionOperation } from '@finalai/agent-protocol/process-inspection';\nimport {\n SandboxAccess,\n toolDescriptorSchema,\n type JsonObject,\n type JsonValue,\n type ToolDescriptorIface,\n} from '@finalai/agent-provider';\nimport {\n defineSandboxProvider,\n SandboxCapability,\n type McpChannelEndpointIface,\n type NetworkSandboxSessionIface,\n type SandboxProviderHandleIface,\n type SandboxProviderIface,\n type SandboxProviderImplementationIface,\n type SandboxStartContextIface,\n} from '@finalai/agent-provider/sandbox';\nimport * as z from 'zod';\nimport { commandEncoderFor } from \"./commands.mjs\";\nimport {\n createDeviceApiClient,\n DEVICE_API_COMPUTER_USE_SERVER,\n DeviceApiCapability,\n DeviceApiLeaseAccess,\n type DeviceApiCallerIface,\n type DeviceApiConfigIface,\n type DeviceApiDeviceIface,\n type DeviceApiFetchIface,\n type DeviceApiLeaseIface,\n} from \"./device-api.mjs\";\nimport { computerOpenOptionsSchema, deviceEnvironmentSchema } from \"./environment.mjs\";\nimport { createMcpPipe } from \"./mcp-pipe.mjs\";\nimport { sandboxPathsFor, SandboxPlatform } from \"./paths.mjs\";\nimport { createTransportSession, sessionForAccess } from \"./session.mjs\";\n\nexport { DEVICE_API_ACTING_USER_HEADER, DeviceApiClientErrorCode } from \"./device-api.mjs\";\nexport type { DeviceApiConfigIface, DeviceApiFetchIface } from \"./device-api.mjs\";\nexport { DEVICE_SANDBOX_SPECIFIER } from \"./environment.mjs\";\nexport type { FinalPlatformAccessIface } from '@finalai/agent-provider/sandbox';\nexport { SandboxPlatform } from \"./paths.mjs\";\n\n// The `device` sandbox: a user's own computer running Final Device. The\n// provider is a client of the Device API, the Device's backend, exactly as a\n// vendor provider is a client of its vendor's API: it\n// leases a workspace there and drives files, processes, computer use and the\n// screen through that API with host-provided credentials. It knows nothing of how\n// the Device API reaches the Device (DEVICE_DIRECT_ENDPOINT.md). A Device is one\n// person's machine: every call names the user the session acts for\n// (ctx.actingUser, read per call since it changes between turns), and the Device\n// API refuses a Device that user does not own.\n\nexport interface DeviceSandboxProviderConfigIface {\n // Transport options; the Device API origin and token come from each context's\n // `credentials.deviceAccess`.\n api: DeviceApiConfigIface;\n fetch?: DeviceApiFetchIface;\n // Device leases last at most an hour; renewed while in use.\n leaseTtlMs?: number;\n // How long start and resume wait for an offline Device to connect (default 0:\n // an offline Device fails at once); polled from 500 ms, doubling to 5 s.\n onlineWaitMs?: number;\n sleep?: (ms: number) => Promise<void>;\n log?: (message: string, fields?: Record<string, string | number | boolean | null>) => void;\n randomUuid?: () => string;\n now?: () => number;\n}\n\nconst deviceArtifactSchema = z.object({ version: z.literal(1), deviceId: z.string() });\nconst deviceStateSchema = z.object({\n version: z.literal(1),\n deviceId: z.string(),\n generation: z.number().int().min(1),\n access: z.nativeEnum(SandboxAccess),\n});\nexport type DeviceSandboxArtifact = z.infer<typeof deviceArtifactSchema>;\nexport type DeviceSandboxState = z.infer<typeof deviceStateSchema>;\n\nexport const DEVICE_COMPUTER_USE_SERVER = DEVICE_API_COMPUTER_USE_SERVER;\n\nexport interface DeviceSandboxSessionIface extends NetworkSandboxSessionIface {\n readonly capabilities: readonly SandboxCapability[];\n}\n\nexport type DeviceSandboxProviderIface = SandboxProviderIface<\n JsonValue,\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n>;\n\n// The Device API's recorded tools as tool descriptors; one that does not fit\n// the contract is left out.\nconst descriptorsOf = (tools: readonly JsonObject[]): ToolDescriptorIface[] =>\n tools.flatMap((tool) => {\n const parsed = toolDescriptorSchema.safeParse({\n ...tool,\n description: tool.description ?? tool.title ?? tool.name,\n });\n return parsed.success ? [parsed.data] : [];\n });\n\nconst LEASE_RENEW_MARGIN_MS = 5 * 60_000;\nconst ONLINE_POLL_MS = 500;\nconst ONLINE_POLL_MAX_MS = 5_000;\nconst MAX_LEASE_TTL_MS = 60 * 60_000;\nconst MIN_LEASE_TTL_MS = 60_000;\n\nconst platformOf = (platform: string): SandboxPlatform =>\n platform === 'windows' ? SandboxPlatform.Windows : SandboxPlatform.Posix;\n\nexport const createDeviceSandboxProvider = (\n config: DeviceSandboxProviderConfigIface,\n): DeviceSandboxProviderIface => {\n const randomUuid = config.randomUuid ?? (() => crypto.randomUUID());\n const now = config.now ?? (() => Date.now());\n const log = config.log ?? (() => undefined);\n const leaseTtlMs = Math.max(\n MIN_LEASE_TTL_MS,\n Math.min(config.leaseTtlMs ?? 15 * 60_000, MAX_LEASE_TTL_MS),\n );\n const sleep =\n config.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n const onlineWaitMs = config.onlineWaitMs ?? 0;\n const api = createDeviceApiClient({ api: config.api, fetch: config.fetch });\n\n // A Device runs when it is connected to the Device API; nothing is leased before.\n const awaitOnline = async (\n caller: DeviceApiCallerIface,\n deviceId: string,\n timeoutMs: number,\n ): Promise<DeviceApiDeviceIface> => {\n const deadline = now() + timeoutMs;\n let pollMs = ONLINE_POLL_MS;\n for (;;) {\n const device = await api.getDevice({ caller, deviceId });\n if (device == null) throw new Error(`device ${deviceId} was not found`);\n if (device.online) return device;\n const left = deadline - now();\n if (left <= 0) throw new Error(`${device.displayName} is not connected`);\n await sleep(Math.min(pollMs, left));\n pollMs = Math.min(pollMs * 2, ONLINE_POLL_MAX_MS);\n }\n };\n\n const attach = async (args: {\n ctx: SandboxStartContextIface;\n deviceId: string;\n afterGeneration: number | null;\n passive?: boolean;\n access: SandboxAccess;\n }): Promise<{\n handle: SandboxProviderHandleIface<DeviceSandboxSessionIface>;\n generation: number;\n }> => {\n const { ctx, access } = args;\n const caller: DeviceApiCallerIface = {\n organizationId: ctx.session.organizationId,\n credentials: ctx.credentials,\n actingUser: ctx.actingUser,\n };\n const bindingId = ctx.session.bindingId;\n // The provider's awaitRunning, before the lease.\n const device = await awaitOnline(caller, args.deviceId, onlineWaitMs);\n // Processes need the Device's write lease, so execute access does too.\n if (access !== SandboxAccess.Read && !device.writable) {\n throw new Error(`${device.displayName} does not permit writes`);\n }\n const leaseAccess =\n access === SandboxAccess.Read ? DeviceApiLeaseAccess.Read : DeviceApiLeaseAccess.Write;\n let current: DeviceApiLeaseIface =\n args.passive === true\n ? await api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n })\n : await api.lease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n ttlMs: leaseTtlMs,\n afterGeneration: args.afterGeneration ?? undefined,\n });\n if (\n args.passive === true &&\n (current.deviceId !== device.id ||\n current.access !== leaseAccess ||\n current.generation < (args.afterGeneration ?? 0) ||\n current.expiresAtMs <= now())\n )\n throw new Error('The existing Device lease is unavailable');\n let renewing: Promise<DeviceApiLeaseIface> | null = null;\n // The current lease handle, renewed shortly before it lapses.\n const lease = async (): Promise<string> => {\n if (current.expiresAtMs - now() < LEASE_RENEW_MARGIN_MS) {\n renewing ??= (\n args.passive === true\n ? api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n })\n : api.renew({ caller, lease: current.lease, ttlMs: leaseTtlMs })\n ).finally(() => {\n renewing = null;\n });\n current = await renewing;\n }\n return current.lease;\n };\n let released = false;\n const release = async (): Promise<void> => {\n if (released) return;\n released = true;\n await api.release({ caller, lease: current.lease });\n };\n\n const platform = platformOf(current.platform);\n const paths = sandboxPathsFor(platform);\n const session = createTransportSession({\n id: `${device.id}:${bindingId}`,\n description: `${device.displayName} (Final Device)`,\n workspaceRoot: current.workspaceRoot,\n transport: api.transport({ caller, lease }),\n baseEnv: async () => ({}),\n randomUuid,\n paths,\n commands: commandEncoderFor(platform),\n // Lease processes end with the lease generation; a new one knows none.\n processScope: () => String(current.generation),\n network: {\n ports: [],\n getPortEndpoint: async () => {\n throw new Error('Final Device sandboxes expose no ports');\n },\n stop: release,\n destroy: release,\n },\n });\n\n const offered = new Set(current.capabilities);\n const processes = offered.has(DeviceApiCapability.Process);\n const localMcp = offered.has(DeviceApiCapability.LocalMcp);\n const computerUse = offered.has(DeviceApiCapability.ComputerUse);\n const capabilities = [SandboxCapability.Files];\n if (processes) capabilities.push(SandboxCapability.Process);\n if (processes && access !== SandboxAccess.Read)\n capabilities.push(SandboxCapability.DurableProcesses);\n if (localMcp) capabilities.push(SandboxCapability.LocalMcp);\n if (computerUse) capabilities.push(SandboxCapability.ComputerUse);\n if (device.screenReady) capabilities.push(SandboxCapability.Screen);\n\n // The Device's computer-use server, or one of its local MCP servers, is a byte\n // pipe in the Device API (`/v1/leases/:lease/mcp/:server/{open,write,read,close}`):\n // a channel endpoint whoever runs this handle opens in process.\n const channelEndpoint = (server: string): McpChannelEndpointIface => ({\n name: server,\n connect: async () => {\n const pipe = openMcpPipe(server);\n return {\n send: (message, options) => pipe.send(message, options?.abortSignal),\n receive: (options) => pipe.receive(options?.abortSignal),\n close: () => pipe.close(),\n };\n },\n });\n const openMcpPipe = (server: string) => {\n const clientId = randomUuid();\n const request = async (\n action: 'open' | 'write' | 'read' | 'close',\n dataBase64?: string,\n signal?: AbortSignal,\n ): Promise<string> =>\n api.pipe({\n caller,\n lease: await lease(),\n server,\n action,\n clientId,\n dataBase64,\n signal,\n });\n return createMcpPipe({\n wire: {\n open: async (signal) => {\n await request('open', undefined, signal);\n },\n write: async (data, signal) => {\n await request('write', data, signal);\n },\n read: (signal) => request('read', undefined, signal),\n close: async () => {\n await request('close').catch(() => undefined);\n },\n },\n });\n };\n const sandbox: DeviceSandboxSessionIface = { ...session, capabilities };\n // Without process access there is nothing to attach to.\n if (!processes) delete sandbox.attachProcess;\n if (computerUse) {\n sandbox.computerUse = async () => channelEndpoint(DEVICE_COMPUTER_USE_SERVER);\n }\n if (localMcp) {\n sandbox.localMcp = async () =>\n (await api.localMcpServers({ caller, lease: await lease() })).map(channelEndpoint);\n }\n if (device.screenReady) {\n sandbox.screen = (options) =>\n api.screen({\n caller,\n deviceId: device.id,\n sessionId: randomUuid(),\n viewOnly: options?.viewOnly ?? true,\n });\n }\n log('device_sandbox.attached', { deviceId: device.id, generation: current.generation });\n return {\n generation: current.generation,\n handle: {\n // The Device enforces the read lease too; the view refuses before a round trip.\n sandbox: sessionForAccess(sandbox, access),\n onSandboxStop: release,\n onSandboxDelete: release,\n onRuntimeShutdown: async () => undefined,\n },\n };\n };\n\n const environment = (\n rawOptions?: JsonValue,\n ): SandboxProviderImplementationIface<\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n > => {\n const parsed = deviceEnvironmentSchema.safeParse(rawOptions ?? {});\n if (!parsed.success) throw new Error(`invalid device environment: ${parsed.error.message}`);\n const options = parsed.data;\n // The Device the environment names; the Device API checks on every call\n // that the acting user owns it.\n const resolveDeviceId = async (caller: DeviceApiCallerIface): Promise<string> => {\n if ('deviceId' in options) return options.deviceId;\n if ('pairing' in options) {\n const device = await api.resolvePairing({ caller, code: options.pairing });\n if (device == null) throw new Error('the Final Device pairing code is unknown or expired');\n return device.id;\n }\n // An unnamed binding uses the only Device of the user the session acts\n // for (the API lists only that user's Devices), never another member's.\n const devices = await api.listDevices({ caller });\n const [only] = devices;\n if (devices.length !== 1 || only == null) {\n throw new Error(\n devices.length === 0\n ? 'the user this session acts for has no Final Device'\n : 'the user this session acts for has several Final Devices; name one by id or pairing code',\n );\n }\n return only.id;\n };\n return {\n processes: async (ctx, rawState, request) => {\n const state = deviceStateSchema.parse(rawState);\n if (\n request.operation === ProcessInspectionOperation.Stop &&\n state.access === SandboxAccess.Read\n )\n throw new Error('stopping processes requires execute access');\n return api.inspectProcesses({\n caller: ctx,\n deviceId: state.deviceId,\n bindingId: ctx.bindingId,\n request,\n });\n },\n // What a lease on this Device grants, known from the registry without\n // leasing (the Device API's rule: processes, local MCP and computer use with\n // a write lease on a Device that allows processes). The computer-use\n // driver's readiness is checked when leased.\n capabilities: async (ctx) => {\n const device = await api.getDevice({\n caller: ctx,\n deviceId: await resolveDeviceId(ctx),\n });\n if (device == null) return null;\n const offered = [SandboxCapability.Files];\n if (device.writable && device.allowProcesses) {\n offered.push(\n SandboxCapability.Process,\n SandboxCapability.DurableProcesses,\n SandboxCapability.LocalMcp,\n SandboxCapability.ComputerUse,\n );\n }\n if (device.screenReady) offered.push(SandboxCapability.Screen);\n return offered;\n },\n // What the Device's servers last listed through the Device API (it records\n // them from the MCP pipes), so the projection lists them without leasing.\n // A Device the API has never seen list gives unknown; its first lease does.\n mcpCatalog: async (ctx) => {\n const catalog = await api.mcpCatalog({\n caller: ctx,\n deviceId: await resolveDeviceId(ctx),\n });\n const local = catalog.localServers;\n const localMcp: Record<string, ToolDescriptorIface[]> = {};\n let localKnown = local != null;\n for (const server of local?.servers ?? []) {\n if (server.tools == null) localKnown = false;\n else localMcp[server.name] = descriptorsOf(server.tools);\n }\n return {\n computerUse:\n catalog.computerUse == null ? null : descriptorsOf(catalog.computerUse.tools),\n localMcp: localKnown ? localMcp : null,\n };\n },\n awaitRunning: async (ctx, rawState, { timeoutMs }) => {\n const { deviceId } = deviceStateSchema.pick({ deviceId: true }).parse(rawState);\n await awaitOnline(\n {\n organizationId: ctx.session.organizationId,\n credentials: ctx.credentials,\n actingUser: ctx.actingUser,\n },\n deviceId,\n timeoutMs,\n );\n },\n artifactKey: async (ctx) => `device:${await resolveDeviceId(ctx)}`,\n prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(ctx) }),\n start: async (ctx, openOptions, rawArtifact) => {\n const artifact = deviceArtifactSchema.parse(rawArtifact);\n const access = computerOpenOptionsSchema.parse(openOptions ?? {}).access;\n const { handle, generation } = await attach({\n ctx,\n deviceId: artifact.deviceId,\n afterGeneration: null,\n access,\n });\n return { handle, state: { version: 1, deviceId: artifact.deviceId, generation, access } };\n },\n // A Device never takes a lease back at an old generation once it lapsed,\n // so every attach leases a later one.\n resume: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await attach({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n access: state.access,\n })\n ).handle;\n },\n resumePassive: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await attach({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n access: state.access,\n passive: true,\n })\n ).handle;\n },\n onSessionEnd: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n await api.releaseWorkspaceLeases({\n caller: {\n organizationId: ctx.session.organizationId,\n credentials: ctx.credentials,\n actingUser: ctx.actingUser,\n },\n deviceId: state.deviceId,\n bindingId: ctx.session.bindingId,\n });\n },\n // The lease lapses on its own; the next generation retires its processes.\n park: async (_ctx, _artifact, rawState) => deviceStateSchema.parse(rawState),\n };\n };\n\n return defineSandboxProvider({\n name: 'device',\n stateProtocolVersion: 1,\n environment,\n });\n};\n"],
5
- "mappings": "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",
6
- "names": ["SandboxPlatform"]
4
+ "sourcesContent": ["import { Capability, FilesAccess, ScreenAccess, ShellAccess } from '@finalai/agent-protocol';\nimport {\n toolDescriptorSchema,\n type JsonObject,\n type JsonValue,\n type ToolDescriptorIface,\n} from '@finalai/agent-provider';\nimport {\n defineSandboxProvider,\n SandboxCapability,\n type McpChannelEndpointIface,\n type NetworkSandboxSessionIface,\n type SandboxCapabilityOffer,\n type SandboxEnvironmentImplementationIface,\n type SandboxProviderContextIface,\n type SandboxProviderHandleIface,\n type SandboxProviderIface,\n type SandboxResourceContextIface,\n type SandboxSetupIface,\n} from '@finalai/agent-provider/sandbox';\nimport * as z from 'zod';\nimport type { DeviceApiCredentialsIface } from \"./access.mjs\";\nimport { commandEncoderFor } from \"./commands.mjs\";\nimport {\n createDeviceApiClient,\n DEVICE_API_COMPUTER_USE_SERVER,\n DeviceApiCapability,\n DeviceApiLeaseAccess,\n type DeviceApiCallerIface,\n type DeviceApiConfigIface,\n type DeviceApiDeviceIface,\n type DeviceApiFetchIface,\n type DeviceApiLeaseIface,\n} from \"./device-api.mjs\";\nimport { DEVICE_PROVIDER, deviceEnvironmentSchema } from \"./environment.mjs\";\nimport { createMcpPipe } from \"./mcp-pipe.mjs\";\nimport { sandboxPathsFor, SandboxPlatform } from \"./paths.mjs\";\nimport { createTransportSession, readOnlySession } from \"./session.mjs\";\n\nexport { DEVICE_API_ACTING_USER_HEADER, DeviceApiClientErrorCode } from \"./device-api.mjs\";\nexport type { DeviceApiConfigIface, DeviceApiFetchIface } from \"./device-api.mjs\";\nexport { DEVICE_PROVIDER } from \"./environment.mjs\";\nexport type { FinalPlatformAccessIface } from \"./access.mjs\";\nexport { SandboxPlatform } from \"./paths.mjs\";\n\n// The `device` sandbox: a person's own computer running Final Device. The\n// provider is a client of the Device API, the Device's backend, exactly as a\n// vendor provider is a client of its vendor's API: it leases a workspace there\n// and drives files, processes, computer use and the screen through that API.\n// It knows nothing of how the Device API reaches the Device\n// (DEVICE_DIRECT_ENDPOINT.md). A Device is one person's machine: every call\n// names the user the sandbox acts for (ctx.actingUser, read per call since it\n// changes between turns), and the Device API refuses a Device that user does\n// not own. A Device takes no sandbox setup: it is the person's machine as it is.\n\nexport interface DeviceSandboxProviderConfigIface {\n // Transport options.\n api: DeviceApiConfigIface;\n // The Device API origin. Requests authenticate with the provider's platform\n // identity for it (ctx.identity), which names the acting user and which the\n // registration must be granted.\n deviceApiUrl: string;\n fetch?: DeviceApiFetchIface;\n // Device leases last at most an hour; renewed while in use.\n leaseTtlMs?: number;\n // How long attach waits for an offline Device to connect (default 0:\n // an offline Device fails at once); polled from 500 ms, doubling to 5 s.\n onlineWaitMs?: number;\n sleep?: (ms: number) => Promise<void>;\n log?: (message: string, fields?: Record<string, string | number | boolean | null>) => void;\n randomUuid?: () => string;\n now?: () => number;\n}\n\nconst deviceArtifactSchema = z.object({ version: z.literal(1), deviceId: z.string() });\n// `generation`: the last lease generation taken; 0 before the first attach.\nconst deviceStateSchema = z.object({\n version: z.literal(2),\n deviceId: z.string(),\n generation: z.number().int().min(0),\n});\nexport type DeviceSandboxArtifact = z.infer<typeof deviceArtifactSchema>;\nexport type DeviceSandboxState = z.infer<typeof deviceStateSchema>;\n\nexport const DEVICE_COMPUTER_USE_SERVER = DEVICE_API_COMPUTER_USE_SERVER;\n\nexport interface DeviceSandboxSessionIface extends NetworkSandboxSessionIface {\n readonly capabilities: readonly SandboxCapability[];\n}\n\nexport type DeviceSandboxProviderIface = SandboxProviderIface<\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n>;\n\n// The Device API's recorded tools as tool descriptors; one that does not fit\n// the contract is left out.\nconst descriptorsOf = (tools: readonly JsonObject[]): ToolDescriptorIface[] =>\n tools.flatMap((tool) => {\n const parsed = toolDescriptorSchema.safeParse({\n ...tool,\n description: tool.description ?? tool.title ?? tool.name,\n });\n return parsed.success ? [parsed.data] : [];\n });\n\nconst LEASE_RENEW_MARGIN_MS = 5 * 60_000;\nconst ONLINE_POLL_MS = 500;\nconst ONLINE_POLL_MAX_MS = 5_000;\nconst MAX_LEASE_TTL_MS = 60 * 60_000;\nconst MIN_LEASE_TTL_MS = 60_000;\n\nconst platformOf = (platform: string): SandboxPlatform =>\n platform === 'windows' ? SandboxPlatform.Windows : SandboxPlatform.Posix;\n\n// A Device is the person's machine as it is: nothing is cloned, installed or\n// started on it, and its network is the person's. Notes may still describe it.\nconst refuseSetup = (setup: SandboxSetupIface | null): void => {\n if (setup == null) return;\n const parts = [\n setup.repositories.length > 0 ? 'repositories' : null,\n Object.keys(setup.variables).length > 0 ? 'variables' : null,\n setup.steps.length > 0 ? 'steps' : null,\n setup.services.length > 0 ? 'services' : null,\n Object.keys(setup.commands).length > 0 ? 'commands' : null,\n setup.network != null ? 'a network policy' : null,\n ].filter((part) => part != null);\n if (parts.length > 0)\n throw new Error(`a Final Device takes no sandbox setup (this one has ${parts.join(', ')})`);\n};\n\nexport const createDeviceSandboxProvider = (\n config: DeviceSandboxProviderConfigIface,\n): DeviceSandboxProviderIface => {\n const randomUuid = config.randomUuid ?? (() => crypto.randomUUID());\n const now = config.now ?? (() => Date.now());\n const log = config.log ?? (() => undefined);\n const leaseTtlMs = Math.max(\n MIN_LEASE_TTL_MS,\n Math.min(config.leaseTtlMs ?? 15 * 60_000, MAX_LEASE_TTL_MS),\n );\n const sleep =\n config.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n const onlineWaitMs = config.onlineWaitMs ?? 0;\n const api = createDeviceApiClient({ api: config.api, fetch: config.fetch });\n\n // A Device runs when it is connected to the Device API; nothing is leased before.\n const awaitOnline = async (\n caller: DeviceApiCallerIface,\n deviceId: string,\n timeoutMs: number,\n ): Promise<DeviceApiDeviceIface> => {\n const deadline = now() + timeoutMs;\n let pollMs = ONLINE_POLL_MS;\n for (;;) {\n const device = await api.getDevice({ caller, deviceId });\n if (device == null) throw new Error(`device ${deviceId} was not found`);\n if (device.online) return device;\n const left = deadline - now();\n if (left <= 0) throw new Error(`${device.displayName} is not connected`);\n await sleep(Math.min(pollMs, left));\n pollMs = Math.min(pollMs * 2, ONLINE_POLL_MAX_MS);\n }\n };\n\n // An assertion for the Device API before each request, and a new one after\n // a rejection; it carries the acting user it was issued for.\n const credentialsFor = (ctx: SandboxProviderContextIface): DeviceApiCredentialsIface => ({\n deviceAccess: async (args) => ({\n baseUrl: config.deviceApiUrl,\n token: (\n await ctx.identity({ audience: config.deviceApiUrl, refresh: args?.refresh === true })\n ).token,\n }),\n });\n\n const callerFor = (ctx: SandboxProviderContextIface): DeviceApiCallerIface => ({\n organizationId: ctx.organizationId,\n credentials: credentialsFor(ctx),\n actingUser: ctx.actingUser,\n });\n\n const lease = async (args: {\n ctx: SandboxResourceContextIface;\n deviceId: string;\n afterGeneration: number;\n passive: boolean;\n }): Promise<{\n handle: SandboxProviderHandleIface<DeviceSandboxSessionIface>;\n generation: number;\n }> => {\n const { ctx } = args;\n const caller = callerFor(ctx);\n // A Device workspace is keyed by the sandbox: every attach of this sandbox\n // finds the same files, and its processes end with each lease generation.\n const bindingId = ctx.sandboxId;\n // The provider's awaitRunning, before the lease.\n const device = await awaitOnline(caller, args.deviceId, onlineWaitMs);\n // The host gates each call by its grant; the lease is the most the Device allows.\n const leaseAccess = device.writable ? DeviceApiLeaseAccess.Write : DeviceApiLeaseAccess.Read;\n const afterGeneration = args.afterGeneration === 0 ? undefined : args.afterGeneration;\n let current: DeviceApiLeaseIface = args.passive\n ? await api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.sandboxId,\n bindingId,\n access: leaseAccess,\n })\n : await api.lease({\n caller,\n deviceId: device.id,\n sessionId: ctx.sandboxId,\n bindingId,\n access: leaseAccess,\n ttlMs: leaseTtlMs,\n afterGeneration,\n });\n if (\n args.passive &&\n (current.deviceId !== device.id ||\n current.generation < args.afterGeneration ||\n current.expiresAtMs <= now())\n )\n throw new Error('The existing Device lease is unavailable');\n let renewing: Promise<DeviceApiLeaseIface> | null = null;\n // The current lease handle, renewed shortly before it lapses.\n const leaseHandle = async (): Promise<string> => {\n if (current.expiresAtMs - now() < LEASE_RENEW_MARGIN_MS) {\n renewing ??= (\n args.passive\n ? api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.sandboxId,\n bindingId,\n access: leaseAccess,\n })\n : api.renew({ caller, lease: current.lease, ttlMs: leaseTtlMs })\n ).finally(() => {\n renewing = null;\n });\n current = await renewing;\n }\n return current.lease;\n };\n let released = false;\n const release = async (): Promise<void> => {\n if (released || args.passive) return;\n released = true;\n await api.release({ caller, lease: current.lease });\n };\n\n const platform = platformOf(current.platform);\n const paths = sandboxPathsFor(platform);\n const session = createTransportSession({\n id: `${device.id}:${bindingId}`,\n description: `${device.displayName} (Final Device)`,\n workspaceRoot: current.workspaceRoot,\n transport: api.transport({ caller, lease: leaseHandle }),\n baseEnv: async () => ({}),\n randomUuid,\n paths,\n commands: commandEncoderFor(platform),\n // Lease processes end with the lease generation; a new one knows none.\n processScope: () => String(current.generation),\n network: {\n ports: [],\n getPortEndpoint: async () => {\n throw new Error('Final Device sandboxes expose no ports');\n },\n stop: release,\n destroy: release,\n },\n });\n\n const offered = new Set(current.capabilities);\n const processes = offered.has(DeviceApiCapability.Process);\n const localMcp = offered.has(DeviceApiCapability.LocalMcp);\n const computerUse = offered.has(DeviceApiCapability.ComputerUse);\n const capabilities = [SandboxCapability.Files];\n if (processes) capabilities.push(SandboxCapability.Process);\n if (processes && !args.passive) capabilities.push(SandboxCapability.DurableProcesses);\n if (localMcp) capabilities.push(SandboxCapability.LocalMcp);\n if (computerUse) capabilities.push(SandboxCapability.ComputerUse);\n if (device.screenReady) capabilities.push(SandboxCapability.Screen);\n\n // The Device's computer-use server, or one of its local MCP servers, is a byte\n // pipe in the Device API (`/v1/leases/:lease/mcp/:server/{open,write,read,close}`):\n // a channel endpoint whoever runs this handle opens in process.\n const openMcpPipe = (server: string) => {\n const clientId = randomUuid();\n const request = async (\n action: 'open' | 'write' | 'read' | 'close',\n dataBase64?: string,\n signal?: AbortSignal,\n ): Promise<string> =>\n api.pipe({\n caller,\n lease: await leaseHandle(),\n server,\n action,\n clientId,\n dataBase64,\n signal,\n });\n return createMcpPipe({\n wire: {\n open: async (signal) => {\n await request('open', undefined, signal);\n },\n write: async (data, signal) => {\n await request('write', data, signal);\n },\n read: (signal) => request('read', undefined, signal),\n close: async () => {\n await request('close').catch(() => undefined);\n },\n },\n });\n };\n const channelEndpoint = (server: string): McpChannelEndpointIface => ({\n name: server,\n connect: async () => {\n const pipe = openMcpPipe(server);\n return {\n send: (message, options) => pipe.send(message, options?.abortSignal),\n receive: (options) => pipe.receive(options?.abortSignal),\n close: () => pipe.close(),\n };\n },\n });\n const sandbox: DeviceSandboxSessionIface = { ...session, capabilities };\n // Without process access there is nothing to attach to.\n if (!processes) delete sandbox.attachProcess;\n if (computerUse) {\n sandbox.computerUse = async () => channelEndpoint(DEVICE_COMPUTER_USE_SERVER);\n }\n if (localMcp) {\n sandbox.localMcp = async () =>\n (await api.localMcpServers({ caller, lease: await leaseHandle() })).map(channelEndpoint);\n }\n if (device.screenReady) {\n sandbox.screen = (options) =>\n api.screen({\n caller,\n deviceId: device.id,\n sessionId: randomUuid(),\n viewOnly: options?.viewOnly ?? true,\n });\n }\n log('device_sandbox.attached', { deviceId: device.id, generation: current.generation });\n return {\n generation: current.generation,\n handle: {\n // A viewer's lease is the existing one: it changes nothing.\n sandbox: args.passive ? readOnlySession(sandbox) : sandbox,\n onSandboxStop: release,\n onSandboxDelete: release,\n onRuntimeShutdown: async () => undefined,\n },\n };\n };\n\n const environment = (\n rawOptions?: JsonValue,\n ): SandboxEnvironmentImplementationIface<\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n > => {\n const parsed = deviceEnvironmentSchema.safeParse(rawOptions ?? {});\n if (!parsed.success) throw new Error(`invalid device environment: ${parsed.error.message}`);\n const options = parsed.data;\n // The Device the environment names; the Device API checks on every call\n // that the acting user owns it.\n const resolveDeviceId = async (caller: DeviceApiCallerIface): Promise<string> => {\n if ('deviceId' in options) return options.deviceId;\n const device = await api.resolvePairing({ caller, code: options.pairing });\n if (device == null) throw new Error('the Final Device pairing code is unknown or expired');\n return device.id;\n };\n return {\n // What a lease on this Device grants, known from the registry without\n // leasing (the Device API's rule: processes and computer use with a write\n // lease on a Device that allows processes).\n capabilities: async (ctx): Promise<SandboxCapabilityOffer> => {\n const device = await api.getDevice({\n caller: callerFor(ctx),\n deviceId: await resolveDeviceId(callerFor(ctx)),\n });\n if (device == null) return {};\n const offer: SandboxCapabilityOffer = {\n [Capability.Files]: device.writable ? FilesAccess.Write : FilesAccess.Read,\n };\n if (device.writable && device.allowProcesses) {\n offer[Capability.Shell] = ShellAccess.Execute;\n offer[Capability.ComputerUse] = ScreenAccess.Control;\n } else if (device.screenReady) offer[Capability.ComputerUse] = ScreenAccess.View;\n return offer;\n },\n features: async (ctx) => {\n const device = await api.getDevice({\n caller: callerFor(ctx),\n deviceId: await resolveDeviceId(callerFor(ctx)),\n });\n if (device == null) return null;\n const offered = [SandboxCapability.Files];\n if (device.writable && device.allowProcesses) {\n offered.push(\n SandboxCapability.Process,\n SandboxCapability.DurableProcesses,\n SandboxCapability.LocalMcp,\n SandboxCapability.ComputerUse,\n );\n }\n if (device.screenReady) offered.push(SandboxCapability.Screen);\n return offered;\n },\n // What the Device's servers last listed through the Device API (it records\n // them from the MCP pipes), so the projection lists them without leasing.\n // A Device the API has never seen list gives unknown; its first lease does.\n mcpCatalog: async (ctx) => {\n const catalog = await api.mcpCatalog({\n caller: callerFor(ctx),\n deviceId: await resolveDeviceId(callerFor(ctx)),\n });\n const local = catalog.localServers;\n const localMcp: Record<string, ToolDescriptorIface[]> = {};\n let localKnown = local != null;\n for (const server of local?.servers ?? []) {\n if (server.tools == null) localKnown = false;\n else localMcp[server.name] = descriptorsOf(server.tools);\n }\n return {\n computerUse:\n catalog.computerUse == null ? null : descriptorsOf(catalog.computerUse.tools),\n localMcp: localKnown ? localMcp : null,\n };\n },\n processes: async (ctx, rawState, request) => {\n const state = deviceStateSchema.parse(rawState);\n return api.inspectProcesses({\n caller: callerFor(ctx),\n deviceId: state.deviceId,\n bindingId: ctx.sandboxId,\n request,\n });\n },\n artifactKey: async (ctx) => `device:${await resolveDeviceId(callerFor(ctx))}`,\n prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(callerFor(ctx)) }),\n create: async (_ctx, { artifact, setup }) => {\n refuseSetup(setup);\n return { state: { version: 2, deviceId: artifact.deviceId, generation: 0 } };\n },\n awaitRunning: async (ctx, rawState, { timeoutMs }) => {\n const { deviceId } = deviceStateSchema.pick({ deviceId: true }).parse(rawState);\n await awaitOnline(callerFor(ctx), deviceId, timeoutMs);\n },\n // A Device never takes a lease back at an old generation once it lapsed,\n // so every attach leases a later one, and no process from before survives.\n attach: async (ctx, { state: rawState }) => {\n const state = deviceStateSchema.parse(rawState);\n const { handle, generation } = await lease({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n passive: false,\n });\n return {\n handle,\n state: { ...state, generation },\n fresh: state.generation === 0,\n booted: true,\n };\n },\n attachPassive: async (ctx, { state: rawState }) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await lease({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n passive: true,\n })\n ).handle;\n },\n // The lease lapses on its own; the next generation retires its processes.\n detach: async (_ctx, { state }) => deviceStateSchema.parse(state),\n park: async (_ctx, { state }) => deviceStateSchema.parse(state),\n // The Device stays the person's; its workspace for this sandbox is let go.\n delete: async (ctx, { state: rawState }) => {\n const state = deviceStateSchema.parse(rawState);\n await api.releaseWorkspaceLeases({\n caller: callerFor(ctx),\n deviceId: state.deviceId,\n bindingId: ctx.sandboxId,\n });\n },\n };\n };\n\n return defineSandboxProvider({\n name: DEVICE_PROVIDER,\n stateProtocolVersion: 2,\n environment,\n });\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["DEVICE_PROVIDER", "SandboxPlatform"]
7
7
  }
@@ -1,13 +1,13 @@
1
1
  import { type WorkspaceEgressRequestIface, type WorkspaceEgressStatusIface } from '@finalai/agent-protocol/device-egress';
2
- import { SandboxAccess } from '@finalai/agent-provider';
3
- import { type SandboxNetworkingExtensionIface } from '@finalai/agent-provider/sandbox';
4
- import type { NetworkSandboxSessionIface, SandboxActingUserIface, SandboxCredentialsIface, SandboxRunResultIface, SandboxProviderHandleIface, NetworkPolicyIface } from '@finalai/agent-provider/sandbox';
2
+ import { type SandboxGrantIface, type SandboxNetworkingExtensionIface } from '@finalai/agent-provider/sandbox';
3
+ import type { NetworkSandboxSessionIface, SandboxActingUserIface, SandboxRunResultIface, SandboxProviderHandleIface, NetworkPolicyIface } from '@finalai/agent-provider/sandbox';
4
+ import { type DeviceAccessIface, type DeviceApiCredentialsIface } from "./access.mjs";
5
5
  import { type DeviceApiClientIface, type DeviceApiConfigIface } from "./device-api.mjs";
6
6
  export interface WorkspaceEgressManagerOptionsIface {
7
7
  sandbox: NetworkSandboxSessionIface;
8
8
  workspaceId: string;
9
9
  organizationId: string;
10
- credentials?: SandboxCredentialsIface;
10
+ credentials?: DeviceApiCredentialsIface;
11
11
  actingUser: SandboxActingUserIface;
12
12
  api: DeviceApiConfigIface | (() => DeviceApiConfigIface);
13
13
  privilegedRun?: (request: {
@@ -37,7 +37,7 @@ export interface WorkspaceEgressManagerOptionsIface {
37
37
  export declare const suspendWorkspaceEgress: (args: {
38
38
  api: DeviceApiConfigIface | (() => DeviceApiConfigIface);
39
39
  organizationId: string;
40
- credentials?: SandboxCredentialsIface;
40
+ credentials?: DeviceApiCredentialsIface;
41
41
  workspaceId: string;
42
42
  actingUser: SandboxActingUserIface;
43
43
  }) => Promise<void>;
@@ -54,7 +54,7 @@ export declare const createWorkspaceEgressManager: (options: WorkspaceEgressMana
54
54
  export declare const preserveWorkspaceNetworkPolicy: (options: {
55
55
  api: DeviceApiConfigIface | (() => DeviceApiConfigIface);
56
56
  organizationId: string;
57
- credentials?: SandboxCredentialsIface;
57
+ credentials?: DeviceApiCredentialsIface;
58
58
  workspaceId: string;
59
59
  policy: NetworkPolicyIface;
60
60
  client?: Pick<DeviceApiClientIface, "egress">;
@@ -62,12 +62,13 @@ export declare const preserveWorkspaceNetworkPolicy: (options: {
62
62
  export type WorkspaceEgressManagerIface = ReturnType<typeof createWorkspaceEgressManager>;
63
63
  export interface WorkspaceDeviceNetworkingOptionsIface {
64
64
  api: WorkspaceEgressManagerOptionsIface['api'];
65
+ access?: DeviceAccessIface;
65
66
  mcpFetch?: typeof globalThis.fetch;
66
67
  needed: (args: {
67
68
  organizationId: string;
69
+ projectId: string;
68
70
  workspaceId: string;
69
71
  teardown?: boolean;
70
- access?: SandboxAccess;
71
72
  }) => Promise<boolean>;
72
73
  allowConfiguration?: boolean;
73
74
  authorizeOwnerChange?: (args: {
@@ -80,14 +81,14 @@ export declare const createWorkspaceDeviceNetworking: (options: WorkspaceDeviceN
80
81
  export declare const withWorkspaceEgress: <S extends NetworkSandboxSessionIface>(options: {
81
82
  handle: SandboxProviderHandleIface<S>;
82
83
  manager: WorkspaceEgressManagerIface;
83
- access: SandboxAccess;
84
+ grant: SandboxGrantIface;
84
85
  beforeUse?: () => Promise<void>;
85
86
  initialized?: boolean;
86
87
  fetch?: typeof globalThis.fetch;
87
88
  }) => Promise<SandboxProviderHandleIface<S>>;
88
89
  export declare const withWorkspaceAdmission: <S extends NetworkSandboxSessionIface>(options: {
89
90
  handle: SandboxProviderHandleIface<S>;
90
- access: SandboxAccess;
91
+ grant: SandboxGrantIface;
91
92
  admission: () => Promise<void>;
92
93
  fetch?: typeof globalThis.fetch;
93
94
  }) => Promise<SandboxProviderHandleIface<S>>;