@tangle-network/agent-app 0.60.218 → 0.60.219

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.
@@ -40,7 +40,7 @@ import {
40
40
  flattenHistory,
41
41
  readSandboxBinaryBytes,
42
42
  statSandboxFileSize
43
- } from "../chunk-4QUOPWIG.js";
43
+ } from "../chunk-RDLOC7BJ.js";
44
44
  import "../chunk-4LBCTFBP.js";
45
45
  import {
46
46
  ModelFailoverExhaustedError,
@@ -165,6 +165,57 @@ async function* traceSandboxPromptEvents(events, options) {
165
165
  }
166
166
  }
167
167
 
168
+ // src/sandbox/reuse-state.ts
169
+ var CACHE_NAME = "agent-app-sandbox-reuse";
170
+ var KEY_ORIGIN = "https://sandbox-reuse.agent-app.invalid/";
171
+ var ENTRY_MAX_AGE_SECONDS = 15 * 60;
172
+ function isRecord(value) {
173
+ return typeof value === "object" && value !== null && !Array.isArray(value);
174
+ }
175
+ function stamped(value, field) {
176
+ if (!isRecord(value) || typeof value.at !== "number" || typeof value[field] !== "string") return void 0;
177
+ return { text: value[field], at: value.at };
178
+ }
179
+ function parseState(value) {
180
+ if (!isRecord(value) || typeof value.boxId !== "string" || !isRecord(value.checks)) return null;
181
+ const checks = value.checks;
182
+ if (typeof checks.identity !== "string") return null;
183
+ const parsed = { identity: checks.identity };
184
+ if (typeof checks.alive === "number") parsed.alive = checks.alive;
185
+ const egress = stamped(checks.egress, "key");
186
+ if (egress) parsed.egress = { key: egress.text, at: egress.at };
187
+ const env = stamped(checks.env, "digest");
188
+ if (env) parsed.env = { digest: env.text, at: env.at };
189
+ const corpus = stamped(checks.corpus, "hash");
190
+ if (corpus) parsed.corpus = { hash: corpus.text, at: corpus.at };
191
+ return { boxId: value.boxId, checks: parsed };
192
+ }
193
+ function createCacheReuseStore(open) {
194
+ let cache;
195
+ const handle = async () => cache ??= await open();
196
+ const request = (name) => new Request(`${KEY_ORIGIN}${encodeURIComponent(name)}`);
197
+ return {
198
+ async load(name) {
199
+ const hit = await (await handle()).match(request(name));
200
+ if (!hit) return null;
201
+ return parseState(await hit.json());
202
+ },
203
+ async save(name, state) {
204
+ await (await handle()).put(request(name), new Response(JSON.stringify(state), {
205
+ headers: { "content-type": "application/json", "cache-control": `public, max-age=${ENTRY_MAX_AGE_SECONDS}` }
206
+ }));
207
+ },
208
+ async drop(name) {
209
+ await (await handle()).delete(request(name));
210
+ }
211
+ };
212
+ }
213
+ function workersReuseStore() {
214
+ const storage = globalThis.caches;
215
+ if (typeof storage?.open !== "function") return null;
216
+ return createCacheReuseStore(() => storage.open(CACHE_NAME));
217
+ }
218
+
168
219
  // src/sandbox/model.ts
169
220
  function backendModelFields(model) {
170
221
  return {
@@ -1547,6 +1598,10 @@ var REUSE_CHECKS_MAX = 1e3;
1547
1598
  var reuseChecks = /* @__PURE__ */ new Map();
1548
1599
  var runningBoxIds = /* @__PURE__ */ new Map();
1549
1600
  var trustedBoxes = /* @__PURE__ */ new WeakSet();
1601
+ var reuseNames = /* @__PURE__ */ new Map();
1602
+ var savedShared = /* @__PURE__ */ new Map();
1603
+ var defaultReuseStore;
1604
+ var activeReuseStore = null;
1550
1605
  function boxIdentity(box) {
1551
1606
  const startedAt = box.startedAt instanceof Date ? box.startedAt.getTime() : Number.NaN;
1552
1607
  return `${Number.isFinite(startedAt) ? startedAt : ""}|${box.filesystemIncarnationId ?? ""}`;
@@ -1573,6 +1628,10 @@ function recordCheck(box, patch) {
1573
1628
  }
1574
1629
  function forgetBox(boxId) {
1575
1630
  reuseChecks.delete(boxId);
1631
+ const name = reuseNames.get(boxId);
1632
+ if (name === void 0 || !activeReuseStore) return;
1633
+ savedShared.delete(name);
1634
+ void Promise.resolve(activeReuseStore.drop(name)).catch(() => void 0);
1576
1635
  }
1577
1636
  function isRecent(at, ttlMs, now = Date.now()) {
1578
1637
  return at !== void 0 && Number.isFinite(ttlMs) && ttlMs > 0 && now - at < ttlMs;
@@ -1600,6 +1659,10 @@ function resetClientCache() {
1600
1659
  _cached = null;
1601
1660
  reuseChecks.clear();
1602
1661
  runningBoxIds.clear();
1662
+ reuseNames.clear();
1663
+ savedShared.clear();
1664
+ defaultReuseStore = void 0;
1665
+ activeReuseStore = null;
1603
1666
  }
1604
1667
  function buildAppToolMcpServers(options) {
1605
1668
  const entries = {};
@@ -2075,11 +2138,17 @@ async function listRunning(client, name) {
2075
2138
  function rememberRunningBox(name, box) {
2076
2139
  runningBoxIds.delete(name);
2077
2140
  runningBoxIds.set(name, box.id);
2141
+ reuseNames.set(box.id, name);
2078
2142
  while (runningBoxIds.size > REUSE_CHECKS_MAX) {
2079
2143
  const oldest = runningBoxIds.keys().next().value;
2080
2144
  if (oldest === void 0) break;
2081
2145
  runningBoxIds.delete(oldest);
2082
2146
  }
2147
+ while (reuseNames.size > REUSE_CHECKS_MAX) {
2148
+ const oldest = reuseNames.keys().next().value;
2149
+ if (oldest === void 0) break;
2150
+ reuseNames.delete(oldest);
2151
+ }
2083
2152
  return box;
2084
2153
  }
2085
2154
  async function getRunningBox(client, id, name) {
@@ -2612,16 +2681,57 @@ async function ensureWorkspaceSandbox(shell, options) {
2612
2681
  return box;
2613
2682
  }
2614
2683
  async function reuseWorkspaceSandbox(shell, options) {
2615
- if (options.forceNew || !hasRecentProbe(shell, options)) return void 0;
2684
+ if (options.forceNew) return void 0;
2685
+ await hydrateReuseState(shell, options);
2686
+ if (!hasRecentProbe(shell, options)) return void 0;
2616
2687
  return claimWorkspaceSandbox(shell, options, true);
2617
2688
  }
2689
+ function boxNameFor(shell, options) {
2690
+ const scope = { workspaceId: options.workspaceId, ...options.userId ? { userId: options.userId } : {} };
2691
+ return shell.boxKey ? shell.boxKey(scope) : shell.name(options.workspaceId);
2692
+ }
2618
2693
  function hasRecentProbe(shell, options) {
2619
2694
  const probe = shell.livenessProbe;
2620
2695
  if (!probe || options.forceNew) return false;
2621
- const scope = { workspaceId: options.workspaceId, ...options.userId ? { userId: options.userId } : {} };
2622
- const id = runningBoxIds.get(shell.boxKey ? shell.boxKey(scope) : shell.name(options.workspaceId));
2696
+ const id = runningBoxIds.get(boxNameFor(shell, options));
2623
2697
  return isRecent(id === void 0 ? void 0 : reuseChecks.get(id)?.alive, probe.cacheTtlMs ?? TRUSTED_REUSE_TTL_MS);
2624
2698
  }
2699
+ function reuseStoreOf(shell) {
2700
+ if (shell.reuseState !== void 0) return shell.reuseState;
2701
+ if (defaultReuseStore === void 0) defaultReuseStore = workersReuseStore();
2702
+ return defaultReuseStore;
2703
+ }
2704
+ async function hydrateReuseState(shell, options) {
2705
+ if (options.forceNew) return;
2706
+ const store = reuseStoreOf(shell);
2707
+ activeReuseStore = store;
2708
+ if (!store) return;
2709
+ const name = boxNameFor(shell, options);
2710
+ if (runningBoxIds.has(name)) return;
2711
+ try {
2712
+ const shared = await store.load(name);
2713
+ if (!shared) return;
2714
+ runningBoxIds.set(name, shared.boxId);
2715
+ reuseNames.set(shared.boxId, name);
2716
+ reuseChecks.set(shared.boxId, shared.checks);
2717
+ savedShared.set(name, JSON.stringify(shared));
2718
+ } catch {
2719
+ }
2720
+ }
2721
+ async function persistReuseState(shell, box) {
2722
+ const store = reuseStoreOf(shell);
2723
+ const name = reuseNames.get(box.id);
2724
+ const checks = reuseChecks.get(box.id);
2725
+ if (!store || name === void 0 || !checks) return;
2726
+ const state = { boxId: box.id, checks };
2727
+ const serialized = JSON.stringify(state);
2728
+ if (savedShared.get(name) === serialized) return;
2729
+ try {
2730
+ await store.save(name, state);
2731
+ savedShared.set(name, serialized);
2732
+ } catch {
2733
+ }
2734
+ }
2625
2735
  function spendGate(options) {
2626
2736
  let gated;
2627
2737
  return () => {
@@ -2637,6 +2747,7 @@ function spendGate(options) {
2637
2747
  async function claimWorkspaceSandbox(shell, options, reuseOnly) {
2638
2748
  const { trace } = options;
2639
2749
  const gate = spendGate(options);
2750
+ await hydrateReuseState(shell, options);
2640
2751
  if (!hasRecentProbe(shell, options)) await gate();
2641
2752
  const claim = trace?.start("sandbox.claim", "sandbox", { "workspace.harness": options.harness }, options.traceParent);
2642
2753
  const traced = trace && claim ? {
@@ -2654,6 +2765,7 @@ async function claimWorkspaceSandbox(shell, options, reuseOnly) {
2654
2765
  }
2655
2766
  claim?.end({ "sandbox.id": box.id, ...trustedBoxes.has(box) ? { "sandbox.probe_trusted": true } : {} });
2656
2767
  if (!trustedBoxes.has(box)) await observeProvisionedBox(shell, options, box);
2768
+ await persistReuseState(shell, box);
2657
2769
  return box;
2658
2770
  } catch (error) {
2659
2771
  claim?.end(void 0, error);
@@ -3579,6 +3691,7 @@ function firstQuestionText(value) {
3579
3691
 
3580
3692
  export {
3581
3693
  traceSandboxPromptEvents,
3694
+ createCacheReuseStore,
3582
3695
  backendModelFields,
3583
3696
  resolveModelSelection,
3584
3697
  resolveModel,
@@ -3680,4 +3793,4 @@ export {
3680
3793
  isTerminalPromptEvent,
3681
3794
  detectInteractiveQuestion
3682
3795
  };
3683
- //# sourceMappingURL=chunk-4QUOPWIG.js.map
3796
+ //# sourceMappingURL=chunk-RDLOC7BJ.js.map