@electric-ax/agents-server-conformance-tests 0.1.9 → 0.1.11

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/index.cjs CHANGED
@@ -328,11 +328,8 @@ var ElectricAgentsScenario = class {
328
328
  });
329
329
  return this;
330
330
  }
331
- readStream(stream) {
332
- this.steps.push({
333
- kind: `readStream`,
334
- stream
335
- });
331
+ readStream() {
332
+ this.steps.push({ kind: `readStream` });
336
333
  return this;
337
334
  }
338
335
  list(filter) {
@@ -671,7 +668,6 @@ async function executeStep(ctx, step) {
671
668
  (0, vitest.expect)(entity, `Webhook payload must contain entity context`).toBeDefined();
672
669
  (0, vitest.expect)(entity.url).toBeTruthy();
673
670
  (0, vitest.expect)(entity.streams.main).toBeTruthy();
674
- (0, vitest.expect)(entity.streams.error).toBeTruthy();
675
671
  if (step.checks?.url) (0, vitest.expect)(entity.url).toBe(step.checks.url);
676
672
  if (step.checks?.type) (0, vitest.expect)(entity.type).toBe(step.checks.type);
677
673
  if (step.checks?.status) (0, vitest.expect)(entity.status).toBe(step.checks.status);
@@ -712,7 +708,7 @@ async function executeStep(ctx, step) {
712
708
  }
713
709
  case `readStream`: {
714
710
  if (!ctx.currentEntityStreams) throw new Error(`No current entity streams`);
715
- const streamPath = step.stream === `error` ? ctx.currentEntityStreams.error : ctx.currentEntityStreams.main;
711
+ const streamPath = ctx.currentEntityStreams.main;
716
712
  const res = await fetch(appendPathToUrl(ctx.baseUrl, `${streamPath}?offset=0000000000000000_0000000000000000`));
717
713
  if (res.status === 200) {
718
714
  const text = await res.text();
@@ -1207,14 +1203,11 @@ function checkEntityContextOnWebhook(history) {
1207
1203
  }
1208
1204
  }
1209
1205
  /**
1210
- * Spec S5 — Structural: stream paths must match {entity.url}/main and {entity.url}/error.
1206
+ * Spec S5 — Structural: stream path must match {entity.url}/main.
1211
1207
  * Soundness: Sound | Completeness: Complete (within trace)
1212
1208
  */
1213
1209
  function checkStreamPathsMatchEntityUrl(history) {
1214
- for (const event of history) if (event.type === `entity_spawned`) {
1215
- (0, vitest.expect)(event.streams.main).toBe(`${event.entityUrl}/main`);
1216
- (0, vitest.expect)(event.streams.error).toBe(`${event.entityUrl}/error`);
1217
- }
1210
+ for (const event of history) if (event.type === `entity_spawned`) (0, vitest.expect)(event.streams.main).toBe(`${event.entityUrl}/main`);
1218
1211
  }
1219
1212
  /**
1220
1213
  * Spec S4 — Safety: entity status transitions must be valid.
@@ -1276,8 +1269,8 @@ function checkAdditiveSchemaEvolution(history) {
1276
1269
  }
1277
1270
  /**
1278
1271
  * Spec R4 — Registry stream consistency: every entity_spawned must refer to an
1279
- * entity that has not already been killed, and stream URLs must follow the
1280
- * convention {entity.url}/main and {entity.url}/error.
1272
+ * entity that has not already been killed, and stream URL must follow the
1273
+ * convention {entity.url}/main.
1281
1274
  * Soundness: Sound | Completeness: Complete (within trace)
1282
1275
  */
1283
1276
  function checkRegistryStreamConsistency(history) {
@@ -1287,7 +1280,6 @@ function checkRegistryStreamConsistency(history) {
1287
1280
  if (event.type === `entity_spawned`) {
1288
1281
  (0, vitest.expect)(!killedEntities.has(event.entityUrl), `Registry consistency: entity_spawned for ${event.entityUrl} but entity was already killed`).toBe(true);
1289
1282
  (0, vitest.expect)(event.streams.main, `Registry consistency: entity ${event.entityUrl} streams.main must be ${event.entityUrl}/main`).toBe(`${event.entityUrl}/main`);
1290
- (0, vitest.expect)(event.streams.error, `Registry consistency: entity ${event.entityUrl} streams.error must be ${event.entityUrl}/error`).toBe(`${event.entityUrl}/error`);
1291
1283
  }
1292
1284
  }
1293
1285
  }
@@ -1832,8 +1824,6 @@ function runElectricAgentsConformanceTests(config) {
1832
1824
  }).spawn(`spawn-test-agent`, `entity-1`).expectStatus(`running`).custom(async (ctx) => {
1833
1825
  const mainRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.main), { method: `HEAD` });
1834
1826
  (0, vitest.expect)(mainRes.status).toBe(200);
1835
- const errorRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.error), { method: `HEAD` });
1836
- (0, vitest.expect)(errorRes.status).toBe(200);
1837
1827
  }).run());
1838
1828
  (0, vitest.test)(`spawn at unregistered type returns UNKNOWN_ENTITY_TYPE`, () => electricAgents(config.baseUrl).custom(async (ctx) => {
1839
1829
  const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/unregistered/entity-1`)), {
@@ -2904,25 +2894,51 @@ function runElectricAgentsConformanceTests(config) {
2904
2894
  const block = result.content[0];
2905
2895
  return block?.type === `text` && block.text ? block.text : ``;
2906
2896
  }
2897
+ async function makeSandbox(workingDirectory) {
2898
+ const { unrestrictedSandbox } = await import(`../../agents-runtime/src/sandbox/unrestricted`);
2899
+ return unrestrictedSandbox({ workingDirectory });
2900
+ }
2907
2901
  (0, vitest.test)(`bash tool captures stdout and stderr`, async () => {
2908
2902
  const { createBashTool } = await import(`../../agents-runtime/src/tools`);
2909
- const tool = createBashTool(`/tmp`);
2910
- const result = await tool.execute(`test-tc`, { command: `echo "hello" && echo "error" >&2` });
2911
- (0, vitest.expect)(firstText(result)).toContain(`hello`);
2912
- (0, vitest.expect)(firstText(result)).toContain(`error`);
2913
- (0, vitest.expect)(result.details.exitCode).toBe(0);
2903
+ const sandbox = await makeSandbox(`/tmp`);
2904
+ try {
2905
+ const tool = createBashTool(sandbox);
2906
+ const result = await tool.execute(`test-tc`, { command: `echo "hello" && echo "error" >&2` });
2907
+ (0, vitest.expect)(firstText(result)).toContain(`hello`);
2908
+ (0, vitest.expect)(firstText(result)).toContain(`error`);
2909
+ (0, vitest.expect)(result.details.exitCode).toBe(0);
2910
+ } finally {
2911
+ await sandbox.dispose();
2912
+ }
2914
2913
  });
2915
2914
  (0, vitest.test)(`bash tool enforces timeout`, async () => {
2916
2915
  const { createBashTool } = await import(`../../agents-runtime/src/tools`);
2917
- const tool = createBashTool(`/tmp`);
2918
- const result = await tool.execute(`test-tc`, { command: `sleep 60` });
2919
- (0, vitest.expect)(result.details.timedOut).toBe(true);
2916
+ const sandbox = await makeSandbox(`/tmp`);
2917
+ try {
2918
+ const tool = createBashTool(sandbox);
2919
+ const result = await tool.execute(`test-tc`, { command: `sleep 60` });
2920
+ (0, vitest.expect)(result.details.timedOut).toBe(true);
2921
+ } finally {
2922
+ await sandbox.dispose();
2923
+ }
2920
2924
  }, 35e3);
2921
2925
  (0, vitest.test)(`read_file rejects paths outside working directory`, async () => {
2922
2926
  const { createReadFileTool } = await import(`../../agents-runtime/src/tools`);
2923
- const tool = createReadFileTool(`/tmp/test-workdir`);
2924
- const result = await tool.execute(`test-tc`, { path: `../../etc/passwd` });
2925
- (0, vitest.expect)(firstText(result)).toContain(`outside the working directory`);
2927
+ const fs = await import(`node:fs/promises`);
2928
+ const dir = `/tmp/test-workdir-${Date.now()}`;
2929
+ await fs.mkdir(dir, { recursive: true });
2930
+ const sandbox = await makeSandbox(dir);
2931
+ try {
2932
+ const tool = createReadFileTool(sandbox);
2933
+ const result = await tool.execute(`test-tc`, { path: `../../etc/passwd` });
2934
+ (0, vitest.expect)(firstText(result)).toContain(`outside the working directory`);
2935
+ } finally {
2936
+ await sandbox.dispose();
2937
+ await fs.rm(dir, {
2938
+ recursive: true,
2939
+ force: true
2940
+ });
2941
+ }
2926
2942
  });
2927
2943
  (0, vitest.test)(`read_file rejects binary files`, async () => {
2928
2944
  const { createReadFileTool } = await import(`../../agents-runtime/src/tools`);
@@ -2937,10 +2953,15 @@ function runElectricAgentsConformanceTests(config) {
2937
2953
  2,
2938
2954
  255
2939
2955
  ]));
2940
- const tool = createReadFileTool(dir);
2941
- const result = await tool.execute(`test-tc`, { path: `test.bin` });
2942
- (0, vitest.expect)(firstText(result)).toContain(`binary file`);
2943
- await fs.rm(dir, { recursive: true });
2956
+ const sandbox = await makeSandbox(dir);
2957
+ try {
2958
+ const tool = createReadFileTool(sandbox);
2959
+ const result = await tool.execute(`test-tc`, { path: `test.bin` });
2960
+ (0, vitest.expect)(firstText(result)).toContain(`binary file`);
2961
+ } finally {
2962
+ await sandbox.dispose();
2963
+ await fs.rm(dir, { recursive: true });
2964
+ }
2944
2965
  });
2945
2966
  (0, vitest.test)(`read_file rejects oversized files`, async () => {
2946
2967
  const { createReadFileTool } = await import(`../../agents-runtime/src/tools`);
@@ -2950,10 +2971,15 @@ function runElectricAgentsConformanceTests(config) {
2950
2971
  await fs.mkdir(dir, { recursive: true });
2951
2972
  const bigPath = path.join(dir, `big.txt`);
2952
2973
  await fs.writeFile(bigPath, `x`.repeat(600 * 1024));
2953
- const tool = createReadFileTool(dir);
2954
- const result = await tool.execute(`test-tc`, { path: `big.txt` });
2955
- (0, vitest.expect)(firstText(result)).toContain(`too large`);
2956
- await fs.rm(dir, { recursive: true });
2974
+ const sandbox = await makeSandbox(dir);
2975
+ try {
2976
+ const tool = createReadFileTool(sandbox);
2977
+ const result = await tool.execute(`test-tc`, { path: `big.txt` });
2978
+ (0, vitest.expect)(firstText(result)).toContain(`too large`);
2979
+ } finally {
2980
+ await sandbox.dispose();
2981
+ await fs.rm(dir, { recursive: true });
2982
+ }
2957
2983
  });
2958
2984
  (0, vitest.test)(`web_search tool has correct interface`, async () => {
2959
2985
  const { braveSearchTool } = await import(`../../agents-runtime/src/tools`);
@@ -2961,9 +2987,15 @@ function runElectricAgentsConformanceTests(config) {
2961
2987
  (0, vitest.expect)(typeof braveSearchTool.execute).toBe(`function`);
2962
2988
  });
2963
2989
  (0, vitest.test)(`fetch_url tool has correct interface`, async () => {
2964
- const { fetchUrlTool } = await import(`../../agents-runtime/src/tools`);
2965
- (0, vitest.expect)(fetchUrlTool.name).toBe(`fetch_url`);
2966
- (0, vitest.expect)(typeof fetchUrlTool.execute).toBe(`function`);
2990
+ const { createFetchUrlTool } = await import(`../../agents-runtime/src/tools`);
2991
+ const sandbox = await makeSandbox(`/tmp`);
2992
+ try {
2993
+ const tool = createFetchUrlTool(sandbox);
2994
+ (0, vitest.expect)(tool.name).toBe(`fetch_url`);
2995
+ (0, vitest.expect)(typeof tool.execute).toBe(`function`);
2996
+ } finally {
2997
+ await sandbox.dispose();
2998
+ }
2967
2999
  });
2968
3000
  });
2969
3001
  (0, vitest.describe)(`Electric Agents Send — State Protocol Format`, () => {
package/dist/index.d.cts CHANGED
@@ -64,7 +64,6 @@ type HistoryEvent = {
64
64
  status: string;
65
65
  streams: {
66
66
  main: string;
67
- error: string;
68
67
  };
69
68
  parent?: string;
70
69
  } | {
@@ -184,7 +183,6 @@ interface WebhookEntityContext {
184
183
  url: string;
185
184
  streams: {
186
185
  main: string;
187
- error: string;
188
186
  };
189
187
  tags?: Record<string, string>;
190
188
  }
@@ -273,7 +271,6 @@ interface RunContext {
273
271
  currentEntityUrl: string | null;
274
272
  currentEntityStreams: {
275
273
  main: string;
276
- error: string;
277
274
  } | null;
278
275
  currentWriteToken: string | null;
279
276
  notification: WebhookNotification | null;
@@ -320,7 +317,7 @@ declare class ElectricAgentsScenario {
320
317
  expectEntityContext(checks?: EntityContextChecks): this;
321
318
  expectStatus(status: string): this;
322
319
  expectStreamContains(messageType: string): this;
323
- readStream(stream?: `main` | `error`): this;
320
+ readStream(): this;
324
321
  list(filter?: {
325
322
  type?: string;
326
323
  status?: string;
package/dist/index.d.ts CHANGED
@@ -64,7 +64,6 @@ type HistoryEvent = {
64
64
  status: string;
65
65
  streams: {
66
66
  main: string;
67
- error: string;
68
67
  };
69
68
  parent?: string;
70
69
  } | {
@@ -184,7 +183,6 @@ interface WebhookEntityContext {
184
183
  url: string;
185
184
  streams: {
186
185
  main: string;
187
- error: string;
188
186
  };
189
187
  tags?: Record<string, string>;
190
188
  }
@@ -273,7 +271,6 @@ interface RunContext {
273
271
  currentEntityUrl: string | null;
274
272
  currentEntityStreams: {
275
273
  main: string;
276
- error: string;
277
274
  } | null;
278
275
  currentWriteToken: string | null;
279
276
  notification: WebhookNotification | null;
@@ -320,7 +317,7 @@ declare class ElectricAgentsScenario {
320
317
  expectEntityContext(checks?: EntityContextChecks): this;
321
318
  expectStatus(status: string): this;
322
319
  expectStreamContains(messageType: string): this;
323
- readStream(stream?: `main` | `error`): this;
320
+ readStream(): this;
324
321
  list(filter?: {
325
322
  type?: string;
326
323
  status?: string;
package/dist/index.js CHANGED
@@ -304,11 +304,8 @@ var ElectricAgentsScenario = class {
304
304
  });
305
305
  return this;
306
306
  }
307
- readStream(stream) {
308
- this.steps.push({
309
- kind: `readStream`,
310
- stream
311
- });
307
+ readStream() {
308
+ this.steps.push({ kind: `readStream` });
312
309
  return this;
313
310
  }
314
311
  list(filter) {
@@ -647,7 +644,6 @@ async function executeStep(ctx, step) {
647
644
  expect(entity, `Webhook payload must contain entity context`).toBeDefined();
648
645
  expect(entity.url).toBeTruthy();
649
646
  expect(entity.streams.main).toBeTruthy();
650
- expect(entity.streams.error).toBeTruthy();
651
647
  if (step.checks?.url) expect(entity.url).toBe(step.checks.url);
652
648
  if (step.checks?.type) expect(entity.type).toBe(step.checks.type);
653
649
  if (step.checks?.status) expect(entity.status).toBe(step.checks.status);
@@ -688,7 +684,7 @@ async function executeStep(ctx, step) {
688
684
  }
689
685
  case `readStream`: {
690
686
  if (!ctx.currentEntityStreams) throw new Error(`No current entity streams`);
691
- const streamPath = step.stream === `error` ? ctx.currentEntityStreams.error : ctx.currentEntityStreams.main;
687
+ const streamPath = ctx.currentEntityStreams.main;
692
688
  const res = await fetch(appendPathToUrl(ctx.baseUrl, `${streamPath}?offset=0000000000000000_0000000000000000`));
693
689
  if (res.status === 200) {
694
690
  const text = await res.text();
@@ -1183,14 +1179,11 @@ function checkEntityContextOnWebhook(history) {
1183
1179
  }
1184
1180
  }
1185
1181
  /**
1186
- * Spec S5 — Structural: stream paths must match {entity.url}/main and {entity.url}/error.
1182
+ * Spec S5 — Structural: stream path must match {entity.url}/main.
1187
1183
  * Soundness: Sound | Completeness: Complete (within trace)
1188
1184
  */
1189
1185
  function checkStreamPathsMatchEntityUrl(history) {
1190
- for (const event of history) if (event.type === `entity_spawned`) {
1191
- expect(event.streams.main).toBe(`${event.entityUrl}/main`);
1192
- expect(event.streams.error).toBe(`${event.entityUrl}/error`);
1193
- }
1186
+ for (const event of history) if (event.type === `entity_spawned`) expect(event.streams.main).toBe(`${event.entityUrl}/main`);
1194
1187
  }
1195
1188
  /**
1196
1189
  * Spec S4 — Safety: entity status transitions must be valid.
@@ -1252,8 +1245,8 @@ function checkAdditiveSchemaEvolution(history) {
1252
1245
  }
1253
1246
  /**
1254
1247
  * Spec R4 — Registry stream consistency: every entity_spawned must refer to an
1255
- * entity that has not already been killed, and stream URLs must follow the
1256
- * convention {entity.url}/main and {entity.url}/error.
1248
+ * entity that has not already been killed, and stream URL must follow the
1249
+ * convention {entity.url}/main.
1257
1250
  * Soundness: Sound | Completeness: Complete (within trace)
1258
1251
  */
1259
1252
  function checkRegistryStreamConsistency(history) {
@@ -1263,7 +1256,6 @@ function checkRegistryStreamConsistency(history) {
1263
1256
  if (event.type === `entity_spawned`) {
1264
1257
  expect(!killedEntities.has(event.entityUrl), `Registry consistency: entity_spawned for ${event.entityUrl} but entity was already killed`).toBe(true);
1265
1258
  expect(event.streams.main, `Registry consistency: entity ${event.entityUrl} streams.main must be ${event.entityUrl}/main`).toBe(`${event.entityUrl}/main`);
1266
- expect(event.streams.error, `Registry consistency: entity ${event.entityUrl} streams.error must be ${event.entityUrl}/error`).toBe(`${event.entityUrl}/error`);
1267
1259
  }
1268
1260
  }
1269
1261
  }
@@ -1808,8 +1800,6 @@ function runElectricAgentsConformanceTests(config) {
1808
1800
  }).spawn(`spawn-test-agent`, `entity-1`).expectStatus(`running`).custom(async (ctx) => {
1809
1801
  const mainRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.main), { method: `HEAD` });
1810
1802
  expect(mainRes.status).toBe(200);
1811
- const errorRes = await fetch(appendPathToUrl(ctx.baseUrl, ctx.currentEntityStreams.error), { method: `HEAD` });
1812
- expect(errorRes.status).toBe(200);
1813
1803
  }).run());
1814
1804
  test(`spawn at unregistered type returns UNKNOWN_ENTITY_TYPE`, () => electricAgents(config.baseUrl).custom(async (ctx) => {
1815
1805
  const res = await fetch(appendPathToUrl(ctx.baseUrl, routeControlPlanePath(`/unregistered/entity-1`)), {
@@ -2880,25 +2870,51 @@ function runElectricAgentsConformanceTests(config) {
2880
2870
  const block = result.content[0];
2881
2871
  return block?.type === `text` && block.text ? block.text : ``;
2882
2872
  }
2873
+ async function makeSandbox(workingDirectory) {
2874
+ const { unrestrictedSandbox } = await import(`../../agents-runtime/src/sandbox/unrestricted`);
2875
+ return unrestrictedSandbox({ workingDirectory });
2876
+ }
2883
2877
  test(`bash tool captures stdout and stderr`, async () => {
2884
2878
  const { createBashTool } = await import(`../../agents-runtime/src/tools`);
2885
- const tool = createBashTool(`/tmp`);
2886
- const result = await tool.execute(`test-tc`, { command: `echo "hello" && echo "error" >&2` });
2887
- expect(firstText(result)).toContain(`hello`);
2888
- expect(firstText(result)).toContain(`error`);
2889
- expect(result.details.exitCode).toBe(0);
2879
+ const sandbox = await makeSandbox(`/tmp`);
2880
+ try {
2881
+ const tool = createBashTool(sandbox);
2882
+ const result = await tool.execute(`test-tc`, { command: `echo "hello" && echo "error" >&2` });
2883
+ expect(firstText(result)).toContain(`hello`);
2884
+ expect(firstText(result)).toContain(`error`);
2885
+ expect(result.details.exitCode).toBe(0);
2886
+ } finally {
2887
+ await sandbox.dispose();
2888
+ }
2890
2889
  });
2891
2890
  test(`bash tool enforces timeout`, async () => {
2892
2891
  const { createBashTool } = await import(`../../agents-runtime/src/tools`);
2893
- const tool = createBashTool(`/tmp`);
2894
- const result = await tool.execute(`test-tc`, { command: `sleep 60` });
2895
- expect(result.details.timedOut).toBe(true);
2892
+ const sandbox = await makeSandbox(`/tmp`);
2893
+ try {
2894
+ const tool = createBashTool(sandbox);
2895
+ const result = await tool.execute(`test-tc`, { command: `sleep 60` });
2896
+ expect(result.details.timedOut).toBe(true);
2897
+ } finally {
2898
+ await sandbox.dispose();
2899
+ }
2896
2900
  }, 35e3);
2897
2901
  test(`read_file rejects paths outside working directory`, async () => {
2898
2902
  const { createReadFileTool } = await import(`../../agents-runtime/src/tools`);
2899
- const tool = createReadFileTool(`/tmp/test-workdir`);
2900
- const result = await tool.execute(`test-tc`, { path: `../../etc/passwd` });
2901
- expect(firstText(result)).toContain(`outside the working directory`);
2903
+ const fs = await import(`node:fs/promises`);
2904
+ const dir = `/tmp/test-workdir-${Date.now()}`;
2905
+ await fs.mkdir(dir, { recursive: true });
2906
+ const sandbox = await makeSandbox(dir);
2907
+ try {
2908
+ const tool = createReadFileTool(sandbox);
2909
+ const result = await tool.execute(`test-tc`, { path: `../../etc/passwd` });
2910
+ expect(firstText(result)).toContain(`outside the working directory`);
2911
+ } finally {
2912
+ await sandbox.dispose();
2913
+ await fs.rm(dir, {
2914
+ recursive: true,
2915
+ force: true
2916
+ });
2917
+ }
2902
2918
  });
2903
2919
  test(`read_file rejects binary files`, async () => {
2904
2920
  const { createReadFileTool } = await import(`../../agents-runtime/src/tools`);
@@ -2913,10 +2929,15 @@ function runElectricAgentsConformanceTests(config) {
2913
2929
  2,
2914
2930
  255
2915
2931
  ]));
2916
- const tool = createReadFileTool(dir);
2917
- const result = await tool.execute(`test-tc`, { path: `test.bin` });
2918
- expect(firstText(result)).toContain(`binary file`);
2919
- await fs.rm(dir, { recursive: true });
2932
+ const sandbox = await makeSandbox(dir);
2933
+ try {
2934
+ const tool = createReadFileTool(sandbox);
2935
+ const result = await tool.execute(`test-tc`, { path: `test.bin` });
2936
+ expect(firstText(result)).toContain(`binary file`);
2937
+ } finally {
2938
+ await sandbox.dispose();
2939
+ await fs.rm(dir, { recursive: true });
2940
+ }
2920
2941
  });
2921
2942
  test(`read_file rejects oversized files`, async () => {
2922
2943
  const { createReadFileTool } = await import(`../../agents-runtime/src/tools`);
@@ -2926,10 +2947,15 @@ function runElectricAgentsConformanceTests(config) {
2926
2947
  await fs.mkdir(dir, { recursive: true });
2927
2948
  const bigPath = path.join(dir, `big.txt`);
2928
2949
  await fs.writeFile(bigPath, `x`.repeat(600 * 1024));
2929
- const tool = createReadFileTool(dir);
2930
- const result = await tool.execute(`test-tc`, { path: `big.txt` });
2931
- expect(firstText(result)).toContain(`too large`);
2932
- await fs.rm(dir, { recursive: true });
2950
+ const sandbox = await makeSandbox(dir);
2951
+ try {
2952
+ const tool = createReadFileTool(sandbox);
2953
+ const result = await tool.execute(`test-tc`, { path: `big.txt` });
2954
+ expect(firstText(result)).toContain(`too large`);
2955
+ } finally {
2956
+ await sandbox.dispose();
2957
+ await fs.rm(dir, { recursive: true });
2958
+ }
2933
2959
  });
2934
2960
  test(`web_search tool has correct interface`, async () => {
2935
2961
  const { braveSearchTool } = await import(`../../agents-runtime/src/tools`);
@@ -2937,9 +2963,15 @@ function runElectricAgentsConformanceTests(config) {
2937
2963
  expect(typeof braveSearchTool.execute).toBe(`function`);
2938
2964
  });
2939
2965
  test(`fetch_url tool has correct interface`, async () => {
2940
- const { fetchUrlTool } = await import(`../../agents-runtime/src/tools`);
2941
- expect(fetchUrlTool.name).toBe(`fetch_url`);
2942
- expect(typeof fetchUrlTool.execute).toBe(`function`);
2966
+ const { createFetchUrlTool } = await import(`../../agents-runtime/src/tools`);
2967
+ const sandbox = await makeSandbox(`/tmp`);
2968
+ try {
2969
+ const tool = createFetchUrlTool(sandbox);
2970
+ expect(tool.name).toBe(`fetch_url`);
2971
+ expect(typeof tool.execute).toBe(`function`);
2972
+ } finally {
2973
+ await sandbox.dispose();
2974
+ }
2943
2975
  });
2944
2976
  });
2945
2977
  describe(`Electric Agents Send — State Protocol Format`, () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electric-ax/agents-server-conformance-tests",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Conformance test suite for Electric Agents server implementations",
5
5
  "author": "Durable Stream contributors",
6
6
  "license": "Apache-2.0",
@@ -35,7 +35,7 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@durable-streams/client": "^0.2.6",
38
- "@electric-sql/client": "^1.5.19",
38
+ "@electric-sql/client": "^1.5.20",
39
39
  "fast-check": "^4.6.0",
40
40
  "vitest": "^4.1.0"
41
41
  },
@@ -43,7 +43,7 @@
43
43
  "@mariozechner/pi-agent-core": "^0.70.2",
44
44
  "tsdown": "^0.9.0",
45
45
  "typescript": "^5.0.0",
46
- "@electric-ax/agents-server": "0.4.13"
46
+ "@electric-ax/agents-server": "0.4.16"
47
47
  },
48
48
  "engines": {
49
49
  "node": ">=18.0.0"