@tangle-network/agent-app 0.44.27 → 0.44.29

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.
@@ -1,4 +1,4 @@
1
- import { a as AppToolContext } from './types-BCxK0wyS.js';
1
+ import { A as AppToolContext } from './types-DbU-oO5h.js';
2
2
 
3
3
  /**
4
4
  * Header names carrying the server-set per-turn context + the capability token.
@@ -14,8 +14,8 @@ import '../contract-CEewO6DI.js';
14
14
  import '../types-DB82fktc.js';
15
15
  import '../plans/index.js';
16
16
  import '@tangle-network/sandbox';
17
- import '../auth-anc7mv2W.js';
18
- import '../types-BCxK0wyS.js';
17
+ import '../auth-DJs6lfAs.js';
18
+ import '../types-DbU-oO5h.js';
19
19
  import '../harness/index.js';
20
20
  import '../model-CdCDfBA9.js';
21
21
  import '../budget-BOucfcb_.js';
@@ -93,7 +93,7 @@ import {
93
93
  flattenHistory,
94
94
  readSandboxBinaryBytes,
95
95
  statSandboxFileSize
96
- } from "../chunk-7775L5NN.js";
96
+ } from "../chunk-BAC2B2KI.js";
97
97
  import "../chunk-LWSJK546.js";
98
98
  import "../chunk-CQZSAR77.js";
99
99
  import "../chunk-ICOHEZK6.js";
@@ -304,6 +304,11 @@ function createWorkspaceSandboxConnectionHandler(opts) {
304
304
  });
305
305
  };
306
306
  }
307
+ function sandboxSidecarProxyUrl(baseUrl, sandboxId) {
308
+ if (!baseUrl) throw new Error("baseUrl is required");
309
+ if (!sandboxId) throw new Error("sandboxId is required");
310
+ return new URL(`/v1/sidecar-proxy/${encodeURIComponent(sandboxId)}`, baseUrl).toString().replace(/\/+$/, "");
311
+ }
307
312
  function createWorkspaceSandboxRuntimeProxyHandler(opts) {
308
313
  return async function handleWorkspaceSandboxRuntimeProxy({ request, params }) {
309
314
  const user = await opts.requireUser(request);
@@ -325,10 +330,7 @@ function createWorkspaceSandboxRuntimeProxyHandler(opts) {
325
330
  const runtimeConnection = await opts.getSandboxRuntimeConnection?.({ request, userId: user.id, workspaceId, sandboxId });
326
331
  const directRuntimeConnection = runtimeConnection?.runtimeUrl && runtimeConnection.authToken ? runtimeConnection : null;
327
332
  const credentials = directRuntimeConnection ? null : await opts.getSandboxApiCredentials({ request, userId: user.id, workspaceId, sandboxId });
328
- const upstreamUrl = directRuntimeConnection ? new URL(encodedRuntimePath, `${directRuntimeConnection.runtimeUrl.replace(/\/+$/, "")}/`) : new URL(
329
- `/v1/sandboxes/${encodeURIComponent(sandboxId)}/runtime/${encodedRuntimePath}`,
330
- credentials.baseUrl
331
- );
333
+ const upstreamUrl = directRuntimeConnection ? new URL(encodedRuntimePath, `${directRuntimeConnection.runtimeUrl.replace(/\/+$/, "")}/`) : new URL(encodedRuntimePath, `${sandboxSidecarProxyUrl(credentials.baseUrl, sandboxId)}/`);
332
334
  upstreamUrl.search = requestUrl.search;
333
335
  const headers = buildSandboxRuntimeProxyHeaders(
334
336
  request.headers,
@@ -405,17 +407,41 @@ function createWorkspaceSandboxTerminalUpgradeHandler(opts) {
405
407
  const runtimeConnection = await opts.getSandboxRuntimeConnection?.({ request, userId: user.id, workspaceId, sandboxId });
406
408
  const directRuntimeConnection = runtimeConnection?.runtimeUrl && runtimeConnection.authToken ? runtimeConnection : null;
407
409
  const credentials = directRuntimeConnection ? null : await opts.getSandboxApiCredentials({ request, userId: user.id, workspaceId, sandboxId });
408
- const upstreamUrl = directRuntimeConnection ? new URL(subPath, `${directRuntimeConnection.runtimeUrl.replace(/\/+$/, "")}/`) : new URL(`/v1/sandboxes/${encodeURIComponent(sandboxId)}/runtime/${subPath}`, credentials.baseUrl);
410
+ const upstreamUrl = directRuntimeConnection ? new URL(subPath, `${directRuntimeConnection.runtimeUrl.replace(/\/+$/, "")}/`) : new URL(subPath, `${sandboxSidecarProxyUrl(credentials.baseUrl, sandboxId)}/`);
409
411
  upstreamUrl.search = url.search;
410
412
  const upstreamHeaders = new Headers(request.headers);
411
413
  const upstreamBearer = directRuntimeConnection?.authToken ?? credentials.apiKey;
412
414
  upstreamHeaders.set("Authorization", `Bearer ${upstreamBearer}`);
413
415
  upstreamHeaders.delete("host");
416
+ const browserProtocol = selectedBearerSubprotocol(request.headers.get("Sec-WebSocket-Protocol"));
414
417
  stripBearerSubprotocol(upstreamHeaders);
415
418
  const fetchImpl = opts.fetch ?? fetch;
416
- return fetchImpl(upstreamUrl.toString(), { method: request.method, headers: upstreamHeaders });
419
+ const upstream = await fetchImpl(upstreamUrl.toString(), { method: request.method, headers: upstreamHeaders });
420
+ const echo = terminalUpgradeSubprotocolEcho(upstream, browserProtocol);
421
+ if (!echo) return upstream;
422
+ return new Response(null, {
423
+ status: echo.status,
424
+ statusText: echo.statusText,
425
+ headers: echo.headers,
426
+ webSocket: upstream.webSocket ?? null
427
+ });
417
428
  };
418
429
  }
430
+ function terminalUpgradeSubprotocolEcho(upstream, browserProtocol) {
431
+ if (upstream.status !== 101 || !browserProtocol) return null;
432
+ if (upstream.headers.has("Sec-WebSocket-Protocol")) return null;
433
+ const headers = new Headers(upstream.headers);
434
+ headers.set("Sec-WebSocket-Protocol", browserProtocol);
435
+ return { status: upstream.status, statusText: upstream.statusText ?? "", headers };
436
+ }
437
+ function selectedBearerSubprotocol(value) {
438
+ if (!value) return null;
439
+ for (const part of value.split(",")) {
440
+ const protocol = part.trim();
441
+ if (protocol.toLowerCase().startsWith(BEARER_SUBPROTOCOL_PREFIX)) return protocol;
442
+ }
443
+ return null;
444
+ }
419
445
  var DEFAULT_RUNTIME_PROXY_HEADERS = ["accept", "content-type", "last-event-id", "x-session-id"];
420
446
  function buildSandboxRuntimeProxyHeaders(source, sandboxApiKey, forwardHeaders = DEFAULT_RUNTIME_PROXY_HEADERS) {
421
447
  const headers = new Headers();
@@ -484,6 +510,139 @@ function validateTerminalSubject(subject) {
484
510
  if (!subject.sandboxId) throw new Error("sandboxId is required");
485
511
  }
486
512
 
513
+ // src/sandbox/prewarm.ts
514
+ function prewarmKey(scope) {
515
+ return `${scope.workspaceId}::${scope.harness}`;
516
+ }
517
+ function errText(err) {
518
+ return err instanceof Error ? err.message : String(err);
519
+ }
520
+ function createSandboxPrewarmer(shell, options) {
521
+ const mode = options.mode ?? "resume-only";
522
+ const claimTtlSeconds = options.claimTtlSeconds ?? 180;
523
+ const failureCooldownMs = options.failureCooldownMs ?? 6e4;
524
+ const now = options.now ?? (() => Date.now());
525
+ const claim = options.claim === "single-isolate-only" ? null : options.claim;
526
+ const inFlight = /* @__PURE__ */ new Map();
527
+ const failures = /* @__PURE__ */ new Map();
528
+ const emit = (event) => {
529
+ try {
530
+ options.onEvent?.(event);
531
+ } catch {
532
+ }
533
+ };
534
+ async function releaseClaim(key) {
535
+ if (!claim) return;
536
+ try {
537
+ await claim.release(key);
538
+ } catch {
539
+ }
540
+ }
541
+ async function runWarm(key, scope, holdsClaim) {
542
+ const startedAt = now();
543
+ emit({ type: "started", key, workspaceId: scope.workspaceId });
544
+ try {
545
+ const box = await ensureWorkspaceSandbox(shell, {
546
+ workspaceId: scope.workspaceId,
547
+ userId: scope.userId,
548
+ harness: scope.harness,
549
+ billingOwnerId: scope.billingOwnerId
550
+ });
551
+ const ms = now() - startedAt;
552
+ failures.delete(key);
553
+ emit({ type: "succeeded", key, workspaceId: scope.workspaceId, boxId: box.id, ms });
554
+ return { ok: true, boxId: box.id, ms };
555
+ } catch (err) {
556
+ const ms = now() - startedAt;
557
+ const error = errText(err);
558
+ failures.set(key, { error, until: now() + failureCooldownMs });
559
+ emit({ type: "failed", key, workspaceId: scope.workspaceId, error, ms });
560
+ return { ok: false, error, ms };
561
+ } finally {
562
+ inFlight.delete(key);
563
+ if (holdsClaim) await releaseClaim(key);
564
+ }
565
+ }
566
+ return {
567
+ async prewarm(scope) {
568
+ const key = prewarmKey(scope);
569
+ if (inFlight.has(key)) {
570
+ emit({ type: "skipped", key, workspaceId: scope.workspaceId, outcome: "already-warming" });
571
+ return { outcome: "already-warming" };
572
+ }
573
+ const failure = failures.get(key);
574
+ if (failure && now() < failure.until) {
575
+ emit({ type: "skipped", key, workspaceId: scope.workspaceId, outcome: "cooling-down" });
576
+ return { outcome: "cooling-down" };
577
+ }
578
+ let settle = () => {
579
+ };
580
+ const reservation = new Promise((resolve) => {
581
+ settle = resolve;
582
+ });
583
+ inFlight.set(key, reservation);
584
+ const abandon = (outcome) => {
585
+ inFlight.delete(key);
586
+ settle({ ok: false, error: outcome, ms: 0 });
587
+ emit({ type: "skipped", key, workspaceId: scope.workspaceId, outcome });
588
+ return { outcome };
589
+ };
590
+ try {
591
+ const peek = await peekWorkspaceSandbox(shell, {
592
+ workspaceId: scope.workspaceId,
593
+ userId: scope.userId
594
+ });
595
+ if (peek.status === "running") return abandon("already-running");
596
+ if (peek.status === "absent" && mode === "resume-only") {
597
+ return abandon("absent-and-resume-only");
598
+ }
599
+ if (options.shouldPrewarm) {
600
+ const allowed = await options.shouldPrewarm(scope);
601
+ if (!allowed) return abandon("declined-by-policy");
602
+ }
603
+ let holdsClaim = false;
604
+ if (claim) {
605
+ holdsClaim = await claim.acquire(key, claimTtlSeconds);
606
+ if (!holdsClaim) return abandon("warming-elsewhere");
607
+ }
608
+ const warm = runWarm(key, scope, holdsClaim);
609
+ void warm.then(settle);
610
+ return { outcome: "started", completion: warm };
611
+ } catch (err) {
612
+ const error = errText(err);
613
+ inFlight.delete(key);
614
+ failures.set(key, { error, until: now() + failureCooldownMs });
615
+ settle({ ok: false, error, ms: 0 });
616
+ emit({ type: "failed", key, workspaceId: scope.workspaceId, error, ms: 0 });
617
+ return { outcome: "cooling-down" };
618
+ }
619
+ },
620
+ async readiness(scope) {
621
+ const key = prewarmKey(scope);
622
+ const peek = await peekWorkspaceSandbox(shell, {
623
+ workspaceId: scope.workspaceId,
624
+ userId: scope.userId
625
+ });
626
+ if (peek.status === "running") return { status: "ready", boxId: peek.box.id };
627
+ if (inFlight.has(key)) return { status: "warming" };
628
+ if (claim?.isHeld) {
629
+ try {
630
+ if (await claim.isHeld(key)) return { status: "warming" };
631
+ } catch {
632
+ }
633
+ }
634
+ const failure = failures.get(key);
635
+ if (failure && now() < failure.until) {
636
+ return { status: "failed", error: failure.error, retryAfterMs: failure.until - now() };
637
+ }
638
+ return { status: "absent" };
639
+ },
640
+ clearFailure(scope) {
641
+ failures.delete(prewarmKey(scope));
642
+ }
643
+ };
644
+ }
645
+
487
646
  // src/sandbox/index.ts
488
647
  var DEFAULT_SANDBOX_DIRECT_KEY_NAMES = [
489
648
  "TCLOUD_SANDBOX_API_KEY",
@@ -1726,15 +1885,19 @@ export {
1726
1885
  createSandboxTerminalToken,
1727
1886
  verifySandboxTerminalToken,
1728
1887
  createWorkspaceSandboxConnectionHandler,
1888
+ sandboxSidecarProxyUrl,
1729
1889
  createWorkspaceSandboxRuntimeProxyHandler,
1730
1890
  matchSandboxTerminalWsPath,
1731
1891
  isSandboxTerminalWsUpgrade,
1732
1892
  createWorkspaceSandboxTerminalUpgradeHandler,
1893
+ terminalUpgradeSubprotocolEcho,
1894
+ selectedBearerSubprotocol,
1733
1895
  buildSandboxRuntimeProxyHeaders,
1734
1896
  encodeSandboxRuntimePath,
1735
1897
  bearerToken,
1736
1898
  bearerSubprotocolToken,
1737
1899
  terminalTokenFromRequest,
1900
+ createSandboxPrewarmer,
1738
1901
  resolveSandboxClientCredentials,
1739
1902
  DEFAULT_SANDBOX_RESOURCES,
1740
1903
  getClient,
@@ -1778,4 +1941,4 @@ export {
1778
1941
  isTerminalPromptEvent,
1779
1942
  detectInteractiveQuestion
1780
1943
  };
1781
- //# sourceMappingURL=chunk-7775L5NN.js.map
1944
+ //# sourceMappingURL=chunk-BAC2B2KI.js.map