impel-cli 0.20.1 → 0.20.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/apps.js CHANGED
@@ -107,8 +107,8 @@ export const PINNED_VENDOR_APPS = Object.freeze({
107
107
  // The Microsoft Store manifest identifies this exact reviewed build.
108
108
  // Keep one Windows package version so the manifest contract and local
109
109
  // AppX validation cannot silently drift apart again.
110
- packageVersion: "26.727.6591.0",
111
- codexVersion: "0.146.0-alpha.9.2",
110
+ packageVersion: "26.730.8199.0",
111
+ codexVersion: "0.147.0-alpha.1.2",
112
112
  publisherId: "2p2nqsd0c76g0",
113
113
  executable: "app\\ChatGPT.exe",
114
114
  updateManifestUrl: "https://persistent.oaistatic.com/codex-app-prod/windows-store-update.json",
@@ -274,7 +274,9 @@ export function managedAppIdentity(target, tenantId = null, tenantName = null) {
274
274
  // unsupported-selection "Reset to default" treatment.
275
275
  // 27: register the separate tenant-bound Tasks MCP server in managed Claude
276
276
  // and ChatGPT/Codex profiles without changing the specialist MCP server.
277
- export const CURRENT_CONFIG_VERSION = 27;
277
+ // 28: bind generated native-agent profiles to the durable local composite MCP
278
+ // transport and remove model-controlled upstream start/read polling.
279
+ export const CURRENT_CONFIG_VERSION = 28;
278
280
 
279
281
  // Identifies the bundle-BUILDING logic — the asar patches, plist rewrites,
280
282
  // helper rebranding, and signing. A vendored bundle is rebuilt only when this
@@ -8,6 +8,15 @@ import {
8
8
  resolveDefaultGateway,
9
9
  } from "../config.js";
10
10
  import { parseFlags } from "../args.js";
11
+ import {
12
+ NATIVE_AGENT_RECOVER_TOOL,
13
+ NATIVE_AGENT_RESUME_TOOL,
14
+ NATIVE_AGENT_RUN_TOOL,
15
+ NativeAgentCompositeTransport,
16
+ nativeAgentCompositeTools,
17
+ nativeAgentToolCallResult,
18
+ } from "../nativeAgentTransport.js";
19
+ import { IMPEL_NATIVE_AGENT_MCP_TARGET } from "../selfInvocation.js";
11
20
  import { ensureTenantSelection, normalizeTenantId, tenantCredential } from "../tenants.js";
12
21
 
13
22
  const TASKS_TARGET = "tasks";
@@ -168,16 +177,142 @@ function tasksJsonRpcHttpError(id, contentType, body, httpStatus) {
168
177
  return JSON.stringify({ jsonrpc: "2.0", id: id ?? null, error });
169
178
  }
170
179
 
180
+ function nativeRpcResult(id, result) {
181
+ return JSON.stringify({ jsonrpc: "2.0", id, result });
182
+ }
183
+
184
+ function nativeProgressToken(message) {
185
+ const token = message?.params?._meta?.progressToken;
186
+ return typeof token === "string" || (typeof token === "number" && Number.isFinite(token))
187
+ ? token
188
+ : null;
189
+ }
190
+
191
+ /** Run the isolated local composite MCP server without changing proxy mode. */
192
+ export function runNativeAgentMcpServer({
193
+ transport,
194
+ input = process.stdin,
195
+ output = process.stdout,
196
+ recoveryOnly = false,
197
+ }) {
198
+ const lines = readline.createInterface({ input, crlfDelay: Infinity });
199
+ const active = new Map();
200
+ const pending = new Set();
201
+ let progress = 0;
202
+ const write = (value) => output.write(`${value}\n`);
203
+
204
+ function schedule(message) {
205
+ const request = tasksJsonRpcRequestId(message);
206
+ if (!request.valid) {
207
+ write(rpcError(request.id, "Invalid JSON-RPC request."));
208
+ return;
209
+ }
210
+ if (message.method === "notifications/initialized") return;
211
+ if (message.method === "notifications/cancelled") {
212
+ active.get(message.params?.requestId)?.abort();
213
+ return;
214
+ }
215
+ if (!Object.hasOwn(message, "id")) return;
216
+
217
+ const controller = new AbortController();
218
+ active.set(message.id, controller);
219
+ const task = (async () => {
220
+ try {
221
+ if (message.method === "initialize") {
222
+ write(nativeRpcResult(message.id, {
223
+ protocolVersion: "2025-06-18",
224
+ capabilities: { tools: {} },
225
+ serverInfo: { name: "impel-native-agent", version: "1.0.0" },
226
+ }));
227
+ return;
228
+ }
229
+ if (message.method === "ping") {
230
+ write(nativeRpcResult(message.id, {}));
231
+ return;
232
+ }
233
+ if (message.method === "tools/list") {
234
+ write(nativeRpcResult(message.id, { tools: nativeAgentCompositeTools({ recoveryOnly }) }));
235
+ return;
236
+ }
237
+ if (message.method !== "tools/call") {
238
+ throw new Error("unsupported native-agent MCP method");
239
+ }
240
+ const name = message.params?.name;
241
+ const args = message.params?.arguments ?? {};
242
+ const progressToken = nativeProgressToken(message);
243
+ const onProgress = progressToken === null ? undefined : ({ status }) => {
244
+ progress += 1;
245
+ write(JSON.stringify({
246
+ jsonrpc: "2.0",
247
+ method: "notifications/progress",
248
+ params: { progressToken, progress, message: `native-agent run ${status}` },
249
+ }));
250
+ };
251
+ let value;
252
+ if (name === NATIVE_AGENT_RUN_TOOL) {
253
+ if (recoveryOnly) throw new Error("retired native-agent bindings cannot start new runs");
254
+ value = await transport.run(args, { signal: controller.signal, onProgress });
255
+ } else if (name === NATIVE_AGENT_RESUME_TOOL) {
256
+ value = await transport.resume(args, { signal: controller.signal, onProgress });
257
+ } else if (name === NATIVE_AGENT_RECOVER_TOOL) {
258
+ value = transport.recover(args);
259
+ } else {
260
+ throw new Error("unsupported native-agent MCP tool");
261
+ }
262
+ write(nativeRpcResult(message.id, nativeAgentToolCallResult(value)));
263
+ } catch (error) {
264
+ const cancelled = error?.name === "AbortError";
265
+ write(JSON.stringify({
266
+ jsonrpc: "2.0",
267
+ id: message.id,
268
+ error: {
269
+ code: cancelled ? -32800 : -32000,
270
+ message: cancelled
271
+ ? "Native-agent attachment cancelled; resume it with the same handle."
272
+ : redactSecretText(error?.message || error),
273
+ },
274
+ }));
275
+ } finally {
276
+ active.delete(message.id);
277
+ }
278
+ })();
279
+ pending.add(task);
280
+ task.finally(() => pending.delete(task));
281
+ }
282
+
283
+ lines.on("line", (line) => {
284
+ const trimmed = line.trim();
285
+ if (!trimmed) return;
286
+ try {
287
+ schedule(JSON.parse(trimmed));
288
+ } catch {
289
+ write(rpcError(null, "Invalid JSON-RPC request."));
290
+ }
291
+ });
292
+ return new Promise((resolve) => {
293
+ lines.on("close", async () => {
294
+ for (const controller of active.values()) controller.abort();
295
+ await Promise.allSettled([...pending]);
296
+ resolve();
297
+ });
298
+ });
299
+ }
300
+
171
301
  export async function cmdMcp(argv = []) {
172
302
  const { flags, positionals } = parseFlags(argv, {
173
303
  target: { type: "string" },
174
304
  tenant: { type: "string" },
305
+ "agent-id": { type: "string" },
306
+ "scope-param": { type: "string" },
307
+ "policy-fingerprint": { type: "string" },
308
+ "recovery-only": { type: "boolean" },
175
309
  });
176
310
  const targetSpecified = Object.hasOwn(flags, "target");
177
- if (targetSpecified && flags.target !== TASKS_TARGET) {
178
- throw new Error("unsupported MCP target; expected `--target tasks`");
311
+ if (targetSpecified && ![TASKS_TARGET, IMPEL_NATIVE_AGENT_MCP_TARGET].includes(flags.target)) {
312
+ throw new Error("unsupported MCP target; expected `--target tasks` or `--target native-agent`");
179
313
  }
180
314
  const tasksTarget = flags.target === TASKS_TARGET;
315
+ const nativeAgentTarget = flags.target === IMPEL_NATIVE_AGENT_MCP_TARGET;
181
316
  if (tasksTarget) {
182
317
  const unsupportedFlags = Object.keys(flags).filter((name) => !["target", "tenant"].includes(name));
183
318
  if (unsupportedFlags.length > 0 || positionals.length > 0) {
@@ -187,6 +322,23 @@ export async function cmdMcp(argv = []) {
187
322
  throw new Error("Tasks MCP requires a fixed `--tenant <tenant>` argument");
188
323
  }
189
324
  }
325
+ if (nativeAgentTarget) {
326
+ const expectedFlags = ["target", "tenant", "agent-id", "scope-param", "policy-fingerprint", "recovery-only"];
327
+ const unsupportedFlags = Object.keys(flags).filter((name) => !expectedFlags.includes(name));
328
+ if (unsupportedFlags.length > 0 || positionals.length > 0) {
329
+ throw new Error("unsupported native-agent MCP arguments");
330
+ }
331
+ for (const flag of ["tenant", "agent-id", "scope-param", "policy-fingerprint"]) {
332
+ if (typeof flags[flag] !== "string" || !flags[flag].trim()) {
333
+ throw new Error(`native-agent MCP requires a fixed --${flag} argument`);
334
+ }
335
+ }
336
+ } else if (!tasksTarget && (Object.hasOwn(flags, "agent-id")
337
+ || Object.hasOwn(flags, "scope-param")
338
+ || Object.hasOwn(flags, "policy-fingerprint")
339
+ || Object.hasOwn(flags, "recovery-only"))) {
340
+ throw new Error("native-agent binding flags require `--target native-agent`");
341
+ }
190
342
  const config = loadConfig();
191
343
  if (!config?.pat) {
192
344
  throw new Error("not authenticated; run `impel setup` (or `impel auth`) first");
@@ -196,6 +348,19 @@ export async function cmdMcp(argv = []) {
196
348
  : (await ensureTenantSelection(config)).tenantId;
197
349
  const gatewayUrl = config.gatewayUrl || resolveDefaultGateway();
198
350
  const credential = tasksTarget ? null : tenantCredential(config.pat, tenantId);
351
+ if (nativeAgentTarget) {
352
+ return runNativeAgentMcpServer({
353
+ transport: new NativeAgentCompositeTransport({
354
+ tenantId,
355
+ agentId: flags["agent-id"],
356
+ scopeParam: flags["scope-param"],
357
+ policyFingerprint: flags["policy-fingerprint"],
358
+ gatewayUrl,
359
+ credential,
360
+ }),
361
+ recoveryOnly: flags["recovery-only"] === true,
362
+ });
363
+ }
199
364
  const endpoint = tasksTarget ? null : `${gatewayUrl}/mcp`;
200
365
  const input = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
201
366
  let sessionId;