orkestrator 2.26.0-alpha → 2.26.2-beta

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/dist/client.js CHANGED
@@ -7444,7 +7444,7 @@ import path4 from "path";
7444
7444
  // ../../apps/backend/package.json
7445
7445
  var package_default = {
7446
7446
  name: "@orkestrator/backend",
7447
- version: "2.26.0-alpha",
7447
+ version: "2.26.2-beta",
7448
7448
  private: true,
7449
7449
  type: "module",
7450
7450
  exports: {
@@ -7458,7 +7458,7 @@ var package_default = {
7458
7458
  "start:web": "bun dist/main.js --fallback-host 127.0.0.1",
7459
7459
  "start:web-public": "bun dist/main.js --tailscale-serve --allowed-origins https://orkestrator.dev,https://www.orkestrator.dev",
7460
7460
  test: "bun test --preload ../../tests/setup-node.ts src tests --parallel",
7461
- "test:workspace": "bun test --preload ../../tests/setup-node.ts src tests --only-failures ${ORKESTRATOR_TEST_TIMINGS_DIR:+--timings=$ORKESTRATOR_TEST_TIMINGS_DIR/backend.json --update-timings} --parallel=${ORKESTRATOR_TEST_WORKERS:-2}",
7461
+ "test:workspace": "bun test --preload ../../tests/setup-node.ts src tests --only-failures ${ORKESTRATOR_TEST_TIMINGS_DIR:+--timings=$ORKESTRATOR_TEST_TIMINGS_DIR/backend.json --update-timings} $ORKESTRATOR_BACKEND_TEST_IGNORE --parallel=${ORKESTRATOR_BACKEND_TEST_WORKERS:-${ORKESTRATOR_TEST_WORKERS:-2}}",
7462
7462
  typecheck: "bunx tsc --noEmit -p tsconfig.json && bunx tsc --noEmit -p tsconfig.test.json"
7463
7463
  },
7464
7464
  dependencies: {
@@ -7485,8 +7485,26 @@ var package_default = {
7485
7485
  }
7486
7486
  };
7487
7487
 
7488
- // ../../apps/backend/src/core/constants.ts
7489
- var APP_SLUG = "orkestrator-v2";
7488
+ // ../protocol/src/network-allowlist.ts
7489
+ var DEFAULT_ALLOWED_DOMAINS = Object.freeze([
7490
+ "github.com",
7491
+ "api.github.com",
7492
+ "registry.npmjs.org",
7493
+ "nodejs.org",
7494
+ "bun.sh",
7495
+ "mise.jdx.dev",
7496
+ "mise-versions.jdx.dev",
7497
+ "aube.jdx.dev",
7498
+ "api.anthropic.com",
7499
+ "sentry.io",
7500
+ "statsig.anthropic.com",
7501
+ "statsig.com",
7502
+ "marketplace.visualstudio.com",
7503
+ "vscode.blob.core.windows.net",
7504
+ "update.code.visualstudio.com",
7505
+ "mcp.context7.com",
7506
+ "cdn.playwright.dev"
7507
+ ]);
7490
7508
  var AGENT_NETWORK_DOMAINS_BY_PLATFORM = Object.freeze({
7491
7509
  codex: Object.freeze(["chatgpt.com", "auth.openai.com"]),
7492
7510
  cursor: Object.freeze([
@@ -7521,6 +7539,9 @@ var AGENT_NETWORK_DOMAINS_BY_PLATFORM = Object.freeze({
7521
7539
  "openrouter.ai"
7522
7540
  ])
7523
7541
  });
7542
+
7543
+ // ../../apps/backend/src/core/constants.ts
7544
+ var APP_SLUG = "orkestrator-v2";
7524
7545
  var MAX_CODEX_CONCURRENT_THREADS = Number.MAX_SAFE_INTEGER - 1;
7525
7546
  function sanitizeAppVersion(raw) {
7526
7547
  const trimmed = raw?.trim() ?? "";
@@ -8306,8 +8327,63 @@ async function submit(context, action, input, options2) {
8306
8327
  const outcome = await session.mutate(action, input, stringOption(options2, "request-id"));
8307
8328
  return { session, ...outcome };
8308
8329
  }
8309
- async function readOperation(session, operationId) {
8310
- const { receipt } = await session.readWithReceipt("run.get", { operationId });
8330
+ async function submitForWait(context, action, input, options2, timeoutMs) {
8331
+ const deadline = Date.now() + timeoutMs;
8332
+ const session = await context.session();
8333
+ try {
8334
+ return {
8335
+ session,
8336
+ ...await session.mutate(action, input, stringOption(options2, "request-id"))
8337
+ };
8338
+ } catch (error) {
8339
+ const requestId = error instanceof CliError ? error.details?.requestId : undefined;
8340
+ const namespace = error instanceof CliError ? error.details?.namespace : undefined;
8341
+ if (!(error instanceof CliError) || error.code !== "transport-uncertain" || typeof requestId !== "string" || typeof namespace !== "string")
8342
+ throw error;
8343
+ let receipt;
8344
+ try {
8345
+ receipt = await observe(async () => {
8346
+ if (Date.now() >= deadline)
8347
+ throw new ObservationStopped("deadline");
8348
+ try {
8349
+ const found = await session.readWithReceipt("run.get", {
8350
+ requestId,
8351
+ action,
8352
+ namespace,
8353
+ submittedInput: input
8354
+ }, Math.max(1, deadline - Date.now()));
8355
+ return found.receipt ? { done: true, value: found.receipt } : { done: false };
8356
+ } catch (lookup) {
8357
+ if (lookup instanceof CliError && (lookup.code === "not-found" || lookup.code === "connection-failed"))
8358
+ return { done: false };
8359
+ throw lookup;
8360
+ }
8361
+ }, { timeoutMs: Math.max(0, deadline - Date.now()), signal: context.signal });
8362
+ } catch (lookup) {
8363
+ const stopped = context.signal?.aborted ? new ObservationStopped("signal", signalName(context.signal)) : lookup;
8364
+ if (stopped instanceof ObservationStopped && stopped.reason === "deadline")
8365
+ throw error;
8366
+ const failure = stopped instanceof ObservationStopped ? stoppedError(stopped, action, undefined, "admission") : stopped;
8367
+ if (failure instanceof CliError) {
8368
+ throw new CliError(failure.code, failure.message, {
8369
+ action,
8370
+ receipt: failure.receipt,
8371
+ retryable: failure.retryable,
8372
+ details: { ...error.details, ...failure.details }
8373
+ });
8374
+ }
8375
+ throw failure;
8376
+ }
8377
+ return {
8378
+ session,
8379
+ receipt,
8380
+ deadline,
8381
+ warnings: [`The ${action} response was lost; found the operation by its request key`]
8382
+ };
8383
+ }
8384
+ }
8385
+ async function readOperation(session, operationId, timeoutMs) {
8386
+ const { receipt } = await session.readWithReceipt("run.get", { operationId }, timeoutMs);
8311
8387
  if (!receipt)
8312
8388
  throw new CliError("response-invalid", "run.get returned no receipt");
8313
8389
  return receipt;
@@ -8337,9 +8413,20 @@ function receiptFailure(receipt) {
8337
8413
  }
8338
8414
  async function waitForOperation(context, session, initial, timeoutMs, options2 = {}) {
8339
8415
  let latest = initial;
8416
+ const deadline = Date.now() + timeoutMs;
8340
8417
  try {
8341
8418
  return await observe(async () => {
8342
- latest = await readOperation(session, initial.operationId);
8419
+ if (Date.now() >= deadline)
8420
+ throw new ObservationStopped("deadline");
8421
+ try {
8422
+ latest = await readOperation(session, initial.operationId, Math.max(1, deadline - Date.now()));
8423
+ } catch (error) {
8424
+ if (context.signal?.aborted)
8425
+ throw new ObservationStopped("signal", signalName(context.signal));
8426
+ if (Date.now() >= deadline && error instanceof CliError && error.code === "connection-failed")
8427
+ throw new ObservationStopped("deadline");
8428
+ throw error;
8429
+ }
8343
8430
  if (isTerminalReceipt(latest))
8344
8431
  return { done: true, value: latest };
8345
8432
  const execution = latest.execution;
@@ -8820,14 +8907,23 @@ class GatewayTransport {
8820
8907
  if (Buffer.byteLength(body) > PUBLIC_API_LIMITS.requestMaxBytes) {
8821
8908
  throw new CliError("input-too-large", "The request is larger than the client's request limit");
8822
8909
  }
8910
+ const deadline = options2.timeoutMs === undefined ? undefined : Date.now() + options2.timeoutMs;
8911
+ const sendWithBudget = (token) => {
8912
+ if (deadline === undefined)
8913
+ return this.send(body, token, options2);
8914
+ const remaining = deadline - Date.now();
8915
+ if (remaining <= 0)
8916
+ throw new CliError("connection-failed", "The request deadline passed", { retryable: true });
8917
+ return this.send(body, token, { ...options2, timeoutMs: remaining });
8918
+ };
8823
8919
  let token = await this.target.readToken();
8824
- let response = await this.send(body, token, options2);
8920
+ let response = await sendWithBudget(token);
8825
8921
  if (response.status === 401 && !options2.mutation) {
8826
8922
  await response.body?.cancel().catch(() => {
8827
8923
  return;
8828
8924
  });
8829
8925
  token = await this.target.readToken();
8830
- response = await this.send(body, token, options2);
8926
+ response = await sendWithBudget(token);
8831
8927
  }
8832
8928
  return this.decode(response, options2);
8833
8929
  }
@@ -8972,8 +9068,8 @@ class ClientSession {
8972
9068
  throw envelopeError(response);
8973
9069
  return response.result;
8974
9070
  }
8975
- async readWithReceipt(action, input) {
8976
- const response = await this.call(action, input, undefined, false);
9071
+ async readWithReceipt(action, input, timeoutMs) {
9072
+ const response = await this.call(action, input, undefined, false, timeoutMs);
8977
9073
  if (!response.ok)
8978
9074
  throw envelopeError(response);
8979
9075
  return { result: response.result, receipt: response.receipt };
@@ -8993,6 +9089,12 @@ class ClientSession {
8993
9089
  const key = requestId ?? `cli-${randomUUID2()}`;
8994
9090
  const intentDigest = createHash2("sha256").update(canonicalJson({ action, input })).digest("hex");
8995
9091
  const existing = await this.receipts.find(installationId, action, key);
9092
+ if (existing && existing.intentDigest !== intentDigest) {
9093
+ throw new CliError("request-conflict", "This saved request ID was used for a different intent", {
9094
+ action,
9095
+ details: { requestId: key, namespace: existing.namespace }
9096
+ });
9097
+ }
8996
9098
  const namespace = existing?.namespace ?? capabilities.requestKeys.currentNamespace;
8997
9099
  const now = new Date().toISOString();
8998
9100
  const local = {
@@ -9036,7 +9138,7 @@ class ClientSession {
9036
9138
  ...response.warnings ? { warnings: response.warnings } : {}
9037
9139
  };
9038
9140
  }
9039
- async call(action, input, request, mutation) {
9141
+ async call(action, input, request, mutation, timeoutMs) {
9040
9142
  const body = {
9041
9143
  schemaVersion: PUBLIC_API_SCHEMA_VERSION,
9042
9144
  action,
@@ -9044,7 +9146,7 @@ class ClientSession {
9044
9146
  input,
9045
9147
  ...request ? { request } : {}
9046
9148
  };
9047
- const raw = await this.transport.invoke(PUBLIC_ACTION_COMMAND, body, { mutation, signal: this.options.signal });
9149
+ const raw = await this.transport.invoke(PUBLIC_ACTION_COMMAND, body, { mutation, signal: this.options.signal, timeoutMs });
9048
9150
  if (!isPublicActionResponse(raw)) {
9049
9151
  throw new CliError(mutation ? "transport-uncertain" : "response-invalid", "The backend returned a response that does not match the public contract");
9050
9152
  }
@@ -11029,7 +11131,7 @@ var serviceCommands = [
11029
11131
  ["logs", path13.join(layout.logs, "service.log")]
11030
11132
  ]),
11031
11133
  "",
11032
- started ? "The service is running. Remote clients can update it from Settings \u2192 Updates or `orkestrator host update`." : `The unit is written but not loaded; load it with: ${serviceManagerCommands(unit).load.at(-1).join(" ")}`,
11134
+ started ? "The service is running. Remote clients can update it from Settings \u2192 Machines or `orkestrator host update`." : `The unit is written but not loaded; load it with: ${serviceManagerCommands(unit).load.at(-1).join(" ")}`,
11033
11135
  ...unit.manager === "systemd" ? ["Run `loginctl enable-linger $USER` so it keeps running after you log out."] : []
11034
11136
  ]
11035
11137
  };
@@ -11612,15 +11714,26 @@ var CONTROL_OPTIONS = [
11612
11714
  description: "Conversation mode."
11613
11715
  }
11614
11716
  ];
11615
- async function finishRun(context, parsed, base) {
11717
+ function waitBudget(options2) {
11718
+ return options2.timeout ?? DEFAULT_WAIT_MS;
11719
+ }
11720
+ async function submitRun(context, action, input, options2) {
11721
+ const submitted = options2.wait === true ? await submitForWait(context, action, input, options2, waitBudget(options2)) : await submit(context, action, input, options2);
11722
+ return {
11723
+ ...submitted,
11724
+ recoveryDeadline: "deadline" in submitted ? submitted.deadline : undefined
11725
+ };
11726
+ }
11727
+ async function finishRun(context, parsed, base, recoveryDeadline, fromReceipt) {
11616
11728
  if (parsed.options.wait !== true || !base.receipt) {
11617
11729
  return base;
11618
11730
  }
11619
11731
  const session = await context.session();
11620
- const final = await waitForOperation(context, session, base.receipt, parsed.options.timeout ?? DEFAULT_WAIT_MS, { continueOnInteraction: parsed.options["continue-on-interaction"] === true });
11732
+ const final = await waitForOperation(context, session, base.receipt, recoveryDeadline === undefined ? waitBudget(parsed.options) : Math.max(0, recoveryDeadline - Date.now()), { continueOnInteraction: parsed.options["continue-on-interaction"] === true });
11621
11733
  const failure = receiptFailure(final);
11622
11734
  return {
11623
11735
  ...base,
11736
+ ...fromReceipt?.(final),
11624
11737
  receipt: final,
11625
11738
  human: receiptLines(final),
11626
11739
  ...failure ? { failure } : {}
@@ -11804,27 +11917,32 @@ var sessionCommands = [
11804
11917
  throw new CliError("invalid-input", "--environment and --agent are required");
11805
11918
  }
11806
11919
  const prompt = await readPrompt(context.io, options2);
11807
- const { session, result, receipt, warnings } = await submit(context, "session.start", {
11920
+ const { session, result, receipt, warnings, recoveryDeadline } = await submitRun(context, "session.start", {
11808
11921
  environmentId,
11809
11922
  agent,
11810
11923
  prompt,
11811
11924
  ...options2.title !== undefined ? { title: options2.title } : {},
11812
11925
  ...controlsInput(options2)
11813
11926
  }, options2);
11927
+ const fromReceipt = (final) => ({
11928
+ result: {
11929
+ sessionId: final.resources.sessionId ?? null,
11930
+ tabId: final.resources.tabId ?? null,
11931
+ runId: final.operationId
11932
+ },
11933
+ ids: final.resources.sessionId ? [final.resources.sessionId] : []
11934
+ });
11814
11935
  return finishRun(context, parsed, {
11815
11936
  action: "session.start",
11816
- result,
11937
+ ...result ? { result, ids: [result.sessionId] } : fromReceipt(receipt),
11817
11938
  receipt,
11818
11939
  warnings,
11819
11940
  connection: session.identity,
11820
- ids: [result.sessionId],
11821
- human: [
11822
- ...keyValues([
11823
- ["session", result.sessionId],
11824
- ["run", result.runId]
11825
- ])
11826
- ]
11827
- });
11941
+ human: result ? keyValues([
11942
+ ["session", result.sessionId],
11943
+ ["run", result.runId]
11944
+ ]) : []
11945
+ }, recoveryDeadline, result ? undefined : fromReceipt);
11828
11946
  }
11829
11947
  },
11830
11948
  {
@@ -11848,20 +11966,21 @@ var sessionCommands = [
11848
11966
  const options2 = parsed.options;
11849
11967
  validateWaitOptions(options2);
11850
11968
  const prompt = await readPrompt(context.io, options2);
11851
- const { session, result, receipt, warnings } = await submit(context, "session.prompt", {
11969
+ const { session, result, receipt, warnings, recoveryDeadline } = await submitRun(context, "session.prompt", {
11852
11970
  sessionId: parsed.positionals.session,
11853
11971
  prompt,
11854
11972
  ...options2.mode !== undefined ? { mode: oneOf(options2.mode, ["plan", "build"], "--mode") } : {}
11855
11973
  }, options2);
11974
+ const runId = result?.runId ?? receipt.operationId;
11856
11975
  return finishRun(context, parsed, {
11857
11976
  action: "session.prompt",
11858
- result,
11977
+ result: result ?? { runId },
11859
11978
  receipt,
11860
11979
  warnings,
11861
11980
  connection: session.identity,
11862
- ids: [result.runId],
11863
- human: receipt ? receiptLines(receipt) : [`run ${result.runId}`]
11864
- });
11981
+ ids: [runId],
11982
+ human: receipt ? receiptLines(receipt) : [`run ${runId}`]
11983
+ }, recoveryDeadline);
11865
11984
  }
11866
11985
  },
11867
11986
  {
@@ -12590,7 +12709,12 @@ class OutputWriter {
12590
12709
  action: context.action,
12591
12710
  ok: false,
12592
12711
  ...context.connection ? { connection: context.connection } : {},
12593
- error: { code: "observation-interrupted", message, exitCode: code, details: { signal } },
12712
+ error: {
12713
+ code: "observation-interrupted",
12714
+ message,
12715
+ exitCode: code,
12716
+ details: { ...context.details, signal }
12717
+ },
12594
12718
  ...context.receipt ? { receipt: context.receipt } : {}
12595
12719
  })}
12596
12720
  `);
@@ -12721,6 +12845,7 @@ async function runClient(argv, options2) {
12721
12845
  if (error instanceof CliError && error.code === "observation-interrupted") {
12722
12846
  return output.interrupted(signalName(signal), {
12723
12847
  action: error.action ?? action,
12848
+ details: error.details,
12724
12849
  ...error.receipt ? { receipt: error.receipt } : {},
12725
12850
  ...connection ? { connection } : {}
12726
12851
  });