@tangle-network/agent-app 0.39.0 → 0.39.2

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 (35) hide show
  1. package/dist/{auth-BlS9GWfL.d.ts → auth-DcK5ERaL.d.ts} +7 -4
  2. package/dist/{chunk-IOREXWLM.js → chunk-2FDTJIU4.js} +8 -35
  3. package/dist/{chunk-IOREXWLM.js.map → chunk-2FDTJIU4.js.map} +1 -1
  4. package/dist/{chunk-JT6HHO5P.js → chunk-BUUJ7TEQ.js} +8 -33
  5. package/dist/{chunk-JT6HHO5P.js.map → chunk-BUUJ7TEQ.js.map} +1 -1
  6. package/dist/{chunk-CDYYR3Z7.js → chunk-DBIG2PHS.js} +208 -36
  7. package/dist/chunk-DBIG2PHS.js.map +1 -0
  8. package/dist/{chunk-VT5DI6GL.js → chunk-FDJ6JQCI.js} +3 -3
  9. package/dist/{chunk-JZZ6AWF4.js → chunk-PPSZNVKT.js} +9 -7
  10. package/dist/chunk-PPSZNVKT.js.map +1 -0
  11. package/dist/{chunk-A76ZHWNF.js → chunk-RH74YJIK.js} +31 -1
  12. package/dist/chunk-RH74YJIK.js.map +1 -0
  13. package/dist/{chunk-4IQMTTNE.js → chunk-TQ5M7BFV.js} +2 -2
  14. package/dist/design-canvas/index.d.ts +8 -21
  15. package/dist/design-canvas/index.js +2 -2
  16. package/dist/eval/index.d.ts +1 -1
  17. package/dist/index.d.ts +6 -6
  18. package/dist/index.js +12 -9
  19. package/dist/{mcp-BShTlESm.d.ts → mcp-4I_RHhNa.d.ts} +38 -3
  20. package/dist/preset-cloudflare/index.d.ts +1 -1
  21. package/dist/runtime/index.d.ts +3 -3
  22. package/dist/runtime/index.js +2 -2
  23. package/dist/sandbox/index.d.ts +7 -3
  24. package/dist/sandbox/index.js +6 -4
  25. package/dist/sequences/index.d.ts +10 -27
  26. package/dist/sequences/index.js +5 -4
  27. package/dist/tools/index.d.ts +5 -5
  28. package/dist/tools/index.js +5 -3
  29. package/dist/{types-2rOJo8Hc.d.ts → types-wHs0rmtu.d.ts} +12 -1
  30. package/package.json +1 -1
  31. package/dist/chunk-A76ZHWNF.js.map +0 -1
  32. package/dist/chunk-CDYYR3Z7.js.map +0 -1
  33. package/dist/chunk-JZZ6AWF4.js.map +0 -1
  34. /package/dist/{chunk-VT5DI6GL.js.map → chunk-FDJ6JQCI.js.map} +0 -0
  35. /package/dist/{chunk-4IQMTTNE.js.map → chunk-TQ5M7BFV.js.map} +0 -0
@@ -6,10 +6,10 @@ import {
6
6
  base64UrlEncodeText,
7
7
  constantTimeEqual,
8
8
  hmacSha256Base64Url
9
- } from "./chunk-VT5DI6GL.js";
9
+ } from "./chunk-FDJ6JQCI.js";
10
10
  import {
11
11
  buildAppToolMcpServer
12
- } from "./chunk-A76ZHWNF.js";
12
+ } from "./chunk-RH74YJIK.js";
13
13
  import {
14
14
  resolveTangleExecutionEnvironment,
15
15
  trimOrNull
@@ -19,6 +19,7 @@ import {
19
19
  import {
20
20
  Sandbox
21
21
  } from "@tangle-network/sandbox";
22
+ import { createHash } from "crypto";
22
23
 
23
24
  // src/sandbox/outcome.ts
24
25
  var ok = (value) => ({ succeeded: true, value });
@@ -592,21 +593,70 @@ function execWithTimeout(box, cmd, timeoutMs) {
592
593
  );
593
594
  });
594
595
  }
596
+ var TRANSIENT_EXEC_STATUS_CODES = /* @__PURE__ */ new Set([408, 409, 425, 429, 500, 502, 503, 504]);
597
+ var TRANSIENT_EXEC_CODE_RE = /^(ECONNRESET|ECONNREFUSED|ETIMEDOUT|EPIPE|ECONNABORTED)$/i;
598
+ var TRANSIENT_EXEC_MESSAGE_RE = /\b(408|409|425|429|500|502|503|504)\b|rate.?limit|too many requests|\bfetch failed\b|network error|connection reset|socket hang up|timed? out|service unavailable|bad gateway|gateway timeout|internal server error|\b(?:sidecar|runtime|exec(?:ution)?|terminal|sandbox|service|command(?:s)?|proxy)\b.{0,80}\bnot ready\b|\bnot ready\b.{0,80}\b(?:sidecar|runtime|exec(?:ution)?|terminal|sandbox|service|command(?:s)?|proxy)\b/i;
599
+ var RUNTIME_AUTH_REFRESH_SKEW_MS = 6e4;
600
+ function errorStatus(err) {
601
+ const rawStatus = err.status ?? err.statusCode ?? (err.response && typeof err.response === "object" ? err.response.status : void 0);
602
+ if (typeof rawStatus === "number") return rawStatus;
603
+ if (typeof rawStatus === "string" && /^\d+$/.test(rawStatus)) return Number(rawStatus);
604
+ return void 0;
605
+ }
606
+ function retryAfterMs(err, seen = /* @__PURE__ */ new Set()) {
607
+ if (!err || typeof err !== "object") return void 0;
608
+ if (seen.has(err)) return void 0;
609
+ seen.add(err);
610
+ const e = err;
611
+ if (typeof e.retryAfterMs === "number") return e.retryAfterMs;
612
+ return retryAfterMs(e.cause, seen);
613
+ }
614
+ function isTransientExecError(err, seen = /* @__PURE__ */ new Set()) {
615
+ if (!err || typeof err !== "object") return false;
616
+ if (seen.has(err)) return false;
617
+ seen.add(err);
618
+ const e = err;
619
+ const status = errorStatus(e);
620
+ if (status !== void 0 && TRANSIENT_EXEC_STATUS_CODES.has(status)) return true;
621
+ if (typeof e.code === "string") {
622
+ if (TRANSIENT_EXEC_CODE_RE.test(e.code)) return true;
623
+ if (/rate.?limit|too.?many.?requests|429|server.?error|service.?unavailable/i.test(e.code)) return true;
624
+ }
625
+ if (typeof e.message === "string" && TRANSIENT_EXEC_MESSAGE_RE.test(e.message)) return true;
626
+ return isTransientExecError(e.cause, seen);
627
+ }
628
+ function isRuntimeExecAuthError(err, seen = /* @__PURE__ */ new Set()) {
629
+ if (!err || typeof err !== "object") return false;
630
+ if (seen.has(err)) return false;
631
+ seen.add(err);
632
+ const e = err;
633
+ if (errorStatus(e) === 401) return true;
634
+ if (typeof e.code === "string" && /^(AUTH_ERROR|AUTHENTICATION_ERROR|UNAUTHORIZED|UNAUTHENTICATED|ERR_UNAUTHORIZED|ERR_UNAUTHENTICATED|401)$/i.test(e.code)) {
635
+ return true;
636
+ }
637
+ if (typeof e.name === "string" && /^(AuthError|AuthenticationError|UnauthorizedError|UnauthenticatedError|SandboxAuthError)$/i.test(e.name)) {
638
+ return true;
639
+ }
640
+ return isRuntimeExecAuthError(e.cause, seen);
641
+ }
642
+ function isRuntimeAuthRefreshDenied(err) {
643
+ if (!err || typeof err !== "object") return false;
644
+ return isRuntimeExecAuthError(err) || errorStatus(err) === 403;
645
+ }
595
646
  function transientExecError(err) {
596
647
  if (err instanceof ProfileWriteExecTimeoutError) return { retryable: true };
597
- if (err && typeof err === "object") {
598
- const e = err;
599
- const retryAfterMs = typeof e.retryAfterMs === "number" ? e.retryAfterMs : void 0;
600
- if (e.status === 429) return { retryable: true, retryAfterMs };
601
- if (typeof e.code === "string" && /rate.?limit|too.?many.?requests|429/i.test(e.code)) {
602
- return { retryable: true, retryAfterMs };
603
- }
604
- if (typeof e.message === "string" && /\b429\b|rate.?limit|too many requests/i.test(e.message)) {
605
- return { retryable: true, retryAfterMs };
606
- }
607
- }
648
+ if (isTransientExecError(err)) return { retryable: true, retryAfterMs: retryAfterMs(err) };
608
649
  return { retryable: false };
609
650
  }
651
+ function deferredProfileWriteFailed(stage, name, cause) {
652
+ return new Error(`deferred file write failed on ${stage} box ${name}: ${cause.message}`, { cause });
653
+ }
654
+ var SandboxRuntimeAuthRefreshError = class extends Error {
655
+ constructor(stage, name, detail, cause) {
656
+ super(`${stage} sandbox auth refresh failed for ${name}: ${detail}`, { cause });
657
+ this.name = "SandboxRuntimeAuthRefreshError";
658
+ }
659
+ };
610
660
  async function writeProfileFilesToBox(box, files, options = {}) {
611
661
  const execTimeoutMs = options.execTimeoutMs ?? PROFILE_WRITE_EXEC_TIMEOUT_MS;
612
662
  const paceMs = options.paceMs ?? PROFILE_WRITE_PACE_MS;
@@ -616,12 +666,19 @@ async function writeProfileFilesToBox(box, files, options = {}) {
616
666
  if (mount.resource.kind !== "inline") continue;
617
667
  const content = mount.resource.content ?? "";
618
668
  const b64 = Buffer.from(content, "utf8").toString("base64");
669
+ const b64Chunks = [];
670
+ for (let i = 0; i < b64.length; i += PROFILE_WRITE_B64_CHUNK_CHARS) {
671
+ b64Chunks.push(b64.slice(i, i + PROFILE_WRITE_B64_CHUNK_CHARS));
672
+ }
673
+ const expectedSha256 = createHash("sha256").update(content, "utf8").digest("hex");
619
674
  const path = mount.path;
620
675
  const dir = path.replace(/\/[^/]*$/, "");
621
676
  const isBin = /(^|\/)(s?bin)\//.test(path);
622
677
  const executable = mount.executable ?? isBin;
623
678
  const q = shellPath(path);
624
679
  const qb64 = shellPath(`${path}.b64`);
680
+ const qtmp = shellPath(`${path}.tmp`);
681
+ const qpartPrefix = shellPath(`${path}.b64.part.`);
625
682
  const step = async (cmd) => {
626
683
  for (let attempt = 0; ; attempt++) {
627
684
  if (execStarted && paceMs > 0) await sleep(paceMs);
@@ -637,35 +694,37 @@ async function writeProfileFilesToBox(box, files, options = {}) {
637
694
  }
638
695
  return ok(void 0);
639
696
  } catch (err) {
640
- const { retryable, retryAfterMs } = transientExecError(err);
697
+ const { retryable, retryAfterMs: retryAfterMs2 } = transientExecError(err);
641
698
  if (retryable && attempt < maxRetries) {
642
699
  const backoff = Math.min(
643
700
  PROFILE_WRITE_RETRY_BASE_MS * 2 ** attempt,
644
701
  PROFILE_WRITE_RETRY_MAX_MS
645
702
  );
646
- await sleep(retryAfterMs ?? backoff);
703
+ await sleep(retryAfterMs2 ?? backoff);
647
704
  continue;
648
705
  }
649
706
  return fail(new Error(`writeProfileFilesToBox: exec failed for ${path}`, { cause: err }));
650
707
  }
651
708
  }
652
709
  };
653
- const mkdir = dir && dir !== path ? `mkdir -p ${shellPath(dir)} && ` : "";
654
- let res = await step(`${mkdir}: > ${qb64}`);
710
+ const mkdir = dir && dir !== path ? `mkdir -p ${shellPath(dir)}` : ":";
711
+ let res = await step(mkdir);
655
712
  if (!res.succeeded) return res;
656
- for (let i = 0; i < b64.length; i += PROFILE_WRITE_B64_CHUNK_CHARS) {
657
- const slice = b64.slice(i, i + PROFILE_WRITE_B64_CHUNK_CHARS);
658
- res = await step(`printf '%s' '${slice}' >> ${qb64}`);
713
+ for (let i = 0; i < b64Chunks.length; i++) {
714
+ const slice = b64Chunks[i];
715
+ res = await step(`printf '%s' '${slice}' > ${shellPath(`${path}.b64.part.${i}`)}`);
659
716
  if (!res.succeeded) return res;
660
717
  }
661
- const chmod = executable ? ` && chmod +x ${q}` : "";
662
- res = await step(`base64 -d ${qb64} > ${q} && rm -f ${qb64}${chmod}`);
718
+ const chmod = executable ? `chmod +x ${q} || exit 1; ` : "";
719
+ const checksumMismatch = shellSingleQuote(`writeProfileFilesToBox: checksum mismatch for ${path}`);
720
+ const finalCmd = `expected='${expectedSha256}'; if [ -f ${q} ] && [ "$(sha256sum ${q} | awk '{print $1}')" = "$expected" ]; then ${chmod}rm -f ${qb64} ${qtmp}; i=0; while [ "$i" -lt ${b64Chunks.length} ]; do rm -f ${qpartPrefix}$i; i=$((i+1)); done; exit 0; fi; : > ${qb64} && i=0; while [ "$i" -lt ${b64Chunks.length} ]; do cat ${qpartPrefix}$i >> ${qb64} || exit 1; i=$((i+1)); done && base64 -d ${qb64} > ${qtmp} && [ "$(sha256sum ${qtmp} | awk '{print $1}')" = "$expected" ] || { echo ${checksumMismatch} >&2; exit 1; }; mv ${qtmp} ${q} && ${executable ? `chmod +x ${q} && ` : ""}[ "$(sha256sum ${q} | awk '{print $1}')" = "$expected" ] || { echo ${checksumMismatch} >&2; exit 1; }; rm -f ${qb64} ${qtmp}; i=0; while [ "$i" -lt ${b64Chunks.length} ]; do rm -f ${qpartPrefix}$i; i=$((i+1)); done`;
721
+ res = await step(finalCmd);
663
722
  if (!res.succeeded) return res;
664
723
  }
665
724
  return ok(void 0);
666
725
  }
667
- async function materializeDeferredFilesForExistingBox(shell, box, workspaceId, userId) {
668
- if (!shell.deferProfileFiles) return ok(void 0);
726
+ async function materializeDeferredFilesForExistingBox(shell, client, box, stage, name, workspaceId, userId) {
727
+ if (!shell.deferProfileFiles) return ok(box);
669
728
  const connectedIntegrationIds = await shell.connectedIntegrationIds(workspaceId);
670
729
  const buildCtx = {
671
730
  workspaceId,
@@ -675,8 +734,8 @@ async function materializeDeferredFilesForExistingBox(shell, box, workspaceId, u
675
734
  const files = await shell.files(buildCtx);
676
735
  const fullProfile = shell.profile({ extraFiles: files });
677
736
  const { deferredFiles } = splitDeferredProfileFiles(fullProfile);
678
- if (deferredFiles.length === 0) return ok(void 0);
679
- return writeProfileFilesToBox(box, deferredFiles);
737
+ if (deferredFiles.length === 0) return ok(box);
738
+ return writeDeferredFilesWithRuntimeAuthRefresh(client, box, deferredFiles, stage, name);
680
739
  }
681
740
  async function listRunning(client, name) {
682
741
  try {
@@ -726,6 +785,22 @@ function sandboxRuntimeUrl(box) {
726
785
  const connection = box.connection;
727
786
  return connection?.sidecarUrl ?? connection?.runtimeUrl;
728
787
  }
788
+ function runtimeAuthExpiresAtMs(value) {
789
+ if (value instanceof Date) return value.getTime();
790
+ if (typeof value === "number") return value;
791
+ if (typeof value !== "string" || value.trim() === "") return void 0;
792
+ const parsed = Date.parse(value);
793
+ return Number.isNaN(parsed) ? void 0 : parsed;
794
+ }
795
+ function hasFreshRuntimeExecAuth(box, now = Date.now()) {
796
+ const connection = box.connection;
797
+ const token = connection?.authToken ?? connection?.sidecarToken;
798
+ if (!sandboxRuntimeUrl(box) || !token) return false;
799
+ const expiresAt = runtimeAuthExpiresAtMs(
800
+ connection?.authTokenExpiresAt ?? connection?.sidecarTokenExpiresAt
801
+ );
802
+ return expiresAt === void 0 || expiresAt > now + RUNTIME_AUTH_REFRESH_SKEW_MS;
803
+ }
729
804
  function sandboxEdgeFailed(box) {
730
805
  const connection = box.connection;
731
806
  return connection?.edgeStatus === "failed" || Boolean(connection?.edgeError);
@@ -747,6 +822,84 @@ async function refreshRuntimeConnection(client, box) {
747
822
  }
748
823
  return current;
749
824
  }
825
+ async function bestEffortRefreshRuntimeExecAuth(client, box, stage, name) {
826
+ let current = box;
827
+ try {
828
+ await current.refresh();
829
+ if (hasFreshRuntimeExecAuth(current)) return ok(current);
830
+ } catch (err) {
831
+ if (isRuntimeAuthRefreshDenied(err)) {
832
+ return fail(
833
+ new SandboxRuntimeAuthRefreshError(
834
+ stage,
835
+ name,
836
+ "runtime exec auth refresh was unauthorized",
837
+ err
838
+ )
839
+ );
840
+ }
841
+ }
842
+ try {
843
+ const latest = await client.get(current.id);
844
+ if (latest) current = latest;
845
+ if (hasFreshRuntimeExecAuth(current)) return ok(current);
846
+ } catch (err) {
847
+ if (isRuntimeAuthRefreshDenied(err)) {
848
+ return fail(
849
+ new SandboxRuntimeAuthRefreshError(
850
+ stage,
851
+ name,
852
+ "runtime exec auth re-fetch was unauthorized",
853
+ err
854
+ )
855
+ );
856
+ }
857
+ }
858
+ return ok(current);
859
+ }
860
+ async function refreshRuntimeExecAuth(client, box, stage, name) {
861
+ let current = box;
862
+ let lastError;
863
+ const deadline = Date.now() + RUNTIME_CONNECTION_WAIT_MS;
864
+ while (Date.now() < deadline) {
865
+ try {
866
+ await current.refresh();
867
+ if (hasFreshRuntimeExecAuth(current)) return ok(current);
868
+ const latest = await client.get(current.id);
869
+ if (latest) current = latest;
870
+ if (hasFreshRuntimeExecAuth(current)) return ok(current);
871
+ } catch (err) {
872
+ lastError = err;
873
+ }
874
+ await new Promise((resolve) => setTimeout(resolve, RUNTIME_CONNECTION_POLL_MS));
875
+ }
876
+ const detail = sandboxRuntimeUrl(current) ? "runtime exec credentials are missing or expired after refresh" : "runtime connection is missing after refresh";
877
+ return fail(new SandboxRuntimeAuthRefreshError(stage, name, detail, lastError));
878
+ }
879
+ async function writeDeferredFilesWithRuntimeAuthRefresh(client, box, files, stage, name) {
880
+ let writeBox = box;
881
+ if (!hasFreshRuntimeExecAuth(writeBox)) {
882
+ const refreshed2 = await bestEffortRefreshRuntimeExecAuth(client, writeBox, stage, name);
883
+ if (!refreshed2.succeeded) return fail(refreshed2.error);
884
+ writeBox = refreshed2.value;
885
+ }
886
+ const first = await writeProfileFilesToBox(writeBox, files);
887
+ if (first.succeeded) return ok(writeBox);
888
+ if (!isRuntimeExecAuthError(first.error)) return fail(first.error);
889
+ const refreshed = await refreshRuntimeExecAuth(client, writeBox, stage, name);
890
+ if (!refreshed.succeeded) return fail(refreshed.error);
891
+ const second = await writeProfileFilesToBox(refreshed.value, files);
892
+ if (second.succeeded) return ok(refreshed.value);
893
+ if (!isRuntimeExecAuthError(second.error)) return fail(second.error);
894
+ return fail(
895
+ new SandboxRuntimeAuthRefreshError(
896
+ stage,
897
+ name,
898
+ "runtime exec remained unauthorized after auth refresh",
899
+ second.error
900
+ )
901
+ );
902
+ }
750
903
  async function isReusableBox(box, harness, probe) {
751
904
  if (sandboxEdgeFailed(box)) return false;
752
905
  if (!sandboxRuntimeUrl(box)) return false;
@@ -784,17 +937,26 @@ async function ensureWorkspaceSandbox(shell, options) {
784
937
  } else if (found.metadata?.harness === harness) {
785
938
  const ready = await refreshRuntimeConnection(client, found);
786
939
  if (await isReusableBox(ready, harness, shell.livenessProbe)) {
787
- const written = await materializeDeferredFilesForExistingBox(shell, ready, workspaceId, userId);
940
+ const written = await materializeDeferredFilesForExistingBox(
941
+ shell,
942
+ client,
943
+ ready,
944
+ "reused",
945
+ name,
946
+ workspaceId,
947
+ userId
948
+ );
788
949
  if (!written.succeeded) {
789
- throw new Error(`deferred file write failed on reused box ${name}`, { cause: written.error });
950
+ throw deferredProfileWriteFailed("reused", name, written.error);
790
951
  }
952
+ const reusedBox = written.value;
791
953
  if (shell.bootstrap) {
792
- const boot = await shell.bootstrap(ready, scope);
954
+ const boot = await shell.bootstrap(reusedBox, scope);
793
955
  if (!boot.succeeded) {
794
956
  throw new Error(`bootstrap failed on reused box ${name}`, { cause: boot.error });
795
957
  }
796
958
  }
797
- return ready;
959
+ return reusedBox;
798
960
  }
799
961
  const dropped = await deleteBox(ready);
800
962
  if (!dropped.succeeded) {
@@ -818,17 +980,26 @@ async function ensureWorkspaceSandbox(shell, options) {
818
980
  if (resumed.succeeded && resumed.value) {
819
981
  const box2 = await refreshRuntimeConnection(client, resumed.value);
820
982
  if (await isReusableBox(box2, harness, shell.livenessProbe)) {
821
- const written = await materializeDeferredFilesForExistingBox(shell, box2, workspaceId, userId);
983
+ const written = await materializeDeferredFilesForExistingBox(
984
+ shell,
985
+ client,
986
+ box2,
987
+ "resumed",
988
+ name,
989
+ workspaceId,
990
+ userId
991
+ );
822
992
  if (!written.succeeded) {
823
- throw new Error(`deferred file write failed on resumed box ${name}`, { cause: written.error });
993
+ throw deferredProfileWriteFailed("resumed", name, written.error);
824
994
  }
995
+ const resumedBox = written.value;
825
996
  if (shell.bootstrap) {
826
- const boot = await shell.bootstrap(box2, scope);
997
+ const boot = await shell.bootstrap(resumedBox, scope);
827
998
  if (!boot.succeeded) {
828
999
  throw new Error(`bootstrap failed on resumed box ${name}`, { cause: boot.error });
829
1000
  }
830
1001
  }
831
- return box2;
1002
+ return resumedBox;
832
1003
  }
833
1004
  const dropped = await deleteBox(box2);
834
1005
  if (!dropped.succeeded) {
@@ -892,7 +1063,7 @@ async function ensureWorkspaceSandbox(shell, options) {
892
1063
  if (deferredFiles.length > 0) {
893
1064
  const written = await writeProfileFilesToBox(box, deferredFiles);
894
1065
  if (!written.succeeded) {
895
- throw new Error(`deferred file write failed on new box ${name}`, { cause: written.error });
1066
+ throw deferredProfileWriteFailed("new", name, written.error);
896
1067
  }
897
1068
  }
898
1069
  if (shell.bootstrap) {
@@ -1190,6 +1361,7 @@ export {
1190
1361
  buildSandboxToolPathSetupScript,
1191
1362
  runSandboxToolPathSetup,
1192
1363
  splitDeferredProfileFiles,
1364
+ SandboxRuntimeAuthRefreshError,
1193
1365
  writeProfileFilesToBox,
1194
1366
  ensureWorkspaceSandbox,
1195
1367
  resolveModel,
@@ -1211,4 +1383,4 @@ export {
1211
1383
  isTerminalPromptEvent,
1212
1384
  detectInteractiveQuestion
1213
1385
  };
1214
- //# sourceMappingURL=chunk-CDYYR3Z7.js.map
1386
+ //# sourceMappingURL=chunk-DBIG2PHS.js.map