@addai/node 0.30.13 → 0.30.15

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 (143) hide show
  1. package/dist/auto-update.d.ts +3 -0
  2. package/dist/auto-update.js +15 -0
  3. package/dist/capabilities.d.ts +17 -3
  4. package/dist/capabilities.js +18 -8
  5. package/dist/claude-models.d.ts +4 -12
  6. package/dist/claude-models.js +12 -88
  7. package/dist/claude-print.d.ts +15 -0
  8. package/dist/claude-print.js +16 -0
  9. package/dist/codex-models.d.ts +81 -0
  10. package/dist/codex-models.js +385 -0
  11. package/dist/codex-spawn.d.ts +19 -8
  12. package/dist/codex-spawn.js +68 -16
  13. package/dist/command-runner.js +31 -9
  14. package/dist/desktop/docker.js +2 -0
  15. package/dist/desktop/manager.d.ts +2 -1
  16. package/dist/desktop/manager.js +51 -1
  17. package/dist/desktop/provider.d.ts +1 -1
  18. package/dist/desktop/provider.js +2 -0
  19. package/dist/desktop/relay-client.js +7 -0
  20. package/dist/desktop/spec.d.ts +2 -0
  21. package/dist/direct/aios-bridge.d.ts +13 -0
  22. package/dist/direct/aios-bridge.js +177 -0
  23. package/dist/direct/aios-mcp.d.ts +2 -0
  24. package/dist/direct/aios-mcp.js +132 -0
  25. package/dist/direct/aios-tools.d.ts +351 -0
  26. package/dist/direct/aios-tools.js +120 -0
  27. package/dist/direct/board-chat-provider.d.ts +64 -0
  28. package/dist/direct/board-chat-provider.js +154 -0
  29. package/dist/direct/board-chat.d.ts +40 -0
  30. package/dist/direct/board-chat.js +414 -0
  31. package/dist/direct/config.d.ts +2 -0
  32. package/dist/direct/config.js +60 -0
  33. package/dist/direct/launch.d.ts +14 -0
  34. package/dist/direct/launch.js +121 -0
  35. package/dist/direct/protocol.d.ts +46 -0
  36. package/dist/direct/protocol.js +84 -0
  37. package/dist/direct/relay-main.d.ts +1 -0
  38. package/dist/direct/relay-main.js +21 -0
  39. package/dist/direct/relay.d.ts +26 -0
  40. package/dist/direct/relay.js +334 -0
  41. package/dist/direct/sessions.d.ts +61 -0
  42. package/dist/direct/sessions.js +368 -0
  43. package/dist/direct/supervisor.d.ts +9 -0
  44. package/dist/direct/supervisor.js +137 -0
  45. package/dist/direct/terminal.d.ts +1 -0
  46. package/dist/direct/terminal.js +106 -0
  47. package/dist/direct/worker.d.ts +1 -0
  48. package/dist/direct/worker.js +278 -0
  49. package/dist/entitybox/container-action.cjs +85 -0
  50. package/dist/entitybox/container-watchdog.cjs +70 -0
  51. package/dist/entitybox/model-entry.d.ts +3 -0
  52. package/dist/entitybox/model-entry.js +233 -0
  53. package/dist/entitybox/model-mcp-proxy.d.ts +3 -0
  54. package/dist/entitybox/model-mcp-proxy.js +85 -0
  55. package/dist/entitybox-application-boundary.d.ts +59 -0
  56. package/dist/entitybox-application-boundary.js +161 -0
  57. package/dist/entitybox-application-guard-core.d.ts +44 -0
  58. package/dist/entitybox-application-guard-core.js +164 -0
  59. package/dist/entitybox-application-guard-process.d.ts +1 -0
  60. package/dist/entitybox-application-guard-process.js +83 -0
  61. package/dist/entitybox-application-guard.d.ts +46 -0
  62. package/dist/entitybox-application-guard.js +239 -0
  63. package/dist/entitybox-capacity-health-host.d.ts +12 -0
  64. package/dist/entitybox-capacity-health-host.js +80 -0
  65. package/dist/entitybox-capacity-health-worker.d.ts +22 -0
  66. package/dist/entitybox-capacity-health-worker.js +168 -0
  67. package/dist/entitybox-capacity-host.d.ts +17 -0
  68. package/dist/entitybox-capacity-host.js +155 -0
  69. package/dist/entitybox-capacity-owner-process.d.ts +1 -0
  70. package/dist/entitybox-capacity-owner-process.js +32 -0
  71. package/dist/entitybox-capacity-owner.d.ts +16 -0
  72. package/dist/entitybox-capacity-owner.js +90 -0
  73. package/dist/entitybox-capacity-programs.d.ts +7 -0
  74. package/dist/entitybox-capacity-programs.js +49 -0
  75. package/dist/entitybox-capacity-provisioner.d.ts +82 -0
  76. package/dist/entitybox-capacity-provisioner.js +404 -0
  77. package/dist/entitybox-capacity-publication.d.ts +29 -0
  78. package/dist/entitybox-capacity-publication.js +199 -0
  79. package/dist/entitybox-capacity-registration.d.ts +36 -0
  80. package/dist/entitybox-capacity-registration.js +75 -0
  81. package/dist/entitybox-capacity-worker.d.ts +29 -0
  82. package/dist/entitybox-capacity-worker.js +113 -0
  83. package/dist/entitybox-computer-task.d.ts +69 -0
  84. package/dist/entitybox-computer-task.js +176 -0
  85. package/dist/entitybox-controller-recovery.d.ts +40 -0
  86. package/dist/entitybox-controller-recovery.js +156 -0
  87. package/dist/entitybox-desktop-action-server.d.ts +34 -0
  88. package/dist/entitybox-desktop-action-server.js +176 -0
  89. package/dist/entitybox-desktop-admission.d.ts +32 -0
  90. package/dist/entitybox-desktop-admission.js +79 -0
  91. package/dist/entitybox-display-binding.d.ts +13 -0
  92. package/dist/entitybox-display-binding.js +31 -0
  93. package/dist/entitybox-execution-container.d.ts +82 -0
  94. package/dist/entitybox-execution-container.js +450 -0
  95. package/dist/entitybox-execution-host.d.ts +40 -0
  96. package/dist/entitybox-execution-host.js +168 -0
  97. package/dist/entitybox-execution-lease.d.ts +81 -0
  98. package/dist/entitybox-execution-lease.js +332 -0
  99. package/dist/entitybox-file-inputs.d.ts +51 -0
  100. package/dist/entitybox-file-inputs.js +182 -0
  101. package/dist/entitybox-file-session.d.ts +38 -0
  102. package/dist/entitybox-file-session.js +82 -0
  103. package/dist/entitybox-host-process.d.ts +12 -0
  104. package/dist/entitybox-host-process.js +120 -0
  105. package/dist/entitybox-install.d.ts +10 -0
  106. package/dist/entitybox-install.js +40 -0
  107. package/dist/entitybox-mcp-broker.d.ts +77 -0
  108. package/dist/entitybox-mcp-broker.js +420 -0
  109. package/dist/entitybox-mcp-contract.d.ts +10 -0
  110. package/dist/entitybox-mcp-contract.js +24 -0
  111. package/dist/entitybox-mcp-guard.d.ts +3 -0
  112. package/dist/entitybox-mcp-guard.js +167 -0
  113. package/dist/entitybox-model-artifact.d.ts +9 -0
  114. package/dist/entitybox-model-artifact.js +83 -0
  115. package/dist/entitybox-native-services.d.ts +33 -0
  116. package/dist/entitybox-native-services.js +172 -0
  117. package/dist/entitybox-operation.d.ts +4 -0
  118. package/dist/entitybox-operation.js +34 -0
  119. package/dist/entitybox-provider-broker.d.ts +41 -0
  120. package/dist/entitybox-provider-broker.js +149 -0
  121. package/dist/entitybox-queue-request.d.ts +32 -0
  122. package/dist/entitybox-queue-request.js +141 -0
  123. package/dist/entitybox-recovery-host.d.ts +12 -0
  124. package/dist/entitybox-recovery-host.js +80 -0
  125. package/dist/entitybox-recovery-store.d.ts +33 -0
  126. package/dist/entitybox-recovery-store.js +150 -0
  127. package/dist/entitybox-recovery-worker.d.ts +30 -0
  128. package/dist/entitybox-recovery-worker.js +134 -0
  129. package/dist/entitybox-vault-provider.d.ts +27 -0
  130. package/dist/entitybox-vault-provider.js +127 -0
  131. package/dist/harness-registry.js +3 -1
  132. package/dist/index.js +72 -38
  133. package/dist/install.d.ts +14 -2
  134. package/dist/install.js +115 -10
  135. package/dist/model-cache.d.ts +38 -0
  136. package/dist/model-cache.js +137 -0
  137. package/dist/request-pump.d.ts +3 -0
  138. package/dist/request-pump.js +21 -7
  139. package/dist/session-runner.d.ts +8 -1
  140. package/dist/session-runner.js +56 -3
  141. package/dist/supabase-client.js +4 -1
  142. package/package.json +5 -1
  143. package/scripts/copy-assets.js +4 -2
@@ -0,0 +1,420 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.EntityboxMcpBroker = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const path = __importStar(require("path"));
39
+ const crypto_1 = require("crypto");
40
+ const entitybox_execution_lease_1 = require("./entitybox-execution-lease");
41
+ const entitybox_mcp_contract_1 = require("./entitybox-mcp-contract");
42
+ const MAX_RESPONSE = 32 * 1024 * 1024;
43
+ const PROTOCOL = '2025-03-26';
44
+ const unavailable = () => new entitybox_execution_lease_1.EntityboxExecutionError('The required desktop connection ended');
45
+ /** One required, scoped native MCP connection per isolated task. The model
46
+ * receives a small MCP facade over the existing broker pipe, not this process,
47
+ * command line, configuration, entity key, pairing token or lease nonce. */
48
+ class EntityboxMcpBroker {
49
+ lease;
50
+ proc;
51
+ closed;
52
+ didClose = false;
53
+ childPid;
54
+ buffer = Buffer.alloc(0);
55
+ diagnostics = '';
56
+ pending = new Map();
57
+ allowed;
58
+ secrets;
59
+ timeout;
60
+ actions;
61
+ catalog = [];
62
+ ready = false;
63
+ closing = false;
64
+ stopping;
65
+ clientPhase = 'new';
66
+ serial = Promise.resolve();
67
+ queued = 0;
68
+ failureCode;
69
+ constructor(lease, config) {
70
+ this.lease = lease;
71
+ lease.assertActive();
72
+ const descriptor = config?.connection;
73
+ const scope = (0, entitybox_mcp_contract_1.parseEntityboxMcpScope)(descriptor?.env?.ENTITYBOX_DESKTOP_SCOPE);
74
+ const actionScope = (0, entitybox_mcp_contract_1.parseEntityboxMcpScope)(config?.actions?.scope);
75
+ const sameScope = (s) => ['runId', 'desktopId', 'runtimeId'].every(k => s[k] === lease.identity.scope[k]);
76
+ if (!sameScope(scope) || !sameScope(actionScope)
77
+ || typeof config.actions.assertActive !== 'function' || typeof config.actions.stopAndVerify !== 'function'
78
+ || descriptor.command !== process.execPath || !Array.isArray(descriptor.args)
79
+ || descriptor.args[0] !== path.join(__dirname, 'entitybox-mcp-guard.js') || descriptor.args[1] !== '--'
80
+ || descriptor.args.length < 3 || descriptor.args.length > 40 || !descriptor.args.every(s => typeof s === 'string' && s.length > 0 && s.length < 16000)
81
+ || typeof descriptor.env.ENTITY_API_KEY !== 'string' || descriptor.env.ENTITY_API_KEY.length < 8
82
+ || !Object.entries(descriptor.env).every(([k, v]) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(k) && typeof v === 'string' && v.length < 65536)
83
+ || !Array.isArray(config.allowedTools) || !config.allowedTools.length || config.allowedTools.length > 64
84
+ || !config.allowedTools.every(name => typeof name === 'string' && /^[A-Za-z0-9_-]{1,128}$/.test(name))
85
+ || new Set(config.allowedTools).size !== config.allowedTools.length
86
+ || (config.secrets !== undefined && (!Array.isArray(config.secrets) || !config.secrets.every(s => typeof s === 'string' && s.length >= 8 && s.length < 65536))))
87
+ throw unavailable();
88
+ this.timeout = config.requestTimeoutMs ?? 120000;
89
+ if (!Number.isInteger(this.timeout) || this.timeout < 50 || this.timeout > 120000)
90
+ throw unavailable();
91
+ this.actions = config.actions;
92
+ this.allowed = new Set(config.allowedTools);
93
+ if (descriptor.env.ENTITYBOX_ACTION_TRANSPORT !== undefined && descriptor.env.ENTITYBOX_ACTION_TRANSPORT !== this.actions.transport?.configuration)
94
+ throw unavailable();
95
+ this.secrets = [...new Set([descriptor.env.ENTITY_API_KEY, ...(this.actions.transport ? [this.actions.transport.token] : []), ...(config.secrets ?? [])])];
96
+ // Preserve only native process/Docker discovery essentials from the host.
97
+ // The selected connection's resolved environment remains host-side.
98
+ const env = {};
99
+ for (const key of ['PATH', 'Path', 'SystemRoot', 'WINDIR', 'ComSpec', 'PATHEXT', 'TEMP', 'TMP', 'HOME', 'USERPROFILE', 'APPDATA', 'LOCALAPPDATA'])
100
+ if (process.env[key] !== undefined)
101
+ env[key] = process.env[key];
102
+ Object.assign(env, descriptor.env);
103
+ if (this.actions.transport)
104
+ env.ENTITYBOX_ACTION_TRANSPORT = this.actions.transport.configuration;
105
+ this.proc = (0, child_process_1.spawn)(descriptor.command, [...descriptor.args], { env, cwd: __dirname, windowsHide: true, stdio: ['pipe', 'pipe', 'pipe'] });
106
+ this.closed = new Promise(resolve => this.proc.once('close', () => {
107
+ this.didClose = true;
108
+ if (!this.closing)
109
+ this.fail();
110
+ this.rejectPending();
111
+ resolve();
112
+ }));
113
+ this.proc.on('error', () => this.fail());
114
+ this.proc.stdin.on('error', () => { if (!this.closing)
115
+ this.fail(); });
116
+ this.proc.stdout.on('data', chunk => this.consume(chunk));
117
+ this.proc.stdout.on('error', () => this.fail());
118
+ // Guard diagnostics are bounded and private. Raw MCP stderr is suppressed
119
+ // by the actual guard, including package-manager/credential diagnostics.
120
+ this.proc.stderr.setEncoding('utf8');
121
+ this.proc.stderr.on('data', chunk => {
122
+ this.diagnostics += chunk;
123
+ if (this.diagnostics.length > 4096) {
124
+ this.fail();
125
+ return;
126
+ }
127
+ let end;
128
+ while ((end = this.diagnostics.indexOf('\n')) >= 0) {
129
+ const line = this.diagnostics.slice(0, end);
130
+ this.diagnostics = this.diagnostics.slice(end + 1);
131
+ const match = /^\[entitybox-mcp\] child_pid=(\d+)$/.exec(line);
132
+ if (match) {
133
+ if (this.childPid !== undefined)
134
+ this.fail();
135
+ else
136
+ this.childPid = Number(match[1]);
137
+ }
138
+ }
139
+ });
140
+ lease.signal.addEventListener('abort', () => { void this.stopAndVerify().catch(() => { }); }, { once: true });
141
+ if (lease.signal.aborted)
142
+ this.fail();
143
+ }
144
+ static async connect(lease, config) {
145
+ let broker;
146
+ try {
147
+ broker = new EntityboxMcpBroker(lease, config);
148
+ await broker.active();
149
+ const initialized = await broker.rpc('initialize', { protocolVersion: PROTOCOL, capabilities: {}, clientInfo: { name: 'entitybox-native-broker', version: '1' } }, 15000);
150
+ (0, entitybox_mcp_contract_1.assertEntityboxMcpCapability)(initialized, lease.identity.scope);
151
+ if (config.actions.transport) {
152
+ const capability = initialized.capabilities?.experimental?.['ai.add.entitybox.actionTransport'];
153
+ const scoped = (0, entitybox_mcp_contract_1.parseEntityboxMcpScope)(capability?.scope);
154
+ if (capability.version !== 1 || capability.requestId !== lease.identity.requestId || capability.attemptId !== lease.identity.attemptId
155
+ || scoped.runId !== lease.identity.scope.runId || scoped.desktopId !== lease.identity.scope.desktopId || scoped.runtimeId !== lease.identity.scope.runtimeId)
156
+ throw unavailable();
157
+ }
158
+ if (initialized.protocolVersion !== PROTOCOL)
159
+ throw unavailable();
160
+ broker.notify('notifications/initialized');
161
+ const result = await broker.rpc('tools/list', {}, 15000);
162
+ if (!Array.isArray(result.tools) || result.tools.length > 256 || result.nextCursor !== undefined)
163
+ throw unavailable();
164
+ const names = new Set();
165
+ for (const tool of result.tools) {
166
+ if (!tool || typeof tool.name !== 'string' || names.has(tool.name))
167
+ throw unavailable();
168
+ names.add(tool.name);
169
+ if (broker.allowed.has(tool.name)) {
170
+ if (!tool.inputSchema || tool.inputSchema.type !== 'object')
171
+ throw unavailable();
172
+ broker.catalog.push(tool);
173
+ }
174
+ }
175
+ if (broker.catalog.length !== broker.allowed.size)
176
+ throw unavailable();
177
+ await broker.active();
178
+ broker.ready = true;
179
+ return broker;
180
+ }
181
+ catch {
182
+ lease.fence();
183
+ if (broker)
184
+ await broker.stopAndVerify().catch(() => { });
185
+ throw unavailable();
186
+ }
187
+ }
188
+ /** Public process identity for the owning runtime's resource ledger. */
189
+ get processId() { return this.proc.pid; }
190
+ async active() {
191
+ this.lease.assertActive();
192
+ if (this.closing || this.didClose)
193
+ throw unavailable();
194
+ await this.actions.assertActive();
195
+ this.lease.assertActive();
196
+ if (this.closing || this.didClose)
197
+ throw unavailable();
198
+ }
199
+ rejectPending() {
200
+ for (const request of this.pending.values()) {
201
+ clearTimeout(request.timer);
202
+ request.reject(unavailable());
203
+ }
204
+ this.pending.clear();
205
+ }
206
+ fail() {
207
+ if (!this.closing)
208
+ this.failureCode = 'entitybox_mcp_connection_failed';
209
+ this.rejectPending();
210
+ this.lease.fence();
211
+ }
212
+ consume(chunk) {
213
+ if (this.closing)
214
+ return;
215
+ try {
216
+ this.buffer = Buffer.concat([this.buffer, chunk]);
217
+ let end;
218
+ while ((end = this.buffer.indexOf(10)) >= 0) {
219
+ if (end > MAX_RESPONSE)
220
+ throw unavailable();
221
+ const response = JSON.parse(this.buffer.subarray(0, end).toString('utf8'));
222
+ this.buffer = this.buffer.subarray(end + 1);
223
+ if (!response || response.jsonrpc !== '2.0' || Array.isArray(response))
224
+ throw unavailable();
225
+ // This facade advertises no server-initiated requests/subscriptions.
226
+ // Bounded progress/list-change notifications carry no authority.
227
+ if (response.id === undefined && ['notifications/progress', 'notifications/tools/list_changed', 'notifications/message'].includes(response.method))
228
+ continue;
229
+ if (typeof response.id !== 'string' || response.method !== undefined)
230
+ throw unavailable();
231
+ const request = this.pending.get(response.id);
232
+ if (!request)
233
+ throw unavailable();
234
+ this.pending.delete(response.id);
235
+ clearTimeout(request.timer);
236
+ if (response.error || !response.result || typeof response.result !== 'object') {
237
+ request.reject(unavailable());
238
+ this.fail();
239
+ return;
240
+ }
241
+ request.resolve(response.result);
242
+ }
243
+ if (this.buffer.length > MAX_RESPONSE)
244
+ throw unavailable();
245
+ }
246
+ catch {
247
+ this.fail();
248
+ }
249
+ }
250
+ rpc(method, params, timeout = this.timeout) {
251
+ this.lease.assertActive();
252
+ if (this.closing || this.didClose || this.pending.size >= 8)
253
+ return Promise.reject(unavailable());
254
+ const id = (0, crypto_1.randomUUID)();
255
+ return new Promise((resolve, reject) => {
256
+ const timer = setTimeout(() => this.fail(), timeout);
257
+ this.pending.set(id, { resolve, reject, timer });
258
+ this.proc.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n', error => { if (error)
259
+ this.fail(); });
260
+ });
261
+ }
262
+ notify(method) {
263
+ this.lease.assertActive();
264
+ this.proc.stdin.write(JSON.stringify({ jsonrpc: '2.0', method }) + '\n', error => { if (error)
265
+ this.fail(); });
266
+ }
267
+ async handle(raw, signal, emit) {
268
+ const cancellation = raw;
269
+ if (cancellation?.kind === 'desktop_mcp' && cancellation.message?.jsonrpc === '2.0'
270
+ && cancellation.message.method === 'notifications/cancelled' && cancellation.message.id === undefined) {
271
+ // This control message must never wait behind the action it cancels.
272
+ this.fail();
273
+ throw unavailable();
274
+ }
275
+ // Serialize desktop requests; a queued action must recheck authority after
276
+ // the previous action settles. Model request IDs never become native IDs.
277
+ if (++this.queued > 8) {
278
+ this.queued--;
279
+ this.fail();
280
+ throw unavailable();
281
+ }
282
+ const abort = () => this.fail();
283
+ signal.addEventListener('abort', abort, { once: true });
284
+ if (signal.aborted)
285
+ abort();
286
+ const operation = this.serial.then(async () => {
287
+ try {
288
+ await this.active();
289
+ const r = raw, m = r?.message;
290
+ if (!this.ready || !r || r.kind !== 'desktop_mcp' || Object.keys(r).sort().join(',') !== 'kind,message'
291
+ || !m || m.jsonrpc !== '2.0' || typeof m.method !== 'string' || Array.isArray(m)
292
+ || Object.keys(m).some(k => !['jsonrpc', 'id', 'method', 'params'].includes(k))
293
+ || Buffer.byteLength(JSON.stringify(m)) > 512 * 1024
294
+ || (m.id !== undefined && !(typeof m.id === 'string' && m.id.length <= 128) && !(typeof m.id === 'number' && Number.isSafeInteger(m.id))))
295
+ throw unavailable();
296
+ let result;
297
+ if (m.method === 'initialize' && this.clientPhase === 'new' && m.id !== undefined && typeof m.params?.protocolVersion === 'string') {
298
+ this.clientPhase = 'initialized';
299
+ result = { protocolVersion: PROTOCOL, serverInfo: { name: 'entitybox-native-desktop', version: '1' }, capabilities: { tools: {}, experimental: { 'ai.add.entitybox.desktopScope': this.lease.identity.scope } } };
300
+ }
301
+ else if (m.method === 'notifications/initialized' && m.id === undefined && this.clientPhase === 'initialized') {
302
+ this.clientPhase = 'ready';
303
+ result = null;
304
+ }
305
+ else if (m.method === 'notifications/cancelled' && m.id === undefined) {
306
+ // Canceling an external desktop action needs task-wide stop proof.
307
+ this.fail();
308
+ throw unavailable();
309
+ }
310
+ else if (this.clientPhase !== 'ready' || m.id === undefined)
311
+ throw unavailable();
312
+ else if (m.method === 'ping')
313
+ result = {};
314
+ else if (m.method === 'tools/list' && (!m.params || Object.keys(m.params).length === 0))
315
+ result = { tools: this.catalog };
316
+ else if (m.method === 'tools/call' && m.params && typeof m.params.name === 'string' && this.allowed.has(m.params.name)
317
+ && Object.keys(m.params).every(k => ['name', 'arguments', '_meta'].includes(k))
318
+ && (m.params.arguments === undefined || (m.params.arguments && typeof m.params.arguments === 'object' && !Array.isArray(m.params.arguments)))) {
319
+ result = await this.rpc('tools/call', { name: m.params.name, arguments: m.params.arguments ?? {} });
320
+ }
321
+ else
322
+ throw unavailable();
323
+ await this.active();
324
+ if (signal.aborted)
325
+ throw unavailable();
326
+ let text = JSON.stringify(result === null ? null : { jsonrpc: '2.0', id: m.id, result });
327
+ // Scrub JSON-escaped representations too, retaining valid JSON even if
328
+ // a trusted connection secret contains a quote or newline.
329
+ for (const secret of this.secrets)
330
+ text = text.split(JSON.stringify(secret).slice(1, -1)).join('[redacted]');
331
+ const bytes = Buffer.from(text);
332
+ if (bytes.length > MAX_RESPONSE)
333
+ throw unavailable();
334
+ await emit({ type: 'head', status: 200, contentType: 'application/json' });
335
+ for (let offset = 0; offset < bytes.length; offset += 16384) {
336
+ this.lease.assertActive();
337
+ if (signal.aborted)
338
+ throw unavailable();
339
+ await emit({ type: 'data', data: bytes.subarray(offset, offset + 16384).toString('base64') });
340
+ }
341
+ this.lease.assertActive();
342
+ await emit({ type: 'end' });
343
+ }
344
+ catch {
345
+ this.fail();
346
+ throw unavailable();
347
+ }
348
+ });
349
+ this.serial = operation.catch(() => { });
350
+ try {
351
+ await operation;
352
+ }
353
+ finally {
354
+ this.queued--;
355
+ signal.removeEventListener('abort', abort);
356
+ }
357
+ }
358
+ /** Aggregate this with the isolated model owner's stop proof before calling
359
+ * lease.acknowledgeStopped. Never acknowledge on MCP/container exit alone. */
360
+ async stopAndVerify() {
361
+ if (!this.stopping) {
362
+ this.closing = true;
363
+ this.stopping = Promise.resolve().then(async () => {
364
+ this.lease.fence();
365
+ this.rejectPending();
366
+ const results = await Promise.allSettled([this.closeOwnedConnection(), this.actions.stopAndVerify()]);
367
+ if (results.some(r => r.status === 'rejected'))
368
+ throw new entitybox_execution_lease_1.EntityboxExecutionError('Desktop actions have not been confirmed stopped');
369
+ });
370
+ }
371
+ const stopped = this.stopping;
372
+ try {
373
+ await stopped;
374
+ }
375
+ catch (error) {
376
+ if (this.stopping === stopped)
377
+ this.stopping = undefined;
378
+ throw error;
379
+ }
380
+ }
381
+ async closeOwnedConnection() {
382
+ if (!this.didClose) {
383
+ this.proc.stdin.end();
384
+ let timer;
385
+ const wait = (ms) => Promise.race([this.closed.then(() => true), new Promise(r => { timer = setTimeout(() => r(false), ms); })]);
386
+ try {
387
+ if (!await wait(8000)) {
388
+ if (this.proc.exitCode === null && this.proc.signalCode === null) {
389
+ if (process.platform === 'win32' && this.proc.pid)
390
+ await new Promise(resolve => (0, child_process_1.execFile)('taskkill', ['/PID', String(this.proc.pid), '/T', '/F'], { windowsHide: true, timeout: 5000 }, () => resolve()));
391
+ else
392
+ this.proc.kill('SIGTERM'); // guard owns and stops its MCP child
393
+ }
394
+ clearTimeout(timer);
395
+ if (!await wait(8000))
396
+ throw unavailable();
397
+ }
398
+ }
399
+ finally {
400
+ clearTimeout(timer);
401
+ }
402
+ }
403
+ if (!this.didClose)
404
+ throw unavailable();
405
+ if (this.childPid) {
406
+ try {
407
+ process.kill(this.childPid, 0);
408
+ }
409
+ catch (error) {
410
+ if (error.code === 'ESRCH')
411
+ return;
412
+ throw unavailable();
413
+ }
414
+ // Never kill a reaped child's PID: it may have been reused. An ambiguous
415
+ // still-present identity refuses proof and retains the desktop slot.
416
+ throw unavailable();
417
+ }
418
+ }
419
+ }
420
+ exports.EntityboxMcpBroker = EntityboxMcpBroker;
@@ -0,0 +1,10 @@
1
+ /** Public compatibility metadata, not a credential or execution lease. */
2
+ export declare const ENTITYBOX_MCP_SCOPE_CAPABILITY = "ai.add.entitybox.desktopScope";
3
+ export interface EntityboxMcpScope {
4
+ version: 1;
5
+ runId: string;
6
+ desktopId: string;
7
+ runtimeId: string;
8
+ }
9
+ export declare function parseEntityboxMcpScope(raw: unknown): EntityboxMcpScope;
10
+ export declare function assertEntityboxMcpCapability(result: unknown, expected: EntityboxMcpScope): void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ENTITYBOX_MCP_SCOPE_CAPABILITY = void 0;
4
+ exports.parseEntityboxMcpScope = parseEntityboxMcpScope;
5
+ exports.assertEntityboxMcpCapability = assertEntityboxMcpCapability;
6
+ /** Public compatibility metadata, not a credential or execution lease. */
7
+ exports.ENTITYBOX_MCP_SCOPE_CAPABILITY = 'ai.add.entitybox.desktopScope';
8
+ function parseEntityboxMcpScope(raw) {
9
+ const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10
+ const value = typeof raw === 'string' && raw.length <= 512 ? JSON.parse(raw) : raw;
11
+ if (!value || typeof value !== 'object' || Array.isArray(value) || value.version !== 1
12
+ || Object.keys(value).sort().join(',') !== 'desktopId,runId,runtimeId,version'
13
+ || !['runId', 'desktopId', 'runtimeId'].every(key => typeof value[key] === 'string' && uuid.test(value[key]))) {
14
+ throw new Error('Invalid EntityBox desktop scope');
15
+ }
16
+ return Object.freeze({ version: 1, runId: value.runId.toLowerCase(), desktopId: value.desktopId.toLowerCase(), runtimeId: value.runtimeId.toLowerCase() });
17
+ }
18
+ function assertEntityboxMcpCapability(result, expected) {
19
+ const capabilities = result?.capabilities;
20
+ const actual = parseEntityboxMcpScope(capabilities?.experimental?.[exports.ENTITYBOX_MCP_SCOPE_CAPABILITY]);
21
+ if (actual.runId !== expected.runId || actual.desktopId !== expected.desktopId || actual.runtimeId !== expected.runtimeId) {
22
+ throw new Error('The desktop connection did not acknowledge this task scope');
23
+ }
24
+ }
@@ -0,0 +1,3 @@
1
+ export declare function runEntityboxMcpGuard(options?: {
2
+ startupTimeoutMs?: number;
3
+ }): void;
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runEntityboxMcpGuard = runEntityboxMcpGuard;
4
+ // Wrap only EntityBox's desktop MCP. Validate the initialize response from the
5
+ // SAME child that receives subsequent messages, before exposing it to an agent.
6
+ const child_process_1 = require("child_process");
7
+ const entitybox_mcp_contract_1 = require("./entitybox-mcp-contract");
8
+ const MAX_MESSAGE_BYTES = 32 * 1024 * 1024;
9
+ const ERROR_MESSAGE = 'The native desktop connection cannot verify this computer task. Update the runtime and desktop connection, then start a new task.';
10
+ function runEntityboxMcpGuard(options = {}) {
11
+ let phase = 'new';
12
+ let child, requestId;
13
+ let timer, exitCode = 0;
14
+ let expected;
15
+ const [separator, command, ...args] = process.argv.slice(2);
16
+ try {
17
+ if (separator !== '--' || !command)
18
+ throw new Error();
19
+ expected = (0, entitybox_mcp_contract_1.parseEntityboxMcpScope)(process.env.ENTITYBOX_DESKTOP_SCOPE);
20
+ }
21
+ catch {
22
+ process.stderr.write('[entitybox-mcp] invalid_configuration\n');
23
+ process.exitCode = 1;
24
+ return;
25
+ }
26
+ const finish = () => {
27
+ if (timer)
28
+ clearTimeout(timer);
29
+ process.stdin.destroy();
30
+ process.exitCode = exitCode;
31
+ process.stdout.end();
32
+ };
33
+ const stop = (code, reason) => {
34
+ if (phase === 'closing')
35
+ return;
36
+ phase = 'closing';
37
+ exitCode = code;
38
+ if (timer)
39
+ clearTimeout(timer);
40
+ if (reason)
41
+ process.stderr.write('[entitybox-mcp] ' + reason + '\n');
42
+ if (reason && requestId !== undefined && !process.stdout.destroyed) {
43
+ process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: requestId, error: { code: -32001, message: ERROR_MESSAGE } }) + '\n');
44
+ }
45
+ if (!child || child.exitCode !== null || child.signalCode !== null) {
46
+ finish();
47
+ return;
48
+ }
49
+ // Only this directly owned child/tree is eligible for termination. This
50
+ // does not cancel work already started inside a desktop container.
51
+ child.stdin.destroy();
52
+ if (process.platform === 'win32' && child.pid) {
53
+ (0, child_process_1.execFile)('taskkill', ['/PID', String(child.pid), '/T', '/F'], { windowsHide: true, timeout: 5000 }, () => {
54
+ if (child && child.exitCode === null && child.signalCode === null)
55
+ child.kill('SIGKILL');
56
+ });
57
+ }
58
+ else if (child.pid) {
59
+ try {
60
+ process.kill(-child.pid, 'SIGKILL');
61
+ }
62
+ catch {
63
+ child.kill('SIGKILL');
64
+ }
65
+ }
66
+ };
67
+ const send = (source, target, line) => {
68
+ if (!target.write(line + '\n')) {
69
+ source.pause();
70
+ target.once('drain', () => source.resume());
71
+ }
72
+ };
73
+ const readLines = (source, accept) => {
74
+ let buffer = Buffer.alloc(0);
75
+ source.on('data', (chunk) => {
76
+ if (phase === 'closing')
77
+ return;
78
+ buffer = Buffer.concat([buffer, chunk]);
79
+ let newline;
80
+ while ((newline = buffer.indexOf(10)) !== -1) {
81
+ if (newline > MAX_MESSAGE_BYTES) {
82
+ stop(1, 'message_too_large');
83
+ return;
84
+ }
85
+ const line = buffer.subarray(0, newline).toString('utf8').replace(/\r$/, '');
86
+ buffer = buffer.subarray(newline + 1);
87
+ try {
88
+ const message = JSON.parse(line);
89
+ if (!message || Array.isArray(message) || message.jsonrpc !== '2.0')
90
+ throw new Error();
91
+ accept(message, line);
92
+ }
93
+ catch {
94
+ stop(1, 'invalid_protocol');
95
+ }
96
+ if (phase === 'closing')
97
+ return;
98
+ }
99
+ if (buffer.length > MAX_MESSAGE_BYTES)
100
+ stop(1, 'message_too_large');
101
+ });
102
+ source.on('error', () => stop(1, 'transport_error'));
103
+ };
104
+ readLines(process.stdin, (message, line) => {
105
+ if (phase === 'ready') {
106
+ if (message.method === 'initialize') {
107
+ stop(1, 'duplicate_initialize');
108
+ return;
109
+ }
110
+ send(process.stdin, child.stdin, line);
111
+ return;
112
+ }
113
+ if (phase !== 'new' || message.method !== 'initialize' || !['string', 'number'].includes(typeof message.id)) {
114
+ stop(1, 'initialize_required');
115
+ return;
116
+ }
117
+ requestId = message.id;
118
+ phase = 'starting';
119
+ try {
120
+ child = (0, child_process_1.spawn)(command, args, { env: process.env, cwd: process.cwd(), windowsHide: true, detached: process.platform !== 'win32', stdio: ['pipe', 'pipe', 'pipe'] });
121
+ }
122
+ catch {
123
+ stop(1, 'connection_start_failed');
124
+ return;
125
+ }
126
+ child.once('spawn', () => process.stderr.write('[entitybox-mcp] child_pid=' + child.pid + '\n'));
127
+ child.on('error', () => stop(1, 'connection_start_failed'));
128
+ child.stdin.on('error', () => stop(1, 'transport_error'));
129
+ // Never echo raw startup/stderr diagnostics: package scripts may include
130
+ // credentials or complete command arguments in them.
131
+ child.stderr.resume();
132
+ readLines(child.stdout, (response, responseLine) => {
133
+ if (phase === 'starting') {
134
+ if (response.id !== requestId || response.method !== undefined || response.error || !response.result) {
135
+ stop(1, 'scope_not_acknowledged');
136
+ return;
137
+ }
138
+ try {
139
+ (0, entitybox_mcp_contract_1.assertEntityboxMcpCapability)(response.result, expected);
140
+ }
141
+ catch {
142
+ stop(1, 'scope_not_acknowledged');
143
+ return;
144
+ }
145
+ phase = 'ready';
146
+ if (timer)
147
+ clearTimeout(timer);
148
+ requestId = undefined;
149
+ }
150
+ if (phase === 'ready')
151
+ send(child.stdout, process.stdout, responseLine);
152
+ });
153
+ child.once('close', code => {
154
+ if (phase !== 'closing')
155
+ stop(code === 0 ? 0 : 1, phase === 'ready' ? undefined : 'connection_ended_before_initialize');
156
+ finish();
157
+ });
158
+ timer = setTimeout(() => stop(1, 'initialize_timeout'), options.startupTimeoutMs ?? 15000);
159
+ send(process.stdin, child.stdin, line);
160
+ });
161
+ process.stdin.on('end', () => stop(0));
162
+ process.stdout.on('error', () => stop(1));
163
+ process.on('SIGINT', () => stop(0));
164
+ process.on('SIGTERM', () => stop(0));
165
+ }
166
+ if (require.main === module)
167
+ runEntityboxMcpGuard();
@@ -0,0 +1,9 @@
1
+ /** Explicit compiled adapter dependencies. Never enumerate host configuration,
2
+ * login state, provider directories or the rest of the runtime distribution. */
3
+ export declare const ENTITYBOX_MODEL_FILES: readonly ["paths.js", "claude-binary.js", "win.js", "claude-system-prompt.js", "prompt-transport.js", "claude-print.js", "entitybox/model-mcp-proxy.js", "entitybox/model-entry.js"];
4
+ export interface EntityboxModelArtifact {
5
+ files: Readonly<Record<string, string>>;
6
+ sha256: string;
7
+ }
8
+ export declare function readEntityboxModelArtifact(compiledRoot?: string): EntityboxModelArtifact;
9
+ export declare const ENTITYBOX_MODEL_LOADER: string;