@sebastiantuyu/agest 0.3.2 → 0.3.3-next.1

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.
@@ -24,12 +24,13 @@
24
24
  */
25
25
  export function remote(endpoint, options = {}) {
26
26
  const { headers = {}, method = "POST", body: extraBody, buildRequest = defaultBuildRequest, parseResponse, metadata: staticMetadata, } = options;
27
- return async (input) => {
27
+ return async (input, execOptions) => {
28
28
  let res;
29
29
  try {
30
30
  const fetchOptions = {
31
31
  method,
32
32
  headers: { "Content-Type": "application/json", ...headers },
33
+ signal: execOptions?.signal,
33
34
  };
34
35
  if (method !== "GET") {
35
36
  const built = buildRequest(input);
package/dist/cli.js CHANGED
@@ -2,11 +2,35 @@
2
2
  import { spawn } from "child_process";
3
3
  import { main as stats } from "./stats.js";
4
4
  import { main as preview } from "./preview.js";
5
+ import { DEFAULT_PATTERN, discoverTestFiles } from "./discover.js";
5
6
  const command = process.argv[2];
7
+ function parseRunArgs(args) {
8
+ const targets = [];
9
+ let pattern;
10
+ for (let i = 0; i < args.length; i++) {
11
+ const a = args[i];
12
+ if (a === "--pattern" || a === "-p") {
13
+ pattern = args[++i];
14
+ if (pattern === undefined) {
15
+ console.error(" Error: --pattern requires a value");
16
+ process.exit(1);
17
+ }
18
+ }
19
+ else if (a.startsWith("--pattern=")) {
20
+ pattern = a.slice("--pattern=".length);
21
+ }
22
+ else {
23
+ targets.push(a);
24
+ }
25
+ }
26
+ return { pattern, targets };
27
+ }
6
28
  async function run() {
7
- const files = process.argv.slice(3);
29
+ const { pattern, targets } = parseRunArgs(process.argv.slice(3));
30
+ const files = await discoverTestFiles(targets, { pattern });
8
31
  if (files.length === 0) {
9
- console.error(" Usage: agest run <file...>");
32
+ const effective = pattern ?? DEFAULT_PATTERN;
33
+ console.error(` No test files found (pattern: ${effective})`);
10
34
  process.exit(1);
11
35
  }
12
36
  for (const file of files) {
@@ -29,7 +53,10 @@ if (!command || !commands[command]) {
29
53
  Usage: agest <command>
30
54
 
31
55
  Commands:
32
- run Run test file(s) agest run tests/*.test.ts
56
+ run Run test file(s), directories, or glob patterns
57
+ agest run tests/ # walks for ${DEFAULT_PATTERN}
58
+ agest run src/agest --pattern "**/*.test.ts"
59
+ agest run "tests/**/*.agest.ts" path/to/file.agest.ts
33
60
  stats Show aggregated test statistics
34
61
  preview Generate an HTML report preview
35
62
  `);
@@ -0,0 +1,16 @@
1
+ export declare const DEFAULT_PATTERN = "**/*.agest.ts";
2
+ export interface DiscoverOptions {
3
+ pattern?: string;
4
+ cwd?: string;
5
+ }
6
+ /**
7
+ * Resolve a mix of file paths, directories, and glob patterns into a
8
+ * deduplicated, sorted list of absolute file paths.
9
+ *
10
+ * Rules per target:
11
+ * - directory: search recursively for `pattern` (default `**\/*.agest.ts`)
12
+ * - glob (contains *, ?, [], {}): expand it
13
+ * - file: use as-is
14
+ * - anything else: try as glob (zero matches is fine)
15
+ */
16
+ export declare function discoverTestFiles(targets: string[], options?: DiscoverOptions): Promise<string[]>;
@@ -0,0 +1,62 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { isAbsolute, resolve } from "node:path";
3
+ export const DEFAULT_PATTERN = "**/*.agest.ts";
4
+ const GLOB_CHARS = /[*?[\]{}]/;
5
+ function hasGlobChars(value) {
6
+ return GLOB_CHARS.test(value);
7
+ }
8
+ async function statSafe(path) {
9
+ try {
10
+ const stat = await fs.stat(path);
11
+ return { isFile: stat.isFile(), isDir: stat.isDirectory() };
12
+ }
13
+ catch {
14
+ return { isFile: false, isDir: false };
15
+ }
16
+ }
17
+ async function expandGlob(pattern, cwd) {
18
+ const out = [];
19
+ // fs.promises.glob is available in Node >= 22 (the package's required engine).
20
+ for await (const match of fs.glob(pattern, { cwd })) {
21
+ out.push(isAbsolute(match) ? match : resolve(cwd, match));
22
+ }
23
+ return out;
24
+ }
25
+ /**
26
+ * Resolve a mix of file paths, directories, and glob patterns into a
27
+ * deduplicated, sorted list of absolute file paths.
28
+ *
29
+ * Rules per target:
30
+ * - directory: search recursively for `pattern` (default `**\/*.agest.ts`)
31
+ * - glob (contains *, ?, [], {}): expand it
32
+ * - file: use as-is
33
+ * - anything else: try as glob (zero matches is fine)
34
+ */
35
+ export async function discoverTestFiles(targets, options = {}) {
36
+ const cwd = options.cwd ?? process.cwd();
37
+ const pattern = options.pattern ?? DEFAULT_PATTERN;
38
+ const work = targets.length === 0 ? ["."] : targets;
39
+ const found = new Set();
40
+ for (const target of work) {
41
+ if (hasGlobChars(target)) {
42
+ for (const f of await expandGlob(target, cwd))
43
+ found.add(f);
44
+ continue;
45
+ }
46
+ const stat = await statSafe(isAbsolute(target) ? target : resolve(cwd, target));
47
+ if (stat.isDir) {
48
+ const trimmed = target.replace(/\/+$/, "");
49
+ const dirPattern = `${trimmed}/${pattern}`;
50
+ for (const f of await expandGlob(dirPattern, cwd))
51
+ found.add(f);
52
+ continue;
53
+ }
54
+ if (stat.isFile) {
55
+ found.add(isAbsolute(target) ? target : resolve(cwd, target));
56
+ continue;
57
+ }
58
+ for (const f of await expandGlob(target, cwd))
59
+ found.add(f);
60
+ }
61
+ return [...found].sort();
62
+ }
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export type { AgestConfig, JudgeConfig, JudgeExecutor } from "./config";
7
7
  export type { LogLevel } from "./logger";
8
8
  export type { AgentExpectation, AgentMatchers } from "./assertions";
9
9
  export type { JudgeCriteria } from "./judge";
10
- export type { AgentExecutor, AgentResponse, AgentReport, SceneResult, RunResult, JudgeVerdict, JudgeResult, HookFn, } from "./types";
10
+ export type { AgentExecutor, ExecutorOptions, AgentResponse, AgentReport, SceneResult, RunResult, JudgeVerdict, JudgeResult, HookFn, } from "./types";
11
11
  export interface AgentOptions {
12
12
  name?: string;
13
13
  }
package/dist/runner.js CHANGED
@@ -37,13 +37,20 @@ async function executeSingleRun(executor, scene, timeoutMs, turns, judgeConfig)
37
37
  const start = performance.now();
38
38
  const input = scene.prompt;
39
39
  for (let t = 0; t < turns; t++) {
40
- let timer;
41
- response = await Promise.race([
42
- executor(input).finally(() => clearTimeout(timer)),
43
- new Promise((_, reject) => {
44
- timer = setTimeout(() => reject(new Error(`Scene timed out after ${timeoutMs}ms`)), timeoutMs);
45
- }),
46
- ]);
40
+ const controller = new AbortController();
41
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
42
+ try {
43
+ response = await executor(input, { signal: controller.signal });
44
+ }
45
+ catch (err) {
46
+ if (err.name === "AbortError" || controller.signal.aborted) {
47
+ throw new Error(`Scene timed out after ${timeoutMs}ms`);
48
+ }
49
+ throw err;
50
+ }
51
+ finally {
52
+ clearTimeout(timer);
53
+ }
47
54
  if (response.executionError)
48
55
  break;
49
56
  }
package/dist/types.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- export type AgentExecutor = (input: string) => Promise<AgentResponse>;
1
+ export interface ExecutorOptions {
2
+ signal?: AbortSignal;
3
+ }
4
+ export type AgentExecutor = (input: string, options?: ExecutorOptions) => Promise<AgentResponse>;
2
5
  export interface AgentResponse {
3
6
  text: string;
4
7
  refusal?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sebastiantuyu/agest",
3
- "version": "0.3.2",
3
+ "version": "0.3.3-next.1",
4
4
  "description": "A testing library for agents",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,25 +37,26 @@
37
37
  "site:preview": "npx serve site -p 3000",
38
38
  "release:patch": "npm version patch && git push && git push --tags",
39
39
  "release:minor": "npm version minor && git push && git push --tags",
40
- "release:major": "npm version major && git push && git push --tags"
40
+ "release:major": "npm version major && git push && git push --tags",
41
+ "release:next": "npm version prerelease --preid=next && git push && git push --tags"
41
42
  },
42
43
  "engines": {
43
44
  "node": ">=22.0.0"
44
45
  },
45
46
  "devDependencies": {
46
- "@langchain/core": "^1.1.39",
47
- "@langchain/langgraph": "^1.2.8",
48
- "@langchain/openai": "^1.4.4",
49
- "@types/node": "^22.0.0",
50
- "@vitest/coverage-v8": "^3",
51
- "dotenv": "^17.4.1",
52
- "langchain": "^1.3.1",
53
- "tsx": "^4.21.0",
54
- "typescript": "^5.4.0",
55
- "vitest": "^3",
56
- "zod": "^4.3.6"
47
+ "@langchain/core": "1.1.39",
48
+ "@langchain/langgraph": "1.2.8",
49
+ "@langchain/openai": "1.4.4",
50
+ "@types/node": "22.19.17",
51
+ "@vitest/coverage-v8": "3.2.4",
52
+ "dotenv": "17.4.1",
53
+ "langchain": "1.3.1",
54
+ "tsx": "4.21.0",
55
+ "typescript": "5.9.3",
56
+ "vitest": "3.2.4",
57
+ "zod": "4.3.6"
57
58
  },
58
59
  "dependencies": {
59
- "@supercharge/promise-pool": "^3.3.0"
60
+ "@supercharge/promise-pool": "3.3.0"
60
61
  }
61
62
  }