@theokit/sdk 2.4.0 → 2.6.0
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/CHANGELOG.md +22 -0
- package/README.md +4 -0
- package/dist/eval.cjs +192 -16
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.d.cts +2 -0
- package/dist/eval.d.ts +2 -0
- package/dist/eval.js +191 -18
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +31 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -5
- package/dist/index.d.ts +40 -5
- package/dist/index.js +31 -9
- package/dist/index.js.map +1 -1
- package/dist/internal/eval/code-runner.d.ts +28 -0
- package/dist/internal/persistence/index.cjs +68 -0
- package/dist/internal/persistence/index.cjs.map +1 -1
- package/dist/internal/persistence/index.d.cts +1 -0
- package/dist/internal/persistence/index.d.ts +1 -0
- package/dist/internal/persistence/index.js +65 -1
- package/dist/internal/persistence/index.js.map +1 -1
- package/dist/internal/persistence/jsonl.d.cts +34 -0
- package/dist/internal/persistence/jsonl.d.ts +34 -0
- package/dist/permission-engine.d.ts +12 -4
- package/dist/persistence.cjs +318 -0
- package/dist/persistence.cjs.map +1 -0
- package/dist/persistence.d.cts +24 -0
- package/dist/persistence.d.ts +24 -0
- package/dist/persistence.js +306 -0
- package/dist/persistence.js.map +1 -0
- package/dist/sandbox/index.cjs +71 -1
- package/dist/sandbox/index.cjs.map +1 -1
- package/dist/sandbox/index.d.cts +1 -0
- package/dist/sandbox/index.d.ts +1 -0
- package/dist/sandbox/index.js +70 -2
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/provision.d.cts +53 -0
- package/dist/sandbox/provision.d.ts +53 -0
- package/dist/sandbox/shell-escape.d.cts +8 -0
- package/dist/sandbox/shell-escape.d.ts +8 -0
- package/dist/scorers.d.ts +19 -1
- package/dist/types/eval.d.ts +71 -0
- package/package.json +11 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/sandbox/types.ts","../../src/sandbox/local-sandbox.ts"],"names":["execFile","path","mkdir","dirname","fsWriteFile"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Sandbox backend protocol — pluggable execution environment for agent tools.\n *\n * Per ADR D1: only 2 abstract methods (`execute` + `uploadFile`). All\n * higher-level operations are derived on the base class. New backends\n * (Docker, Firecracker, E2B) only implement those 2 methods.\n *\n * @public\n */\n\nexport interface ExecuteResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n timedOut: boolean;\n}\n\nexport interface SandboxConfig {\n workDir?: string;\n timeoutMs?: number;\n maxOutputBytes?: number;\n}\n\nexport class SandboxSecurityError extends Error {\n readonly code = \"sandbox_security\" as const;\n constructor(message: string) {\n super(message);\n this.name = \"SandboxSecurityError\";\n }\n}\n\nexport class SandboxNotAvailableError extends Error {\n readonly code = \"sandbox_not_available\" as const;\n constructor(message: string) {\n super(message);\n this.name = \"SandboxNotAvailableError\";\n }\n}\n\nconst SHELL_METACHARACTERS = /[;&|`$(){}]/;\n\nexport abstract class SandboxBackend {\n protected config: SandboxConfig;\n\n constructor(config: SandboxConfig = {}) {\n this.config = {\n workDir: config.workDir ?? \"/tmp\",\n timeoutMs: config.timeoutMs ?? 30_000,\n maxOutputBytes: config.maxOutputBytes ?? 5 * 1024 * 1024,\n };\n }\n\n abstract execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult>;\n\n abstract uploadFile(path: string, content: string | Buffer): Promise<void>;\n\n async readFile(path: string): Promise<string> {\n const result = await this.execute(`cat ${this.shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`readFile failed: ${result.stderr}`);\n }\n return result.stdout;\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n await this.uploadFile(path, content);\n }\n\n async glob(pattern: string, cwd?: string): Promise<string[]> {\n const dir = cwd ?? this.config.workDir ?? \".\";\n const result = await this.execute(\n `find ${this.shellEscape(dir)} -name ${this.shellEscape(pattern)} -type f 2>/dev/null`,\n );\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async grep(pattern: string, path?: string): Promise<string[]> {\n const target = path ?? \".\";\n const result = await this.execute(\n `grep -rn ${this.shellEscape(pattern)} ${this.shellEscape(target)} 2>/dev/null`,\n );\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async listDir(path: string): Promise<string[]> {\n const result = await this.execute(`ls -1 ${this.shellEscape(path)}`);\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n protected validateCommand(command: string): void {\n if (SHELL_METACHARACTERS.test(command)) {\n throw new SandboxSecurityError(\n `Command contains shell metacharacters: ${command.slice(0, 80)}`,\n );\n }\n }\n\n protected truncateOutput(output: string): string {\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n if (Buffer.byteLength(output) > max) {\n return `${output.slice(0, max)}\\n...(truncated)`;\n }\n return output;\n }\n\n private shellEscape(arg: string): string {\n return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n }\n}\n","/**\n * LocalSandbox — subprocess-based execution with NO isolation.\n *\n * Uses `execFile` with split args (NOT `exec` with string) per EC-1.\n * This is NOT a security boundary — only DockerSandbox provides isolation.\n *\n * @public\n */\n\nimport { execFile } from \"node:child_process\";\nimport { writeFile as fsWriteFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { type ExecuteResult, SandboxBackend, type SandboxConfig } from \"./types.js\";\n\nexport class LocalSandbox extends SandboxBackend {\n constructor(config: SandboxConfig = {}) {\n super(config);\n }\n\n async execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult> {\n const timeout = opts?.timeoutMs ?? this.config.timeoutMs ?? 30_000;\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n\n return new Promise<ExecuteResult>((resolve) => {\n const child = execFile(\n \"/bin/sh\",\n [\"-c\", command],\n {\n cwd: this.config.workDir,\n timeout,\n maxBuffer: max,\n encoding: \"utf-8\",\n },\n (error, stdout, stderr) => {\n resolve(this.buildResult(error, stdout ?? \"\", stderr ?? \"\"));\n },\n );\n\n // Safety: if child somehow doesn't callback\n child.on(\"error\", () => {\n resolve({ stdout: \"\", stderr: \"spawn error\", exitCode: 1, timedOut: false });\n });\n });\n }\n\n private buildResult(error: Error | null, stdout: string, stderr: string): ExecuteResult {\n const timedOut = error !== null && \"killed\" in error && (error as { killed: boolean }).killed;\n return {\n stdout: this.truncateOutput(stdout),\n stderr: this.truncateOutput(stderr),\n exitCode: timedOut ? 124 : error ? 1 : 0,\n timedOut,\n };\n }\n\n async uploadFile(path: string, content: string | Buffer): Promise<void> {\n const fullPath = path.startsWith(\"/\") ? path : `${this.config.workDir}/${path}`;\n await mkdir(dirname(fullPath), { recursive: true });\n await fsWriteFile(fullPath, content, \"utf-8\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/sandbox/shell-escape.ts","../../src/sandbox/types.ts","../../src/sandbox/local-sandbox.ts","../../src/errors.ts","../../src/sandbox/provision.ts"],"names":["execFile","path","mkdir","dirname","fsWriteFile"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * POSIX shell escaping for values interpolated into a `SandboxBackend.execute`\n * command string. `execute` runs via `/bin/sh -c`, so any untrusted value\n * (repo URL, ref, path) MUST be quoted to prevent command injection.\n *\n * @internal\n */\n\n/** Wrap `arg` in single quotes, escaping embedded single quotes (`'\\''`). */\nexport function shellEscapePosix(arg: string): string {\n return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n}\n","/**\n * Sandbox backend protocol — pluggable execution environment for agent tools.\n *\n * Per ADR D1: only 2 abstract methods (`execute` + `uploadFile`). All\n * higher-level operations are derived on the base class. New backends\n * (Docker, Firecracker, E2B) only implement those 2 methods.\n *\n * @public\n */\n\nimport { shellEscapePosix } from \"./shell-escape.js\";\n\nexport interface ExecuteResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n timedOut: boolean;\n}\n\nexport interface SandboxConfig {\n workDir?: string;\n timeoutMs?: number;\n maxOutputBytes?: number;\n}\n\nexport class SandboxSecurityError extends Error {\n readonly code = \"sandbox_security\" as const;\n constructor(message: string) {\n super(message);\n this.name = \"SandboxSecurityError\";\n }\n}\n\nexport class SandboxNotAvailableError extends Error {\n readonly code = \"sandbox_not_available\" as const;\n constructor(message: string) {\n super(message);\n this.name = \"SandboxNotAvailableError\";\n }\n}\n\nconst SHELL_METACHARACTERS = /[;&|`$(){}]/;\n\nexport abstract class SandboxBackend {\n protected config: SandboxConfig;\n\n constructor(config: SandboxConfig = {}) {\n this.config = {\n workDir: config.workDir ?? \"/tmp\",\n timeoutMs: config.timeoutMs ?? 30_000,\n maxOutputBytes: config.maxOutputBytes ?? 5 * 1024 * 1024,\n };\n }\n\n abstract execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult>;\n\n abstract uploadFile(path: string, content: string | Buffer): Promise<void>;\n\n async readFile(path: string): Promise<string> {\n const result = await this.execute(`cat ${this.shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`readFile failed: ${result.stderr}`);\n }\n return result.stdout;\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n await this.uploadFile(path, content);\n }\n\n async glob(pattern: string, cwd?: string): Promise<string[]> {\n const dir = cwd ?? this.config.workDir ?? \".\";\n const result = await this.execute(\n `find ${this.shellEscape(dir)} -name ${this.shellEscape(pattern)} -type f 2>/dev/null`,\n );\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async grep(pattern: string, path?: string): Promise<string[]> {\n const target = path ?? \".\";\n const result = await this.execute(\n `grep -rn ${this.shellEscape(pattern)} ${this.shellEscape(target)} 2>/dev/null`,\n );\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async listDir(path: string): Promise<string[]> {\n const result = await this.execute(`ls -1 ${this.shellEscape(path)}`);\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n protected validateCommand(command: string): void {\n if (SHELL_METACHARACTERS.test(command)) {\n throw new SandboxSecurityError(\n `Command contains shell metacharacters: ${command.slice(0, 80)}`,\n );\n }\n }\n\n protected truncateOutput(output: string): string {\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n if (Buffer.byteLength(output) > max) {\n return `${output.slice(0, max)}\\n...(truncated)`;\n }\n return output;\n }\n\n private shellEscape(arg: string): string {\n return shellEscapePosix(arg);\n }\n}\n","/**\n * LocalSandbox — subprocess-based execution with NO isolation.\n *\n * Uses `execFile` with split args (NOT `exec` with string) per EC-1.\n * This is NOT a security boundary — only DockerSandbox provides isolation.\n *\n * @public\n */\n\nimport { execFile } from \"node:child_process\";\nimport { writeFile as fsWriteFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { type ExecuteResult, SandboxBackend, type SandboxConfig } from \"./types.js\";\n\nexport class LocalSandbox extends SandboxBackend {\n constructor(config: SandboxConfig = {}) {\n super(config);\n }\n\n async execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult> {\n const timeout = opts?.timeoutMs ?? this.config.timeoutMs ?? 30_000;\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n\n return new Promise<ExecuteResult>((resolve) => {\n const child = execFile(\n \"/bin/sh\",\n [\"-c\", command],\n {\n cwd: this.config.workDir,\n timeout,\n maxBuffer: max,\n encoding: \"utf-8\",\n },\n (error, stdout, stderr) => {\n resolve(this.buildResult(error, stdout ?? \"\", stderr ?? \"\"));\n },\n );\n\n // Safety: if child somehow doesn't callback\n child.on(\"error\", () => {\n resolve({ stdout: \"\", stderr: \"spawn error\", exitCode: 1, timedOut: false });\n });\n });\n }\n\n private buildResult(error: Error | null, stdout: string, stderr: string): ExecuteResult {\n const timedOut = error !== null && \"killed\" in error && (error as { killed: boolean }).killed;\n return {\n stdout: this.truncateOutput(stdout),\n stderr: this.truncateOutput(stderr),\n exitCode: timedOut ? 124 : error ? 1 : 0,\n timedOut,\n };\n }\n\n async uploadFile(path: string, content: string | Buffer): Promise<void> {\n const fullPath = path.startsWith(\"/\") ? path : `${this.config.workDir}/${path}`;\n await mkdir(dirname(fullPath), { recursive: true });\n await fsWriteFile(fullPath, content, \"utf-8\");\n }\n}\n","import { defaultRetriableForCode } from \"./internal/default-retriable.js\";\nimport { redactSecrets } from \"./internal/security/redact.js\";\nimport type { RunOperation } from \"./types/run.js\";\n\n/**\n * Finite, machine-readable error codes for provider-originated errors\n * (ADR D66). Consumers can `switch (err.metadata?.code)` exhaustively\n * — adding a new variant is an explicit decision + test coverage.\n *\n * @public\n */\nexport type ErrorCode =\n | \"rate_limit\"\n | \"auth_failed\"\n | \"invalid_request\"\n | \"timeout\"\n | \"server_error\"\n | \"context_too_long\"\n | \"content_filtered\"\n | \"model_unavailable\"\n | \"network\"\n | \"quota_exceeded\"\n | \"unknown\";\n\n/**\n * Codes used by {@link AgentRunError} (Production-Readiness #3, ADR D311).\n *\n * Superset of {@link ErrorCode} extended with codes that do NOT originate\n * from a provider HTTP response:\n *\n * - `quota_exceeded` — billing limit hit (provider 402 or signalled error)\n * - `tool_runtime_error` — custom tool handler threw inside dispatch\n * - `aborted` — caller's `AbortSignal` fired (Phase 4)\n * - `invalid_model` — model id rejected by provider (400 \"model not found\")\n * - `safety_blocked` — provider safety filter blocked req or resp\n * - `provider_unreachable` — DNS/TCP/timeout/5xx at transport boundary\n *\n * The `& {}` tail keeps the literal-union ergonomics (autocomplete) while\n * accepting any string for forward compatibility with constructor calls\n * that pass arbitrary code values (legacy callers).\n *\n * @public\n */\n/**\n * T1.1 — closed literal union for `AgentRunError.code`. The previous\n * `(string & {})` escape hatch let arbitrary strings slip into the type\n * surface and defeated exhaustive `switch (code)` discrimination. This is\n * the canonical closed form. `AgentRunErrorCode` is re-aliased below for\n * source-level back-compat.\n *\n * Adding a new code: append the literal here AND audit every `switch (err.code)`\n * in callers. Type-checker enforces the audit via the `default: assertNever(code)`\n * convention.\n *\n * @public\n */\nexport type KnownAgentRunErrorCode =\n | ErrorCode\n | \"quota_exceeded\"\n | \"tool_runtime_error\"\n | \"aborted\"\n | \"invalid_model\"\n | \"safety_blocked\"\n | \"provider_unreachable\";\n\n/**\n * Back-compat alias of {@link KnownAgentRunErrorCode}. Pre-T1.1 callers that\n * imported `AgentRunErrorCode` keep working; new code SHOULD prefer\n * `KnownAgentRunErrorCode` to make the closed-union intent explicit.\n *\n * @public\n */\nexport type AgentRunErrorCode = KnownAgentRunErrorCode;\n\n/** Snapshot of every known code at runtime — used by the boundary coercer. */\nconst KNOWN_AGENT_RUN_ERROR_CODES = new Set<string>([\n \"rate_limit\",\n \"auth_failed\",\n \"invalid_request\",\n \"timeout\",\n \"server_error\",\n \"context_too_long\",\n \"content_filtered\",\n \"model_unavailable\",\n \"network\",\n \"unknown\",\n \"quota_exceeded\",\n \"tool_runtime_error\",\n \"aborted\",\n \"invalid_model\",\n \"safety_blocked\",\n \"provider_unreachable\",\n]);\n\n/**\n * T1.1 boundary helper — coerce an arbitrary string (typically arriving from\n * a downstream `RunErrorDetail.code` or a deserialized cloud response) into a\n * `KnownAgentRunErrorCode`. Unknown strings collapse to `\"unknown\"` so the\n * closed type contract holds without forcing every caller to switch.\n *\n * @internal\n */\nexport function coerceToKnownAgentRunErrorCode(code: string | undefined): KnownAgentRunErrorCode {\n if (code !== undefined && KNOWN_AGENT_RUN_ERROR_CODES.has(code)) {\n return code as KnownAgentRunErrorCode;\n }\n return \"unknown\";\n}\n\n/**\n * Structured context for errors that originated from a provider HTTP\n * call (ADR D65). Lets callers retry with the right backoff (`retryAfter`),\n * surface actionable diagnostics (`provider`, `endpoint`), and inspect the\n * raw response body when needed (`raw`, capped at ~2KB by the mapper).\n *\n * @public\n */\nexport interface ErrorMetadata {\n /** Provider canonical name (e.g., `\"anthropic\"`, `\"openai\"`, `\"openrouter\"`, `\"gemini\"`). */\n provider: string;\n /** HTTP endpoint that failed (e.g., `\"/v1/messages\"`, `\"/v1/chat/completions\"`). */\n endpoint: string;\n /** Machine-readable error code (finite enum). */\n code: ErrorCode;\n /** HTTP status code if applicable. */\n statusCode?: number;\n /** Seconds to wait before retry, per provider's `retry-after` header (numeric form only). */\n retryAfter?: number;\n /** Raw response body for debugging (truncated to ~2KB by the mapper). */\n raw?: unknown;\n}\n\n/**\n * Base class for all errors thrown by `@theokit/sdk`.\n *\n * Use `isRetryable` to drive retry/backoff logic. `code` and `protoErrorCode`\n * are populated for server-originated errors when available. `metadata`\n * (ADR D65) carries structured `{ provider, endpoint, code, ... }` when\n * the error originated from a provider HTTP call.\n *\n * @public\n */\nexport class TheokitAgentError extends Error {\n override readonly name: string = \"TheokitAgentError\";\n readonly isRetryable: boolean;\n readonly code?: string;\n readonly protoErrorCode?: string;\n readonly metadata?: ErrorMetadata;\n\n constructor(\n message: string,\n options: {\n isRetryable?: boolean;\n code?: string;\n protoErrorCode?: string;\n cause?: unknown;\n metadata?: ErrorMetadata;\n } = {},\n ) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.isRetryable = options.isRetryable ?? false;\n if (options.code !== undefined) this.code = options.code;\n if (options.protoErrorCode !== undefined) this.protoErrorCode = options.protoErrorCode;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n}\n\n/**\n * Invalid API key, not logged in, insufficient permissions.\n *\n * @public\n */\nexport class AuthenticationError extends TheokitAgentError {\n override readonly name: string = \"AuthenticationError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: false });\n }\n}\n\n/**\n * Too many requests or usage limits exceeded.\n *\n * @public\n */\nexport class RateLimitError extends TheokitAgentError {\n override readonly name: string = \"RateLimitError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: true });\n }\n}\n\n/**\n * Invalid model, bad request parameters, malformed options.\n *\n * @public\n */\nexport class ConfigurationError extends TheokitAgentError {\n override readonly name: string = \"ConfigurationError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: false });\n }\n}\n\n/**\n * Thrown when creating a cloud agent for a repo whose SCM provider is not\n * connected. Use `helpUrl` to point the user at the right reconnect flow.\n *\n * @public\n */\nexport class IntegrationNotConnectedError extends ConfigurationError {\n override readonly name: string = \"IntegrationNotConnectedError\";\n readonly provider: string;\n readonly helpUrl: string;\n\n constructor(\n message: string,\n options: {\n provider: string;\n helpUrl: string;\n code?: string;\n cause?: unknown;\n metadata?: ErrorMetadata;\n },\n ) {\n super(message, options);\n this.provider = options.provider;\n this.helpUrl = options.helpUrl;\n }\n}\n\n/**\n * Service unavailable, timeout, transport-level failure.\n *\n * @public\n */\nexport class NetworkError extends TheokitAgentError {\n override readonly name: string = \"NetworkError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: true });\n }\n}\n\n/**\n * Catch-all for unclassified server or runtime errors.\n *\n * @public\n */\nexport class UnknownAgentError extends TheokitAgentError {\n override readonly name: string = \"UnknownAgentError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: false });\n }\n}\n\n/**\n * Thrown by `Agent.prompt` (and helpers that go through `run.wait()`) when\n * the option `{ throwOnError: true }` is set and the run terminates with\n * `status: 'error'`. Carries the structured `RunResult.error` fields so\n * callers can `catch` once and branch on `code` / `provider` instead of\n * unwrapping the run.\n *\n * Extends {@link TheokitAgentError} per ADR D65 — no new hierarchy.\n *\n * @example\n * try {\n * await Agent.prompt(msg, { apiKey, model, throwOnError: true });\n * } catch (err) {\n * if (err instanceof AgentRunError && err.code === 'auth_failed') {\n * // bad key\n * }\n * }\n *\n * @public\n */\nexport class AgentRunError extends TheokitAgentError {\n override readonly name: string = \"AgentRunError\";\n readonly provider?: string;\n readonly raw?: string;\n /** Provider's request id (`x-request-id` / `request-id` header). Useful for support tickets. */\n readonly requestId?: string;\n /** SDK conversation id this error was raised inside. */\n readonly conversationId?: string;\n\n constructor(\n message: string,\n options: {\n code: AgentRunErrorCode;\n provider?: string;\n raw?: string;\n requestId?: string;\n conversationId?: string;\n retriable?: boolean;\n cause?: unknown;\n metadata?: ErrorMetadata;\n },\n ) {\n super(message, {\n code: options.code,\n cause: options.cause,\n metadata: options.metadata,\n // D311: most AgentRunErrors are not retriable (auth, validation, abort).\n // Provider mappers (D314) override per-status — explicit `retriable` wins\n // over the implicit default when supplied.\n isRetryable: options.retriable ?? defaultRetriableForCode(options.code),\n });\n if (options.provider !== undefined) this.provider = options.provider;\n if (options.raw !== undefined) this.raw = options.raw;\n if (options.requestId !== undefined) this.requestId = options.requestId;\n if (options.conversationId !== undefined) this.conversationId = options.conversationId;\n }\n\n /**\n * Production-Readiness #3 (ADR D311): alias for `isRetryable` exposed as\n * `retriable` to match the handoff contract. Future v2 will deprecate\n * `isRetryable` in favor of this.\n */\n get retriable(): boolean {\n return this.isRetryable;\n }\n\n /**\n * D312: provider's `Retry-After` header in **milliseconds**. Mappers store\n * the header value (seconds) in `metadata.retryAfter`; this getter\n * multiplies by 1000 so the result composes with `Date.now()`/`setTimeout`.\n *\n * Returns `undefined` when no hint was provided. `0` is a legitimate value\n * — use `=== undefined` check rather than truthy check.\n */\n get retryAfterMs(): number | undefined {\n if (this.metadata?.retryAfter === undefined) return undefined;\n return this.metadata.retryAfter * 1000;\n }\n\n /**\n * D313 + T1.5: alias for `metadata.raw`. Provider response body for\n * debugging. T1.5 wraps the value in `redactSecrets` at the getter\n * boundary so secret-shaped substrings (`sk-...`, Bearer JWTs, etc.) are\n * stripped before reaching the caller. Available but NEVER serialized\n * into `.message` (anti-leak invariant).\n */\n get providerError(): unknown {\n const raw = this.metadata?.raw;\n if (raw === undefined) return undefined;\n if (typeof raw === \"string\") return redactSecrets(raw);\n // Non-string raw (object/buffer) — stringify then redact.\n try {\n return redactSecrets(JSON.stringify(raw));\n } catch {\n return redactSecrets(String(raw));\n }\n }\n\n /**\n * T1.5 — sanitized JSON form. `metadata.raw` is OMITTED by default; opt\n * in via `THEOKIT_DEBUG_RAW_ERRORS=1` to surface the (redacted) raw\n * payload for diagnostics. Every other field stays accessible.\n *\n * The single env-var gate is read each call so operators can toggle at\n * runtime without restarting the process.\n */\n toJSON(): Record<string, unknown> {\n const json: Record<string, unknown> = {\n name: this.name,\n message: this.message,\n isRetryable: this.isRetryable,\n };\n addOptionalFields(json, this);\n const safeMeta = sanitizeMetadata(this.metadata);\n if (safeMeta !== undefined) json.metadata = safeMeta;\n return json;\n }\n}\n\nfunction addOptionalFields(json: Record<string, unknown>, err: AgentRunError): void {\n if (err.code !== undefined) json.code = err.code;\n if (err.provider !== undefined) json.provider = err.provider;\n if (err.requestId !== undefined) json.requestId = err.requestId;\n if (err.conversationId !== undefined) json.conversationId = err.conversationId;\n if (err.raw !== undefined) json.raw = redactSecrets(err.raw);\n}\n\nfunction sanitizeMetadata(meta: ErrorMetadata | undefined): ErrorMetadata | undefined {\n if (meta === undefined) return undefined;\n const { raw, ...rest } = meta;\n const debugRaw = process.env.THEOKIT_DEBUG_RAW_ERRORS === \"1\";\n if (debugRaw && raw !== undefined) {\n const redactedRaw =\n typeof raw === \"string\" ? redactSecrets(raw) : redactSecrets(safeStringify(raw));\n return { ...rest, raw: redactedRaw } as ErrorMetadata;\n }\n return rest as ErrorMetadata;\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Is this error transient (worth retrying)?\n *\n * Returns the SDK's own retryability verdict: every {@link TheokitAgentError}\n * subclass computes `isRetryable` at construction (rate-limit / network /\n * credential-pool-exhausted are retryable; auth / configuration / unsupported\n * are not), so this predicate is a single source of truth rather than a\n * re-derivation. Non-SDK errors return `false` conservatively — wrap a foreign\n * error in the appropriate SDK error first if you want it considered transient.\n * It never inspects `err.message`.\n *\n * @example\n * try {\n * await agent.send(message, { throwOnError: true });\n * } catch (err) {\n * if (isTransientError(err)) return retryWithBackoff();\n * throw err;\n * }\n *\n * @public\n */\nexport function isTransientError(err: unknown): boolean {\n return err instanceof TheokitAgentError && err.isRetryable === true;\n}\n\n/**\n * Thrown when a {@link Run} or agent operation is not available on the current\n * runtime. Check first with `run.supports(operation)`.\n *\n * Extends {@link TheokitAgentError} (so error-catching code that branches on\n * `instanceof TheokitAgentError` continues to work) but is never retryable —\n * an unsupported operation will not become supported on retry.\n *\n * @public\n */\nexport class UnsupportedRunOperationError extends TheokitAgentError {\n override readonly name: string = \"UnsupportedRunOperationError\";\n readonly operation: RunOperation;\n\n constructor(\n message: string,\n operation: RunOperation,\n options: { code?: string; cause?: unknown } = {},\n ) {\n super(message, {\n ...options,\n isRetryable: false,\n code: options.code ?? \"unsupported_run_operation\",\n });\n this.operation = operation;\n }\n}\n\n/**\n * Thrown when every credential in a per-provider pool is in cooldown\n * and no healthy key is available (ADR D133). The caller's\n * {@link import(\"./internal/llm/fallback-client.js\").FallbackLlmClient}\n * catches this and tries the next provider in the fallback chain.\n *\n * `metadata.nextRetryAt` (epoch ms) tells callers when the soonest\n * pool entry resumes — useful for manual retry scheduling.\n *\n * @public\n */\nexport class CredentialPoolExhaustedError extends TheokitAgentError {\n override readonly name: string = \"CredentialPoolExhaustedError\";\n readonly provider: string;\n readonly nextRetryAt: number | undefined;\n\n constructor(\n message: string,\n options: {\n provider: string;\n nextRetryAt?: number;\n code?: string;\n cause?: unknown;\n metadata?: ErrorMetadata;\n },\n ) {\n super(message, {\n ...options,\n isRetryable: true,\n code: options.code ?? \"credential_pool_exhausted\",\n });\n this.provider = options.provider;\n this.nextRetryAt = options.nextRetryAt;\n }\n}\n\n/**\n * Finite error codes specific to memory adapter operations (ADR D141).\n *\n * @public\n */\nexport type MemoryAdapterErrorCode =\n | \"auth_failed\"\n | \"rate_limited\"\n | \"not_found\"\n | \"network\"\n | \"invalid_input\"\n | \"unknown\";\n\n/**\n * Error raised by `@theokit-memory-*` adapters. Carries `adapterId`\n * so callers can branch on which provider failed (ADR D141).\n *\n * @public\n */\nexport class MemoryAdapterError extends TheokitAgentError {\n override readonly name: string = \"MemoryAdapterError\";\n readonly adapterId: string;\n\n constructor(\n message: string,\n options: {\n adapterId: string;\n code: MemoryAdapterErrorCode;\n cause?: unknown;\n metadata?: ErrorMetadata;\n },\n ) {\n super(message, {\n isRetryable: options.code === \"rate_limited\" || options.code === \"network\",\n code: options.code,\n ...(options.cause !== undefined ? { cause: options.cause } : {}),\n ...(options.metadata !== undefined ? { metadata: options.metadata } : {}),\n });\n this.adapterId = options.adapterId;\n }\n}\n\n/**\n * Thrown when a user-supplied task ID violates the grammar\n * `^[a-z0-9][a-z0-9_-]*$` (D368) OR starts with a reserved adapter\n * prefix (`wf-` / `b-` / `cron-`, EC-5).\n *\n * @public\n */\nexport class InvalidTaskIdError extends TheokitAgentError {\n override readonly name: string = \"InvalidTaskIdError\";\n readonly taskId: string;\n\n constructor(message: string, taskId: string, options: { cause?: unknown } = {}) {\n super(message, {\n ...options,\n isRetryable: false,\n code: \"invalid_task_id\",\n });\n this.taskId = taskId;\n }\n}\n\n/**\n * Thrown when `Task.subscribe(id)` is called for a task that has been\n * evicted, never submitted, or evicted after retention (D373).\n *\n * @public\n */\nexport class TaskNotFoundError extends TheokitAgentError {\n override readonly name: string = \"TaskNotFoundError\";\n readonly taskId: string;\n\n constructor(taskId: string, options: { cause?: unknown } = {}) {\n super(`Task not found: ${taskId}`, {\n ...options,\n isRetryable: false,\n code: \"task_not_found\",\n });\n this.taskId = taskId;\n }\n}\n\n/**\n * Thrown when `CloudAgent` is asked to wrap a task (D370). Cloud\n * task observability is deferred until Theo PaaS GA.\n *\n * @public\n */\nexport class UnsupportedTaskOperationError extends TheokitAgentError {\n override readonly name: string = \"UnsupportedTaskOperationError\";\n readonly operation: string;\n\n constructor(operation: string, options: { cause?: unknown } = {}) {\n super(\n `Task operation \"${operation}\" is not supported on CloudAgent (pre-release; see ADR D370)`,\n {\n ...options,\n isRetryable: false,\n code: \"task_op_unsupported\",\n },\n );\n this.operation = operation;\n }\n}\n\n/**\n * Thrown by `Budget` enforcement (ADR D386) when a `mode: \"block\"`\n * budget would be exceeded by the upcoming LLM call. Caller pega\n * tipado para retry-after-window-reset or surface to the user.\n *\n * @public\n */\nexport class BudgetExceededError extends TheokitAgentError {\n override readonly name: string = \"BudgetExceededError\";\n readonly budgetName: string;\n readonly window: import(\"./types/budget.js\").BudgetWindow;\n readonly spentUsd: number;\n readonly limitUsd: number;\n readonly mode: import(\"./types/budget.js\").BudgetMode;\n\n constructor(args: {\n budgetName: string;\n window: import(\"./types/budget.js\").BudgetWindow;\n spentUsd: number;\n limitUsd: number;\n mode: import(\"./types/budget.js\").BudgetMode;\n cause?: unknown;\n }) {\n super(\n `Budget \"${args.budgetName}\" exceeded for window ${args.window}: spent $${args.spentUsd.toFixed(4)} > limit $${args.limitUsd.toFixed(4)}`,\n {\n ...(args.cause !== undefined ? { cause: args.cause } : {}),\n isRetryable: false,\n code: \"budget_exceeded\",\n },\n );\n this.budgetName = args.budgetName;\n this.window = args.window;\n this.spentUsd = args.spentUsd;\n this.limitUsd = args.limitUsd;\n this.mode = args.mode;\n }\n}\n\n/**\n * Thrown when `CloudAgent.send({ budget })` is invoked (D388). Cloud\n * budget surface waits for Theo PaaS GA.\n *\n * @public\n */\n/**\n * T1.6 — Thrown when a consumer calls `agent.send()` or any method\n * on an agent that has already been `dispose()`d. Pre-T1.6 this was\n * a generic `new Error(\"Agent has been disposed\")` — consumers\n * couldn't catch it without string-matching the message.\n *\n * @public\n */\nexport class AgentDisposedError extends TheokitAgentError {\n override readonly name: string = \"AgentDisposedError\";\n readonly agentId: string;\n\n constructor(agentId: string) {\n super(`Agent \"${agentId}\" has been disposed. Create a new agent or use Agent.resume().`, {\n isRetryable: false,\n code: \"agent_disposed\",\n });\n this.agentId = agentId;\n }\n}\n\nexport class UnsupportedBudgetOperationError extends TheokitAgentError {\n override readonly name: string = \"UnsupportedBudgetOperationError\";\n readonly operation: string;\n\n constructor(operation: string, options: { cause?: unknown } = {}) {\n super(\n `Budget operation \"${operation}\" is not supported on CloudAgent (pre-release; see ADR D388)`,\n {\n ...options,\n isRetryable: false,\n code: \"budget_op_unsupported\",\n },\n );\n this.operation = operation;\n }\n}\n","/**\n * M6-3 — portable repo provisioner for the eval harness.\n *\n * Clones a repository and checks out a ref into an isolated working dir, issuing\n * every git command through {@link SandboxBackend.execute} (ADR D2 — same code\n * runs on Local/Docker/E2B; never a direct `child_process` import). Promotes\n * theocode's `prepareRepo` (`swebench-provision.ts:37`) onto the SDK's sandbox\n * abstraction.\n *\n * referencia: knowledge-base/references/theocode-eval/lib/swebench-provision.ts:37\n * (clone+checkout), :13 (ProvisionError with instanceId).\n *\n * @public\n */\n\nimport { TheokitAgentError } from \"../errors.js\";\nimport { shellEscapePosix } from \"./shell-escape.js\";\nimport type { SandboxBackend } from \"./types.js\";\n\n/**\n * Raised when cloning or checking out a repo fails. Carries the `instanceId`\n * so a batch run can attribute the failure to the offending dataset row.\n */\nexport class RepoProvisionError extends TheokitAgentError {\n override readonly name = \"RepoProvisionError\";\n\n constructor(\n readonly instanceId: string,\n message: string,\n options: { cause?: unknown } = {},\n ) {\n super(`[${instanceId}] ${message}`, {\n code: \"repo_provision_failed\",\n isRetryable: false,\n ...(options.cause !== undefined ? { cause: options.cause } : {}),\n });\n }\n}\n\n/** Options for {@link provisionRepo}. */\nexport interface ProvisionRepoOptions {\n /**\n * Clonable repo URL or local path. SECURITY: when this comes from an\n * untrusted dataset, the value is passed to `git clone` after a `--`\n * end-of-options terminator (no flag injection) and with the `ext::`\n * transport disabled (no arbitrary-command transport).\n */\n readonly repoUrl: string;\n /** Branch, tag, or commit SHA to check out. Rejected if it begins with `-`. */\n readonly ref: string;\n /**\n * Unique id for this row — names the target dir and any error. Validated to\n * `[A-Za-z0-9._-]` (no path traversal) since it becomes a directory name.\n */\n readonly instanceId: string;\n}\n\n/**\n * Reject ids that would escape the workdir or be parsed as a git flag. Must\n * start with an alphanumeric (blocks `.`, `..`, `-foo`, leading-dot names) and\n * thereafter allow only `[A-Za-z0-9._-]`.\n */\nconst SAFE_INSTANCE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\n/**\n * Clone `repoUrl` into `<sandbox workdir>/<instanceId>` and check out `ref`.\n * Returns the absolute `repoDir` (resolved via `git rev-parse --show-toplevel`,\n * which is portable across backends). Throws {@link RepoProvisionError} naming\n * the `instanceId` when clone or checkout exits non-zero.\n */\nexport async function provisionRepo(\n sandbox: SandboxBackend,\n opts: ProvisionRepoOptions,\n): Promise<{ repoDir: string }> {\n const { repoUrl, ref, instanceId } = opts;\n\n // Validate untrusted-derivable inputs before they reach git/the shell.\n if (!SAFE_INSTANCE_ID.test(instanceId)) {\n throw new RepoProvisionError(\n instanceId,\n \"invalid instanceId: must match [A-Za-z0-9._-] (no path traversal)\",\n );\n }\n if (ref.startsWith(\"-\")) {\n throw new RepoProvisionError(instanceId, `invalid ref: must not begin with '-' (got ${ref})`);\n }\n\n // `--` terminates options (no `--upload-pack=` flag injection); `protocol.ext.allow=never`\n // blocks the `ext::` arbitrary-command transport. `file`/`https` stay allowed.\n const clone = await sandbox.execute(\n `git -c protocol.ext.allow=never clone --quiet -- ${shellEscapePosix(repoUrl)} ${shellEscapePosix(instanceId)}`,\n );\n if (clone.exitCode !== 0) {\n throw new RepoProvisionError(instanceId, `clone failed: ${clone.stderr.trim()}`);\n }\n\n const top = await sandbox.execute(\n `git -C ${shellEscapePosix(instanceId)} rev-parse --show-toplevel`,\n );\n if (top.exitCode !== 0) {\n throw new RepoProvisionError(instanceId, `resolve repoDir failed: ${top.stderr.trim()}`);\n }\n const repoDir = top.stdout.trim();\n\n // `ref` is validated above not to begin with `-`, so it cannot be parsed as a flag.\n const checkout = await sandbox.execute(\n `git -C ${shellEscapePosix(repoDir)} checkout --quiet ${shellEscapePosix(ref)}`,\n );\n if (checkout.exitCode !== 0) {\n throw new RepoProvisionError(instanceId, `checkout ${ref} failed: ${checkout.stderr.trim()}`);\n }\n\n return { repoDir };\n}\n"]}
|
package/dist/sandbox/index.d.cts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { LocalSandbox } from "./local-sandbox.js";
|
|
2
|
+
export { type ProvisionRepoOptions, provisionRepo, RepoProvisionError, } from "./provision.js";
|
|
2
3
|
export { type ExecuteResult, SandboxBackend, type SandboxConfig, SandboxNotAvailableError, SandboxSecurityError, } from "./types.js";
|
package/dist/sandbox/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { LocalSandbox } from "./local-sandbox.js";
|
|
2
|
+
export { type ProvisionRepoOptions, provisionRepo, RepoProvisionError, } from "./provision.js";
|
|
2
3
|
export { type ExecuteResult, SandboxBackend, type SandboxConfig, SandboxNotAvailableError, SandboxSecurityError, } from "./types.js";
|
package/dist/sandbox/index.js
CHANGED
|
@@ -4,6 +4,11 @@ import { dirname } from 'path';
|
|
|
4
4
|
|
|
5
5
|
// src/sandbox/local-sandbox.ts
|
|
6
6
|
|
|
7
|
+
// src/sandbox/shell-escape.ts
|
|
8
|
+
function shellEscapePosix(arg) {
|
|
9
|
+
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
// src/sandbox/types.ts
|
|
8
13
|
var SandboxSecurityError = class extends Error {
|
|
9
14
|
code = "sandbox_security";
|
|
@@ -76,7 +81,7 @@ var SandboxBackend = class {
|
|
|
76
81
|
return output;
|
|
77
82
|
}
|
|
78
83
|
shellEscape(arg) {
|
|
79
|
-
return
|
|
84
|
+
return shellEscapePosix(arg);
|
|
80
85
|
}
|
|
81
86
|
};
|
|
82
87
|
|
|
@@ -123,6 +128,69 @@ var LocalSandbox = class extends SandboxBackend {
|
|
|
123
128
|
}
|
|
124
129
|
};
|
|
125
130
|
|
|
126
|
-
|
|
131
|
+
// src/errors.ts
|
|
132
|
+
var TheokitAgentError = class extends Error {
|
|
133
|
+
name = "TheokitAgentError";
|
|
134
|
+
isRetryable;
|
|
135
|
+
code;
|
|
136
|
+
protoErrorCode;
|
|
137
|
+
metadata;
|
|
138
|
+
constructor(message, options = {}) {
|
|
139
|
+
super(message, options.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
140
|
+
this.isRetryable = options.isRetryable ?? false;
|
|
141
|
+
if (options.code !== void 0) this.code = options.code;
|
|
142
|
+
if (options.protoErrorCode !== void 0) this.protoErrorCode = options.protoErrorCode;
|
|
143
|
+
if (options.metadata !== void 0) this.metadata = options.metadata;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// src/sandbox/provision.ts
|
|
148
|
+
var RepoProvisionError = class extends TheokitAgentError {
|
|
149
|
+
constructor(instanceId, message, options = {}) {
|
|
150
|
+
super(`[${instanceId}] ${message}`, {
|
|
151
|
+
code: "repo_provision_failed",
|
|
152
|
+
isRetryable: false,
|
|
153
|
+
...options.cause !== void 0 ? { cause: options.cause } : {}
|
|
154
|
+
});
|
|
155
|
+
this.instanceId = instanceId;
|
|
156
|
+
}
|
|
157
|
+
instanceId;
|
|
158
|
+
name = "RepoProvisionError";
|
|
159
|
+
};
|
|
160
|
+
var SAFE_INSTANCE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
161
|
+
async function provisionRepo(sandbox, opts) {
|
|
162
|
+
const { repoUrl, ref, instanceId } = opts;
|
|
163
|
+
if (!SAFE_INSTANCE_ID.test(instanceId)) {
|
|
164
|
+
throw new RepoProvisionError(
|
|
165
|
+
instanceId,
|
|
166
|
+
"invalid instanceId: must match [A-Za-z0-9._-] (no path traversal)"
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
if (ref.startsWith("-")) {
|
|
170
|
+
throw new RepoProvisionError(instanceId, `invalid ref: must not begin with '-' (got ${ref})`);
|
|
171
|
+
}
|
|
172
|
+
const clone = await sandbox.execute(
|
|
173
|
+
`git -c protocol.ext.allow=never clone --quiet -- ${shellEscapePosix(repoUrl)} ${shellEscapePosix(instanceId)}`
|
|
174
|
+
);
|
|
175
|
+
if (clone.exitCode !== 0) {
|
|
176
|
+
throw new RepoProvisionError(instanceId, `clone failed: ${clone.stderr.trim()}`);
|
|
177
|
+
}
|
|
178
|
+
const top = await sandbox.execute(
|
|
179
|
+
`git -C ${shellEscapePosix(instanceId)} rev-parse --show-toplevel`
|
|
180
|
+
);
|
|
181
|
+
if (top.exitCode !== 0) {
|
|
182
|
+
throw new RepoProvisionError(instanceId, `resolve repoDir failed: ${top.stderr.trim()}`);
|
|
183
|
+
}
|
|
184
|
+
const repoDir = top.stdout.trim();
|
|
185
|
+
const checkout = await sandbox.execute(
|
|
186
|
+
`git -C ${shellEscapePosix(repoDir)} checkout --quiet ${shellEscapePosix(ref)}`
|
|
187
|
+
);
|
|
188
|
+
if (checkout.exitCode !== 0) {
|
|
189
|
+
throw new RepoProvisionError(instanceId, `checkout ${ref} failed: ${checkout.stderr.trim()}`);
|
|
190
|
+
}
|
|
191
|
+
return { repoDir };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export { LocalSandbox, RepoProvisionError, SandboxBackend, SandboxNotAvailableError, SandboxSecurityError, provisionRepo };
|
|
127
195
|
//# sourceMappingURL=index.js.map
|
|
128
196
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/sandbox/types.ts","../../src/sandbox/local-sandbox.ts"],"names":["fsWriteFile"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Sandbox backend protocol — pluggable execution environment for agent tools.\n *\n * Per ADR D1: only 2 abstract methods (`execute` + `uploadFile`). All\n * higher-level operations are derived on the base class. New backends\n * (Docker, Firecracker, E2B) only implement those 2 methods.\n *\n * @public\n */\n\nexport interface ExecuteResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n timedOut: boolean;\n}\n\nexport interface SandboxConfig {\n workDir?: string;\n timeoutMs?: number;\n maxOutputBytes?: number;\n}\n\nexport class SandboxSecurityError extends Error {\n readonly code = \"sandbox_security\" as const;\n constructor(message: string) {\n super(message);\n this.name = \"SandboxSecurityError\";\n }\n}\n\nexport class SandboxNotAvailableError extends Error {\n readonly code = \"sandbox_not_available\" as const;\n constructor(message: string) {\n super(message);\n this.name = \"SandboxNotAvailableError\";\n }\n}\n\nconst SHELL_METACHARACTERS = /[;&|`$(){}]/;\n\nexport abstract class SandboxBackend {\n protected config: SandboxConfig;\n\n constructor(config: SandboxConfig = {}) {\n this.config = {\n workDir: config.workDir ?? \"/tmp\",\n timeoutMs: config.timeoutMs ?? 30_000,\n maxOutputBytes: config.maxOutputBytes ?? 5 * 1024 * 1024,\n };\n }\n\n abstract execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult>;\n\n abstract uploadFile(path: string, content: string | Buffer): Promise<void>;\n\n async readFile(path: string): Promise<string> {\n const result = await this.execute(`cat ${this.shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`readFile failed: ${result.stderr}`);\n }\n return result.stdout;\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n await this.uploadFile(path, content);\n }\n\n async glob(pattern: string, cwd?: string): Promise<string[]> {\n const dir = cwd ?? this.config.workDir ?? \".\";\n const result = await this.execute(\n `find ${this.shellEscape(dir)} -name ${this.shellEscape(pattern)} -type f 2>/dev/null`,\n );\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async grep(pattern: string, path?: string): Promise<string[]> {\n const target = path ?? \".\";\n const result = await this.execute(\n `grep -rn ${this.shellEscape(pattern)} ${this.shellEscape(target)} 2>/dev/null`,\n );\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async listDir(path: string): Promise<string[]> {\n const result = await this.execute(`ls -1 ${this.shellEscape(path)}`);\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n protected validateCommand(command: string): void {\n if (SHELL_METACHARACTERS.test(command)) {\n throw new SandboxSecurityError(\n `Command contains shell metacharacters: ${command.slice(0, 80)}`,\n );\n }\n }\n\n protected truncateOutput(output: string): string {\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n if (Buffer.byteLength(output) > max) {\n return `${output.slice(0, max)}\\n...(truncated)`;\n }\n return output;\n }\n\n private shellEscape(arg: string): string {\n return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n }\n}\n","/**\n * LocalSandbox — subprocess-based execution with NO isolation.\n *\n * Uses `execFile` with split args (NOT `exec` with string) per EC-1.\n * This is NOT a security boundary — only DockerSandbox provides isolation.\n *\n * @public\n */\n\nimport { execFile } from \"node:child_process\";\nimport { writeFile as fsWriteFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { type ExecuteResult, SandboxBackend, type SandboxConfig } from \"./types.js\";\n\nexport class LocalSandbox extends SandboxBackend {\n constructor(config: SandboxConfig = {}) {\n super(config);\n }\n\n async execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult> {\n const timeout = opts?.timeoutMs ?? this.config.timeoutMs ?? 30_000;\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n\n return new Promise<ExecuteResult>((resolve) => {\n const child = execFile(\n \"/bin/sh\",\n [\"-c\", command],\n {\n cwd: this.config.workDir,\n timeout,\n maxBuffer: max,\n encoding: \"utf-8\",\n },\n (error, stdout, stderr) => {\n resolve(this.buildResult(error, stdout ?? \"\", stderr ?? \"\"));\n },\n );\n\n // Safety: if child somehow doesn't callback\n child.on(\"error\", () => {\n resolve({ stdout: \"\", stderr: \"spawn error\", exitCode: 1, timedOut: false });\n });\n });\n }\n\n private buildResult(error: Error | null, stdout: string, stderr: string): ExecuteResult {\n const timedOut = error !== null && \"killed\" in error && (error as { killed: boolean }).killed;\n return {\n stdout: this.truncateOutput(stdout),\n stderr: this.truncateOutput(stderr),\n exitCode: timedOut ? 124 : error ? 1 : 0,\n timedOut,\n };\n }\n\n async uploadFile(path: string, content: string | Buffer): Promise<void> {\n const fullPath = path.startsWith(\"/\") ? path : `${this.config.workDir}/${path}`;\n await mkdir(dirname(fullPath), { recursive: true });\n await fsWriteFile(fullPath, content, \"utf-8\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/sandbox/shell-escape.ts","../../src/sandbox/types.ts","../../src/sandbox/local-sandbox.ts","../../src/errors.ts","../../src/sandbox/provision.ts"],"names":["fsWriteFile"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * POSIX shell escaping for values interpolated into a `SandboxBackend.execute`\n * command string. `execute` runs via `/bin/sh -c`, so any untrusted value\n * (repo URL, ref, path) MUST be quoted to prevent command injection.\n *\n * @internal\n */\n\n/** Wrap `arg` in single quotes, escaping embedded single quotes (`'\\''`). */\nexport function shellEscapePosix(arg: string): string {\n return `'${arg.replace(/'/g, \"'\\\\''\")}'`;\n}\n","/**\n * Sandbox backend protocol — pluggable execution environment for agent tools.\n *\n * Per ADR D1: only 2 abstract methods (`execute` + `uploadFile`). All\n * higher-level operations are derived on the base class. New backends\n * (Docker, Firecracker, E2B) only implement those 2 methods.\n *\n * @public\n */\n\nimport { shellEscapePosix } from \"./shell-escape.js\";\n\nexport interface ExecuteResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n timedOut: boolean;\n}\n\nexport interface SandboxConfig {\n workDir?: string;\n timeoutMs?: number;\n maxOutputBytes?: number;\n}\n\nexport class SandboxSecurityError extends Error {\n readonly code = \"sandbox_security\" as const;\n constructor(message: string) {\n super(message);\n this.name = \"SandboxSecurityError\";\n }\n}\n\nexport class SandboxNotAvailableError extends Error {\n readonly code = \"sandbox_not_available\" as const;\n constructor(message: string) {\n super(message);\n this.name = \"SandboxNotAvailableError\";\n }\n}\n\nconst SHELL_METACHARACTERS = /[;&|`$(){}]/;\n\nexport abstract class SandboxBackend {\n protected config: SandboxConfig;\n\n constructor(config: SandboxConfig = {}) {\n this.config = {\n workDir: config.workDir ?? \"/tmp\",\n timeoutMs: config.timeoutMs ?? 30_000,\n maxOutputBytes: config.maxOutputBytes ?? 5 * 1024 * 1024,\n };\n }\n\n abstract execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult>;\n\n abstract uploadFile(path: string, content: string | Buffer): Promise<void>;\n\n async readFile(path: string): Promise<string> {\n const result = await this.execute(`cat ${this.shellEscape(path)}`);\n if (result.exitCode !== 0) {\n throw new Error(`readFile failed: ${result.stderr}`);\n }\n return result.stdout;\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n await this.uploadFile(path, content);\n }\n\n async glob(pattern: string, cwd?: string): Promise<string[]> {\n const dir = cwd ?? this.config.workDir ?? \".\";\n const result = await this.execute(\n `find ${this.shellEscape(dir)} -name ${this.shellEscape(pattern)} -type f 2>/dev/null`,\n );\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async grep(pattern: string, path?: string): Promise<string[]> {\n const target = path ?? \".\";\n const result = await this.execute(\n `grep -rn ${this.shellEscape(pattern)} ${this.shellEscape(target)} 2>/dev/null`,\n );\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n async listDir(path: string): Promise<string[]> {\n const result = await this.execute(`ls -1 ${this.shellEscape(path)}`);\n if (result.exitCode !== 0) return [];\n return result.stdout.trim().split(\"\\n\").filter(Boolean);\n }\n\n protected validateCommand(command: string): void {\n if (SHELL_METACHARACTERS.test(command)) {\n throw new SandboxSecurityError(\n `Command contains shell metacharacters: ${command.slice(0, 80)}`,\n );\n }\n }\n\n protected truncateOutput(output: string): string {\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n if (Buffer.byteLength(output) > max) {\n return `${output.slice(0, max)}\\n...(truncated)`;\n }\n return output;\n }\n\n private shellEscape(arg: string): string {\n return shellEscapePosix(arg);\n }\n}\n","/**\n * LocalSandbox — subprocess-based execution with NO isolation.\n *\n * Uses `execFile` with split args (NOT `exec` with string) per EC-1.\n * This is NOT a security boundary — only DockerSandbox provides isolation.\n *\n * @public\n */\n\nimport { execFile } from \"node:child_process\";\nimport { writeFile as fsWriteFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { type ExecuteResult, SandboxBackend, type SandboxConfig } from \"./types.js\";\n\nexport class LocalSandbox extends SandboxBackend {\n constructor(config: SandboxConfig = {}) {\n super(config);\n }\n\n async execute(command: string, opts?: { timeoutMs?: number }): Promise<ExecuteResult> {\n const timeout = opts?.timeoutMs ?? this.config.timeoutMs ?? 30_000;\n const max = this.config.maxOutputBytes ?? 5 * 1024 * 1024;\n\n return new Promise<ExecuteResult>((resolve) => {\n const child = execFile(\n \"/bin/sh\",\n [\"-c\", command],\n {\n cwd: this.config.workDir,\n timeout,\n maxBuffer: max,\n encoding: \"utf-8\",\n },\n (error, stdout, stderr) => {\n resolve(this.buildResult(error, stdout ?? \"\", stderr ?? \"\"));\n },\n );\n\n // Safety: if child somehow doesn't callback\n child.on(\"error\", () => {\n resolve({ stdout: \"\", stderr: \"spawn error\", exitCode: 1, timedOut: false });\n });\n });\n }\n\n private buildResult(error: Error | null, stdout: string, stderr: string): ExecuteResult {\n const timedOut = error !== null && \"killed\" in error && (error as { killed: boolean }).killed;\n return {\n stdout: this.truncateOutput(stdout),\n stderr: this.truncateOutput(stderr),\n exitCode: timedOut ? 124 : error ? 1 : 0,\n timedOut,\n };\n }\n\n async uploadFile(path: string, content: string | Buffer): Promise<void> {\n const fullPath = path.startsWith(\"/\") ? path : `${this.config.workDir}/${path}`;\n await mkdir(dirname(fullPath), { recursive: true });\n await fsWriteFile(fullPath, content, \"utf-8\");\n }\n}\n","import { defaultRetriableForCode } from \"./internal/default-retriable.js\";\nimport { redactSecrets } from \"./internal/security/redact.js\";\nimport type { RunOperation } from \"./types/run.js\";\n\n/**\n * Finite, machine-readable error codes for provider-originated errors\n * (ADR D66). Consumers can `switch (err.metadata?.code)` exhaustively\n * — adding a new variant is an explicit decision + test coverage.\n *\n * @public\n */\nexport type ErrorCode =\n | \"rate_limit\"\n | \"auth_failed\"\n | \"invalid_request\"\n | \"timeout\"\n | \"server_error\"\n | \"context_too_long\"\n | \"content_filtered\"\n | \"model_unavailable\"\n | \"network\"\n | \"quota_exceeded\"\n | \"unknown\";\n\n/**\n * Codes used by {@link AgentRunError} (Production-Readiness #3, ADR D311).\n *\n * Superset of {@link ErrorCode} extended with codes that do NOT originate\n * from a provider HTTP response:\n *\n * - `quota_exceeded` — billing limit hit (provider 402 or signalled error)\n * - `tool_runtime_error` — custom tool handler threw inside dispatch\n * - `aborted` — caller's `AbortSignal` fired (Phase 4)\n * - `invalid_model` — model id rejected by provider (400 \"model not found\")\n * - `safety_blocked` — provider safety filter blocked req or resp\n * - `provider_unreachable` — DNS/TCP/timeout/5xx at transport boundary\n *\n * The `& {}` tail keeps the literal-union ergonomics (autocomplete) while\n * accepting any string for forward compatibility with constructor calls\n * that pass arbitrary code values (legacy callers).\n *\n * @public\n */\n/**\n * T1.1 — closed literal union for `AgentRunError.code`. The previous\n * `(string & {})` escape hatch let arbitrary strings slip into the type\n * surface and defeated exhaustive `switch (code)` discrimination. This is\n * the canonical closed form. `AgentRunErrorCode` is re-aliased below for\n * source-level back-compat.\n *\n * Adding a new code: append the literal here AND audit every `switch (err.code)`\n * in callers. Type-checker enforces the audit via the `default: assertNever(code)`\n * convention.\n *\n * @public\n */\nexport type KnownAgentRunErrorCode =\n | ErrorCode\n | \"quota_exceeded\"\n | \"tool_runtime_error\"\n | \"aborted\"\n | \"invalid_model\"\n | \"safety_blocked\"\n | \"provider_unreachable\";\n\n/**\n * Back-compat alias of {@link KnownAgentRunErrorCode}. Pre-T1.1 callers that\n * imported `AgentRunErrorCode` keep working; new code SHOULD prefer\n * `KnownAgentRunErrorCode` to make the closed-union intent explicit.\n *\n * @public\n */\nexport type AgentRunErrorCode = KnownAgentRunErrorCode;\n\n/** Snapshot of every known code at runtime — used by the boundary coercer. */\nconst KNOWN_AGENT_RUN_ERROR_CODES = new Set<string>([\n \"rate_limit\",\n \"auth_failed\",\n \"invalid_request\",\n \"timeout\",\n \"server_error\",\n \"context_too_long\",\n \"content_filtered\",\n \"model_unavailable\",\n \"network\",\n \"unknown\",\n \"quota_exceeded\",\n \"tool_runtime_error\",\n \"aborted\",\n \"invalid_model\",\n \"safety_blocked\",\n \"provider_unreachable\",\n]);\n\n/**\n * T1.1 boundary helper — coerce an arbitrary string (typically arriving from\n * a downstream `RunErrorDetail.code` or a deserialized cloud response) into a\n * `KnownAgentRunErrorCode`. Unknown strings collapse to `\"unknown\"` so the\n * closed type contract holds without forcing every caller to switch.\n *\n * @internal\n */\nexport function coerceToKnownAgentRunErrorCode(code: string | undefined): KnownAgentRunErrorCode {\n if (code !== undefined && KNOWN_AGENT_RUN_ERROR_CODES.has(code)) {\n return code as KnownAgentRunErrorCode;\n }\n return \"unknown\";\n}\n\n/**\n * Structured context for errors that originated from a provider HTTP\n * call (ADR D65). Lets callers retry with the right backoff (`retryAfter`),\n * surface actionable diagnostics (`provider`, `endpoint`), and inspect the\n * raw response body when needed (`raw`, capped at ~2KB by the mapper).\n *\n * @public\n */\nexport interface ErrorMetadata {\n /** Provider canonical name (e.g., `\"anthropic\"`, `\"openai\"`, `\"openrouter\"`, `\"gemini\"`). */\n provider: string;\n /** HTTP endpoint that failed (e.g., `\"/v1/messages\"`, `\"/v1/chat/completions\"`). */\n endpoint: string;\n /** Machine-readable error code (finite enum). */\n code: ErrorCode;\n /** HTTP status code if applicable. */\n statusCode?: number;\n /** Seconds to wait before retry, per provider's `retry-after` header (numeric form only). */\n retryAfter?: number;\n /** Raw response body for debugging (truncated to ~2KB by the mapper). */\n raw?: unknown;\n}\n\n/**\n * Base class for all errors thrown by `@theokit/sdk`.\n *\n * Use `isRetryable` to drive retry/backoff logic. `code` and `protoErrorCode`\n * are populated for server-originated errors when available. `metadata`\n * (ADR D65) carries structured `{ provider, endpoint, code, ... }` when\n * the error originated from a provider HTTP call.\n *\n * @public\n */\nexport class TheokitAgentError extends Error {\n override readonly name: string = \"TheokitAgentError\";\n readonly isRetryable: boolean;\n readonly code?: string;\n readonly protoErrorCode?: string;\n readonly metadata?: ErrorMetadata;\n\n constructor(\n message: string,\n options: {\n isRetryable?: boolean;\n code?: string;\n protoErrorCode?: string;\n cause?: unknown;\n metadata?: ErrorMetadata;\n } = {},\n ) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.isRetryable = options.isRetryable ?? false;\n if (options.code !== undefined) this.code = options.code;\n if (options.protoErrorCode !== undefined) this.protoErrorCode = options.protoErrorCode;\n if (options.metadata !== undefined) this.metadata = options.metadata;\n }\n}\n\n/**\n * Invalid API key, not logged in, insufficient permissions.\n *\n * @public\n */\nexport class AuthenticationError extends TheokitAgentError {\n override readonly name: string = \"AuthenticationError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: false });\n }\n}\n\n/**\n * Too many requests or usage limits exceeded.\n *\n * @public\n */\nexport class RateLimitError extends TheokitAgentError {\n override readonly name: string = \"RateLimitError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: true });\n }\n}\n\n/**\n * Invalid model, bad request parameters, malformed options.\n *\n * @public\n */\nexport class ConfigurationError extends TheokitAgentError {\n override readonly name: string = \"ConfigurationError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: false });\n }\n}\n\n/**\n * Thrown when creating a cloud agent for a repo whose SCM provider is not\n * connected. Use `helpUrl` to point the user at the right reconnect flow.\n *\n * @public\n */\nexport class IntegrationNotConnectedError extends ConfigurationError {\n override readonly name: string = \"IntegrationNotConnectedError\";\n readonly provider: string;\n readonly helpUrl: string;\n\n constructor(\n message: string,\n options: {\n provider: string;\n helpUrl: string;\n code?: string;\n cause?: unknown;\n metadata?: ErrorMetadata;\n },\n ) {\n super(message, options);\n this.provider = options.provider;\n this.helpUrl = options.helpUrl;\n }\n}\n\n/**\n * Service unavailable, timeout, transport-level failure.\n *\n * @public\n */\nexport class NetworkError extends TheokitAgentError {\n override readonly name: string = \"NetworkError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: true });\n }\n}\n\n/**\n * Catch-all for unclassified server or runtime errors.\n *\n * @public\n */\nexport class UnknownAgentError extends TheokitAgentError {\n override readonly name: string = \"UnknownAgentError\";\n\n constructor(\n message: string,\n options: { code?: string; cause?: unknown; metadata?: ErrorMetadata } = {},\n ) {\n super(message, { ...options, isRetryable: false });\n }\n}\n\n/**\n * Thrown by `Agent.prompt` (and helpers that go through `run.wait()`) when\n * the option `{ throwOnError: true }` is set and the run terminates with\n * `status: 'error'`. Carries the structured `RunResult.error` fields so\n * callers can `catch` once and branch on `code` / `provider` instead of\n * unwrapping the run.\n *\n * Extends {@link TheokitAgentError} per ADR D65 — no new hierarchy.\n *\n * @example\n * try {\n * await Agent.prompt(msg, { apiKey, model, throwOnError: true });\n * } catch (err) {\n * if (err instanceof AgentRunError && err.code === 'auth_failed') {\n * // bad key\n * }\n * }\n *\n * @public\n */\nexport class AgentRunError extends TheokitAgentError {\n override readonly name: string = \"AgentRunError\";\n readonly provider?: string;\n readonly raw?: string;\n /** Provider's request id (`x-request-id` / `request-id` header). Useful for support tickets. */\n readonly requestId?: string;\n /** SDK conversation id this error was raised inside. */\n readonly conversationId?: string;\n\n constructor(\n message: string,\n options: {\n code: AgentRunErrorCode;\n provider?: string;\n raw?: string;\n requestId?: string;\n conversationId?: string;\n retriable?: boolean;\n cause?: unknown;\n metadata?: ErrorMetadata;\n },\n ) {\n super(message, {\n code: options.code,\n cause: options.cause,\n metadata: options.metadata,\n // D311: most AgentRunErrors are not retriable (auth, validation, abort).\n // Provider mappers (D314) override per-status — explicit `retriable` wins\n // over the implicit default when supplied.\n isRetryable: options.retriable ?? defaultRetriableForCode(options.code),\n });\n if (options.provider !== undefined) this.provider = options.provider;\n if (options.raw !== undefined) this.raw = options.raw;\n if (options.requestId !== undefined) this.requestId = options.requestId;\n if (options.conversationId !== undefined) this.conversationId = options.conversationId;\n }\n\n /**\n * Production-Readiness #3 (ADR D311): alias for `isRetryable` exposed as\n * `retriable` to match the handoff contract. Future v2 will deprecate\n * `isRetryable` in favor of this.\n */\n get retriable(): boolean {\n return this.isRetryable;\n }\n\n /**\n * D312: provider's `Retry-After` header in **milliseconds**. Mappers store\n * the header value (seconds) in `metadata.retryAfter`; this getter\n * multiplies by 1000 so the result composes with `Date.now()`/`setTimeout`.\n *\n * Returns `undefined` when no hint was provided. `0` is a legitimate value\n * — use `=== undefined` check rather than truthy check.\n */\n get retryAfterMs(): number | undefined {\n if (this.metadata?.retryAfter === undefined) return undefined;\n return this.metadata.retryAfter * 1000;\n }\n\n /**\n * D313 + T1.5: alias for `metadata.raw`. Provider response body for\n * debugging. T1.5 wraps the value in `redactSecrets` at the getter\n * boundary so secret-shaped substrings (`sk-...`, Bearer JWTs, etc.) are\n * stripped before reaching the caller. Available but NEVER serialized\n * into `.message` (anti-leak invariant).\n */\n get providerError(): unknown {\n const raw = this.metadata?.raw;\n if (raw === undefined) return undefined;\n if (typeof raw === \"string\") return redactSecrets(raw);\n // Non-string raw (object/buffer) — stringify then redact.\n try {\n return redactSecrets(JSON.stringify(raw));\n } catch {\n return redactSecrets(String(raw));\n }\n }\n\n /**\n * T1.5 — sanitized JSON form. `metadata.raw` is OMITTED by default; opt\n * in via `THEOKIT_DEBUG_RAW_ERRORS=1` to surface the (redacted) raw\n * payload for diagnostics. Every other field stays accessible.\n *\n * The single env-var gate is read each call so operators can toggle at\n * runtime without restarting the process.\n */\n toJSON(): Record<string, unknown> {\n const json: Record<string, unknown> = {\n name: this.name,\n message: this.message,\n isRetryable: this.isRetryable,\n };\n addOptionalFields(json, this);\n const safeMeta = sanitizeMetadata(this.metadata);\n if (safeMeta !== undefined) json.metadata = safeMeta;\n return json;\n }\n}\n\nfunction addOptionalFields(json: Record<string, unknown>, err: AgentRunError): void {\n if (err.code !== undefined) json.code = err.code;\n if (err.provider !== undefined) json.provider = err.provider;\n if (err.requestId !== undefined) json.requestId = err.requestId;\n if (err.conversationId !== undefined) json.conversationId = err.conversationId;\n if (err.raw !== undefined) json.raw = redactSecrets(err.raw);\n}\n\nfunction sanitizeMetadata(meta: ErrorMetadata | undefined): ErrorMetadata | undefined {\n if (meta === undefined) return undefined;\n const { raw, ...rest } = meta;\n const debugRaw = process.env.THEOKIT_DEBUG_RAW_ERRORS === \"1\";\n if (debugRaw && raw !== undefined) {\n const redactedRaw =\n typeof raw === \"string\" ? redactSecrets(raw) : redactSecrets(safeStringify(raw));\n return { ...rest, raw: redactedRaw } as ErrorMetadata;\n }\n return rest as ErrorMetadata;\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Is this error transient (worth retrying)?\n *\n * Returns the SDK's own retryability verdict: every {@link TheokitAgentError}\n * subclass computes `isRetryable` at construction (rate-limit / network /\n * credential-pool-exhausted are retryable; auth / configuration / unsupported\n * are not), so this predicate is a single source of truth rather than a\n * re-derivation. Non-SDK errors return `false` conservatively — wrap a foreign\n * error in the appropriate SDK error first if you want it considered transient.\n * It never inspects `err.message`.\n *\n * @example\n * try {\n * await agent.send(message, { throwOnError: true });\n * } catch (err) {\n * if (isTransientError(err)) return retryWithBackoff();\n * throw err;\n * }\n *\n * @public\n */\nexport function isTransientError(err: unknown): boolean {\n return err instanceof TheokitAgentError && err.isRetryable === true;\n}\n\n/**\n * Thrown when a {@link Run} or agent operation is not available on the current\n * runtime. Check first with `run.supports(operation)`.\n *\n * Extends {@link TheokitAgentError} (so error-catching code that branches on\n * `instanceof TheokitAgentError` continues to work) but is never retryable —\n * an unsupported operation will not become supported on retry.\n *\n * @public\n */\nexport class UnsupportedRunOperationError extends TheokitAgentError {\n override readonly name: string = \"UnsupportedRunOperationError\";\n readonly operation: RunOperation;\n\n constructor(\n message: string,\n operation: RunOperation,\n options: { code?: string; cause?: unknown } = {},\n ) {\n super(message, {\n ...options,\n isRetryable: false,\n code: options.code ?? \"unsupported_run_operation\",\n });\n this.operation = operation;\n }\n}\n\n/**\n * Thrown when every credential in a per-provider pool is in cooldown\n * and no healthy key is available (ADR D133). The caller's\n * {@link import(\"./internal/llm/fallback-client.js\").FallbackLlmClient}\n * catches this and tries the next provider in the fallback chain.\n *\n * `metadata.nextRetryAt` (epoch ms) tells callers when the soonest\n * pool entry resumes — useful for manual retry scheduling.\n *\n * @public\n */\nexport class CredentialPoolExhaustedError extends TheokitAgentError {\n override readonly name: string = \"CredentialPoolExhaustedError\";\n readonly provider: string;\n readonly nextRetryAt: number | undefined;\n\n constructor(\n message: string,\n options: {\n provider: string;\n nextRetryAt?: number;\n code?: string;\n cause?: unknown;\n metadata?: ErrorMetadata;\n },\n ) {\n super(message, {\n ...options,\n isRetryable: true,\n code: options.code ?? \"credential_pool_exhausted\",\n });\n this.provider = options.provider;\n this.nextRetryAt = options.nextRetryAt;\n }\n}\n\n/**\n * Finite error codes specific to memory adapter operations (ADR D141).\n *\n * @public\n */\nexport type MemoryAdapterErrorCode =\n | \"auth_failed\"\n | \"rate_limited\"\n | \"not_found\"\n | \"network\"\n | \"invalid_input\"\n | \"unknown\";\n\n/**\n * Error raised by `@theokit-memory-*` adapters. Carries `adapterId`\n * so callers can branch on which provider failed (ADR D141).\n *\n * @public\n */\nexport class MemoryAdapterError extends TheokitAgentError {\n override readonly name: string = \"MemoryAdapterError\";\n readonly adapterId: string;\n\n constructor(\n message: string,\n options: {\n adapterId: string;\n code: MemoryAdapterErrorCode;\n cause?: unknown;\n metadata?: ErrorMetadata;\n },\n ) {\n super(message, {\n isRetryable: options.code === \"rate_limited\" || options.code === \"network\",\n code: options.code,\n ...(options.cause !== undefined ? { cause: options.cause } : {}),\n ...(options.metadata !== undefined ? { metadata: options.metadata } : {}),\n });\n this.adapterId = options.adapterId;\n }\n}\n\n/**\n * Thrown when a user-supplied task ID violates the grammar\n * `^[a-z0-9][a-z0-9_-]*$` (D368) OR starts with a reserved adapter\n * prefix (`wf-` / `b-` / `cron-`, EC-5).\n *\n * @public\n */\nexport class InvalidTaskIdError extends TheokitAgentError {\n override readonly name: string = \"InvalidTaskIdError\";\n readonly taskId: string;\n\n constructor(message: string, taskId: string, options: { cause?: unknown } = {}) {\n super(message, {\n ...options,\n isRetryable: false,\n code: \"invalid_task_id\",\n });\n this.taskId = taskId;\n }\n}\n\n/**\n * Thrown when `Task.subscribe(id)` is called for a task that has been\n * evicted, never submitted, or evicted after retention (D373).\n *\n * @public\n */\nexport class TaskNotFoundError extends TheokitAgentError {\n override readonly name: string = \"TaskNotFoundError\";\n readonly taskId: string;\n\n constructor(taskId: string, options: { cause?: unknown } = {}) {\n super(`Task not found: ${taskId}`, {\n ...options,\n isRetryable: false,\n code: \"task_not_found\",\n });\n this.taskId = taskId;\n }\n}\n\n/**\n * Thrown when `CloudAgent` is asked to wrap a task (D370). Cloud\n * task observability is deferred until Theo PaaS GA.\n *\n * @public\n */\nexport class UnsupportedTaskOperationError extends TheokitAgentError {\n override readonly name: string = \"UnsupportedTaskOperationError\";\n readonly operation: string;\n\n constructor(operation: string, options: { cause?: unknown } = {}) {\n super(\n `Task operation \"${operation}\" is not supported on CloudAgent (pre-release; see ADR D370)`,\n {\n ...options,\n isRetryable: false,\n code: \"task_op_unsupported\",\n },\n );\n this.operation = operation;\n }\n}\n\n/**\n * Thrown by `Budget` enforcement (ADR D386) when a `mode: \"block\"`\n * budget would be exceeded by the upcoming LLM call. Caller pega\n * tipado para retry-after-window-reset or surface to the user.\n *\n * @public\n */\nexport class BudgetExceededError extends TheokitAgentError {\n override readonly name: string = \"BudgetExceededError\";\n readonly budgetName: string;\n readonly window: import(\"./types/budget.js\").BudgetWindow;\n readonly spentUsd: number;\n readonly limitUsd: number;\n readonly mode: import(\"./types/budget.js\").BudgetMode;\n\n constructor(args: {\n budgetName: string;\n window: import(\"./types/budget.js\").BudgetWindow;\n spentUsd: number;\n limitUsd: number;\n mode: import(\"./types/budget.js\").BudgetMode;\n cause?: unknown;\n }) {\n super(\n `Budget \"${args.budgetName}\" exceeded for window ${args.window}: spent $${args.spentUsd.toFixed(4)} > limit $${args.limitUsd.toFixed(4)}`,\n {\n ...(args.cause !== undefined ? { cause: args.cause } : {}),\n isRetryable: false,\n code: \"budget_exceeded\",\n },\n );\n this.budgetName = args.budgetName;\n this.window = args.window;\n this.spentUsd = args.spentUsd;\n this.limitUsd = args.limitUsd;\n this.mode = args.mode;\n }\n}\n\n/**\n * Thrown when `CloudAgent.send({ budget })` is invoked (D388). Cloud\n * budget surface waits for Theo PaaS GA.\n *\n * @public\n */\n/**\n * T1.6 — Thrown when a consumer calls `agent.send()` or any method\n * on an agent that has already been `dispose()`d. Pre-T1.6 this was\n * a generic `new Error(\"Agent has been disposed\")` — consumers\n * couldn't catch it without string-matching the message.\n *\n * @public\n */\nexport class AgentDisposedError extends TheokitAgentError {\n override readonly name: string = \"AgentDisposedError\";\n readonly agentId: string;\n\n constructor(agentId: string) {\n super(`Agent \"${agentId}\" has been disposed. Create a new agent or use Agent.resume().`, {\n isRetryable: false,\n code: \"agent_disposed\",\n });\n this.agentId = agentId;\n }\n}\n\nexport class UnsupportedBudgetOperationError extends TheokitAgentError {\n override readonly name: string = \"UnsupportedBudgetOperationError\";\n readonly operation: string;\n\n constructor(operation: string, options: { cause?: unknown } = {}) {\n super(\n `Budget operation \"${operation}\" is not supported on CloudAgent (pre-release; see ADR D388)`,\n {\n ...options,\n isRetryable: false,\n code: \"budget_op_unsupported\",\n },\n );\n this.operation = operation;\n }\n}\n","/**\n * M6-3 — portable repo provisioner for the eval harness.\n *\n * Clones a repository and checks out a ref into an isolated working dir, issuing\n * every git command through {@link SandboxBackend.execute} (ADR D2 — same code\n * runs on Local/Docker/E2B; never a direct `child_process` import). Promotes\n * theocode's `prepareRepo` (`swebench-provision.ts:37`) onto the SDK's sandbox\n * abstraction.\n *\n * referencia: knowledge-base/references/theocode-eval/lib/swebench-provision.ts:37\n * (clone+checkout), :13 (ProvisionError with instanceId).\n *\n * @public\n */\n\nimport { TheokitAgentError } from \"../errors.js\";\nimport { shellEscapePosix } from \"./shell-escape.js\";\nimport type { SandboxBackend } from \"./types.js\";\n\n/**\n * Raised when cloning or checking out a repo fails. Carries the `instanceId`\n * so a batch run can attribute the failure to the offending dataset row.\n */\nexport class RepoProvisionError extends TheokitAgentError {\n override readonly name = \"RepoProvisionError\";\n\n constructor(\n readonly instanceId: string,\n message: string,\n options: { cause?: unknown } = {},\n ) {\n super(`[${instanceId}] ${message}`, {\n code: \"repo_provision_failed\",\n isRetryable: false,\n ...(options.cause !== undefined ? { cause: options.cause } : {}),\n });\n }\n}\n\n/** Options for {@link provisionRepo}. */\nexport interface ProvisionRepoOptions {\n /**\n * Clonable repo URL or local path. SECURITY: when this comes from an\n * untrusted dataset, the value is passed to `git clone` after a `--`\n * end-of-options terminator (no flag injection) and with the `ext::`\n * transport disabled (no arbitrary-command transport).\n */\n readonly repoUrl: string;\n /** Branch, tag, or commit SHA to check out. Rejected if it begins with `-`. */\n readonly ref: string;\n /**\n * Unique id for this row — names the target dir and any error. Validated to\n * `[A-Za-z0-9._-]` (no path traversal) since it becomes a directory name.\n */\n readonly instanceId: string;\n}\n\n/**\n * Reject ids that would escape the workdir or be parsed as a git flag. Must\n * start with an alphanumeric (blocks `.`, `..`, `-foo`, leading-dot names) and\n * thereafter allow only `[A-Za-z0-9._-]`.\n */\nconst SAFE_INSTANCE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\n/**\n * Clone `repoUrl` into `<sandbox workdir>/<instanceId>` and check out `ref`.\n * Returns the absolute `repoDir` (resolved via `git rev-parse --show-toplevel`,\n * which is portable across backends). Throws {@link RepoProvisionError} naming\n * the `instanceId` when clone or checkout exits non-zero.\n */\nexport async function provisionRepo(\n sandbox: SandboxBackend,\n opts: ProvisionRepoOptions,\n): Promise<{ repoDir: string }> {\n const { repoUrl, ref, instanceId } = opts;\n\n // Validate untrusted-derivable inputs before they reach git/the shell.\n if (!SAFE_INSTANCE_ID.test(instanceId)) {\n throw new RepoProvisionError(\n instanceId,\n \"invalid instanceId: must match [A-Za-z0-9._-] (no path traversal)\",\n );\n }\n if (ref.startsWith(\"-\")) {\n throw new RepoProvisionError(instanceId, `invalid ref: must not begin with '-' (got ${ref})`);\n }\n\n // `--` terminates options (no `--upload-pack=` flag injection); `protocol.ext.allow=never`\n // blocks the `ext::` arbitrary-command transport. `file`/`https` stay allowed.\n const clone = await sandbox.execute(\n `git -c protocol.ext.allow=never clone --quiet -- ${shellEscapePosix(repoUrl)} ${shellEscapePosix(instanceId)}`,\n );\n if (clone.exitCode !== 0) {\n throw new RepoProvisionError(instanceId, `clone failed: ${clone.stderr.trim()}`);\n }\n\n const top = await sandbox.execute(\n `git -C ${shellEscapePosix(instanceId)} rev-parse --show-toplevel`,\n );\n if (top.exitCode !== 0) {\n throw new RepoProvisionError(instanceId, `resolve repoDir failed: ${top.stderr.trim()}`);\n }\n const repoDir = top.stdout.trim();\n\n // `ref` is validated above not to begin with `-`, so it cannot be parsed as a flag.\n const checkout = await sandbox.execute(\n `git -C ${shellEscapePosix(repoDir)} checkout --quiet ${shellEscapePosix(ref)}`,\n );\n if (checkout.exitCode !== 0) {\n throw new RepoProvisionError(instanceId, `checkout ${ref} failed: ${checkout.stderr.trim()}`);\n }\n\n return { repoDir };\n}\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M6-3 — portable repo provisioner for the eval harness.
|
|
3
|
+
*
|
|
4
|
+
* Clones a repository and checks out a ref into an isolated working dir, issuing
|
|
5
|
+
* every git command through {@link SandboxBackend.execute} (ADR D2 — same code
|
|
6
|
+
* runs on Local/Docker/E2B; never a direct `child_process` import). Promotes
|
|
7
|
+
* theocode's `prepareRepo` (`swebench-provision.ts:37`) onto the SDK's sandbox
|
|
8
|
+
* abstraction.
|
|
9
|
+
*
|
|
10
|
+
* referencia: knowledge-base/references/theocode-eval/lib/swebench-provision.ts:37
|
|
11
|
+
* (clone+checkout), :13 (ProvisionError with instanceId).
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
import { TheokitAgentError } from "../errors.js";
|
|
16
|
+
import type { SandboxBackend } from "./types.js";
|
|
17
|
+
/**
|
|
18
|
+
* Raised when cloning or checking out a repo fails. Carries the `instanceId`
|
|
19
|
+
* so a batch run can attribute the failure to the offending dataset row.
|
|
20
|
+
*/
|
|
21
|
+
export declare class RepoProvisionError extends TheokitAgentError {
|
|
22
|
+
readonly instanceId: string;
|
|
23
|
+
readonly name = "RepoProvisionError";
|
|
24
|
+
constructor(instanceId: string, message: string, options?: {
|
|
25
|
+
cause?: unknown;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/** Options for {@link provisionRepo}. */
|
|
29
|
+
export interface ProvisionRepoOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Clonable repo URL or local path. SECURITY: when this comes from an
|
|
32
|
+
* untrusted dataset, the value is passed to `git clone` after a `--`
|
|
33
|
+
* end-of-options terminator (no flag injection) and with the `ext::`
|
|
34
|
+
* transport disabled (no arbitrary-command transport).
|
|
35
|
+
*/
|
|
36
|
+
readonly repoUrl: string;
|
|
37
|
+
/** Branch, tag, or commit SHA to check out. Rejected if it begins with `-`. */
|
|
38
|
+
readonly ref: string;
|
|
39
|
+
/**
|
|
40
|
+
* Unique id for this row — names the target dir and any error. Validated to
|
|
41
|
+
* `[A-Za-z0-9._-]` (no path traversal) since it becomes a directory name.
|
|
42
|
+
*/
|
|
43
|
+
readonly instanceId: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Clone `repoUrl` into `<sandbox workdir>/<instanceId>` and check out `ref`.
|
|
47
|
+
* Returns the absolute `repoDir` (resolved via `git rev-parse --show-toplevel`,
|
|
48
|
+
* which is portable across backends). Throws {@link RepoProvisionError} naming
|
|
49
|
+
* the `instanceId` when clone or checkout exits non-zero.
|
|
50
|
+
*/
|
|
51
|
+
export declare function provisionRepo(sandbox: SandboxBackend, opts: ProvisionRepoOptions): Promise<{
|
|
52
|
+
repoDir: string;
|
|
53
|
+
}>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M6-3 — portable repo provisioner for the eval harness.
|
|
3
|
+
*
|
|
4
|
+
* Clones a repository and checks out a ref into an isolated working dir, issuing
|
|
5
|
+
* every git command through {@link SandboxBackend.execute} (ADR D2 — same code
|
|
6
|
+
* runs on Local/Docker/E2B; never a direct `child_process` import). Promotes
|
|
7
|
+
* theocode's `prepareRepo` (`swebench-provision.ts:37`) onto the SDK's sandbox
|
|
8
|
+
* abstraction.
|
|
9
|
+
*
|
|
10
|
+
* referencia: knowledge-base/references/theocode-eval/lib/swebench-provision.ts:37
|
|
11
|
+
* (clone+checkout), :13 (ProvisionError with instanceId).
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
import { TheokitAgentError } from "../errors.js";
|
|
16
|
+
import type { SandboxBackend } from "./types.js";
|
|
17
|
+
/**
|
|
18
|
+
* Raised when cloning or checking out a repo fails. Carries the `instanceId`
|
|
19
|
+
* so a batch run can attribute the failure to the offending dataset row.
|
|
20
|
+
*/
|
|
21
|
+
export declare class RepoProvisionError extends TheokitAgentError {
|
|
22
|
+
readonly instanceId: string;
|
|
23
|
+
readonly name = "RepoProvisionError";
|
|
24
|
+
constructor(instanceId: string, message: string, options?: {
|
|
25
|
+
cause?: unknown;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/** Options for {@link provisionRepo}. */
|
|
29
|
+
export interface ProvisionRepoOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Clonable repo URL or local path. SECURITY: when this comes from an
|
|
32
|
+
* untrusted dataset, the value is passed to `git clone` after a `--`
|
|
33
|
+
* end-of-options terminator (no flag injection) and with the `ext::`
|
|
34
|
+
* transport disabled (no arbitrary-command transport).
|
|
35
|
+
*/
|
|
36
|
+
readonly repoUrl: string;
|
|
37
|
+
/** Branch, tag, or commit SHA to check out. Rejected if it begins with `-`. */
|
|
38
|
+
readonly ref: string;
|
|
39
|
+
/**
|
|
40
|
+
* Unique id for this row — names the target dir and any error. Validated to
|
|
41
|
+
* `[A-Za-z0-9._-]` (no path traversal) since it becomes a directory name.
|
|
42
|
+
*/
|
|
43
|
+
readonly instanceId: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Clone `repoUrl` into `<sandbox workdir>/<instanceId>` and check out `ref`.
|
|
47
|
+
* Returns the absolute `repoDir` (resolved via `git rev-parse --show-toplevel`,
|
|
48
|
+
* which is portable across backends). Throws {@link RepoProvisionError} naming
|
|
49
|
+
* the `instanceId` when clone or checkout exits non-zero.
|
|
50
|
+
*/
|
|
51
|
+
export declare function provisionRepo(sandbox: SandboxBackend, opts: ProvisionRepoOptions): Promise<{
|
|
52
|
+
repoDir: string;
|
|
53
|
+
}>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POSIX shell escaping for values interpolated into a `SandboxBackend.execute`
|
|
3
|
+
* command string. `execute` runs via `/bin/sh -c`, so any untrusted value
|
|
4
|
+
* (repo URL, ref, path) MUST be quoted to prevent command injection.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POSIX shell escaping for values interpolated into a `SandboxBackend.execute`
|
|
3
|
+
* command string. `execute` runs via `/bin/sh -c`, so any untrusted value
|
|
4
|
+
* (repo URL, ref, path) MUST be quoted to prevent command injection.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
package/dist/scorers.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { ZodType } from "zod";
|
|
16
16
|
import { type LlmJudgeOptions } from "./internal/scorers/llm-judge.js";
|
|
17
|
-
import type { NamedScorer } from "./types/eval.js";
|
|
17
|
+
import type { NamedScorer, VerifyGateOptions } from "./types/eval.js";
|
|
18
18
|
interface ExactMatchOptions {
|
|
19
19
|
/** Case-sensitive compare. Default: true. */
|
|
20
20
|
caseSensitive?: boolean;
|
|
@@ -70,6 +70,24 @@ export declare const Scorers: {
|
|
|
70
70
|
* code fences and prose-wrapped JSON — common LLM output shapes.
|
|
71
71
|
*/
|
|
72
72
|
llmJudge(opts: LlmJudgeOptions): NamedScorer;
|
|
73
|
+
/**
|
|
74
|
+
* Verify-gate scorer (M6-2): runs the project's tests in the provisioned
|
|
75
|
+
* repo via `SandboxBackend.execute` and scores `1` iff the command exits `0`,
|
|
76
|
+
* else `0` with the exit code + truncated stderr in `reason`. Grades the
|
|
77
|
+
* artifact captured by `captureArtifact` (D2 — rides `execute`, never a
|
|
78
|
+
* direct `child_process`).
|
|
79
|
+
*
|
|
80
|
+
* SECURITY: `command` is REQUIRED and the caller's builder owns shell-safety
|
|
81
|
+
* of the (potentially untrusted, dataset-derived) test identifiers. There is
|
|
82
|
+
* NO default that runs bare test names — that would interpolate untrusted
|
|
83
|
+
* `failToPass`/`passToPass` straight into a shell. `repoDir` is shell-escaped
|
|
84
|
+
* by the SDK; the test list is the builder's responsibility to render safely.
|
|
85
|
+
*
|
|
86
|
+
* PORTABILITY: the command is wrapped as `cd <repoDir> && <cmd>`, which
|
|
87
|
+
* assumes a shell-backed `SandboxBackend` (LocalSandbox/Docker). A backend
|
|
88
|
+
* that rejects shell metacharacters in `execute` is unsupported for this scorer.
|
|
89
|
+
*/
|
|
90
|
+
verifyGate(opts: VerifyGateOptions): NamedScorer;
|
|
73
91
|
jsonShape<T extends ZodType>(schema: T, opts?: JsonShapeOptions): NamedScorer;
|
|
74
92
|
};
|
|
75
93
|
export {};
|
package/dist/types/eval.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @public
|
|
10
10
|
*/
|
|
11
|
+
import type { SandboxBackend } from "../sandbox/types.js";
|
|
11
12
|
import type { SDKAgent } from "./agent.js";
|
|
12
13
|
/** Inferred `Agent.create` options shape — avoid cycling through `AgentOptions` directly. */
|
|
13
14
|
export type EvalAgentOptions = Parameters<typeof import("../agent.js").Agent.create>[0];
|
|
@@ -92,6 +93,43 @@ export interface EvalRowResult {
|
|
|
92
93
|
readonly tokensOut?: number;
|
|
93
94
|
readonly error?: string;
|
|
94
95
|
readonly metadata?: Record<string, unknown>;
|
|
96
|
+
/**
|
|
97
|
+
* Free-form taxonomy label assigned by `EvalRunOptions.classify` (M6-1).
|
|
98
|
+
* Persisted alongside the row when `persist` is set.
|
|
99
|
+
*/
|
|
100
|
+
readonly outcome?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Captured code change (M6-4): the working-tree `git diff` an agent produced
|
|
103
|
+
* and whether it reverse-applies cleanly. Produced by `captureArtifact`; the
|
|
104
|
+
* caller attaches it to the row (the runner does not set it automatically).
|
|
105
|
+
*/
|
|
106
|
+
readonly artifact?: {
|
|
107
|
+
readonly diff: string;
|
|
108
|
+
readonly applies: boolean;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Options for `Scorers.verifyGate` (M6-2) — grade a patch by running the
|
|
113
|
+
* project's tests in a provisioned repo and reading the exit code.
|
|
114
|
+
*/
|
|
115
|
+
export interface VerifyGateOptions {
|
|
116
|
+
/** Sandbox the test command runs in (D2 — Local/Docker/E2B). */
|
|
117
|
+
readonly sandbox: SandboxBackend;
|
|
118
|
+
/** Repo dir to run the tests from (typically `provisionRepo`'s `repoDir`). */
|
|
119
|
+
readonly repoDir: string;
|
|
120
|
+
/** SWE-bench tests that must flip to passing after the patch. */
|
|
121
|
+
readonly failToPass: readonly string[];
|
|
122
|
+
/** SWE-bench tests that must stay passing after the patch. */
|
|
123
|
+
readonly passToPass: readonly string[];
|
|
124
|
+
/**
|
|
125
|
+
* REQUIRED — builds the shell command from the combined test list, e.g.
|
|
126
|
+
* `(t) => "pytest " + t.join(" ")`. The builder OWNS shell-safety of the test
|
|
127
|
+
* identifiers: the SDK runs the returned string in a shell, so a builder that
|
|
128
|
+
* concatenates untrusted dataset test names without quoting is an injection
|
|
129
|
+
* vector (the SDK deliberately ships NO unsafe default that would run bare
|
|
130
|
+
* identifiers — see SECURITY note on `verifyGate`).
|
|
131
|
+
*/
|
|
132
|
+
readonly command: (tests: readonly string[]) => string;
|
|
95
133
|
}
|
|
96
134
|
/** Per-scorer breakdown computed across all rows. */
|
|
97
135
|
export interface PerScorerStats {
|
|
@@ -125,8 +163,41 @@ export interface EvalRun {
|
|
|
125
163
|
readonly rows: ReadonlyArray<EvalRowResult>;
|
|
126
164
|
readonly metadata?: Record<string, unknown>;
|
|
127
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Crash-durable persistence for `eval.run(...)` (M6-1). Each completed row is
|
|
168
|
+
* appended to `path` the instant it finishes (one `\n`-terminated JSON line),
|
|
169
|
+
* so a crashed multi-hour run resumes without re-paying completed work.
|
|
170
|
+
*
|
|
171
|
+
* Single-process contract: `appendFileSync` serializes writes within one Node
|
|
172
|
+
* process; do NOT point two concurrent processes at the same `path`.
|
|
173
|
+
*/
|
|
174
|
+
export interface EvalPersistOptions {
|
|
175
|
+
/** JSONL output path. Parent directories are created on first append. */
|
|
176
|
+
readonly path: string;
|
|
177
|
+
/**
|
|
178
|
+
* Stable identity of a row, used for resume. MUST be computed only from the
|
|
179
|
+
* fields available at resume-probe time (`index` / `input` / `expected` /
|
|
180
|
+
* `metadata`) — the type enforces this: at resume the key is computed from a
|
|
181
|
+
* probe row BEFORE the agent runs, so `output` / `scores` / `meanScore` are
|
|
182
|
+
* not yet known. Reading any non-durable field would silently break resume.
|
|
183
|
+
*/
|
|
184
|
+
readonly key: (row: Pick<EvalRowResult, "index" | "input" | "expected" | "metadata">) => string;
|
|
185
|
+
/**
|
|
186
|
+
* When `true`, rows whose `key` already appears in `path` with a SUCCESSFUL
|
|
187
|
+
* (no `error`) result are skipped — not re-executed and not re-emitted in
|
|
188
|
+
* `EvalRun.rows`. Failed rows are always retried.
|
|
189
|
+
*/
|
|
190
|
+
readonly resume?: boolean;
|
|
191
|
+
}
|
|
128
192
|
/** Per-call options for `eval.run(...)`. */
|
|
129
193
|
export interface EvalRunOptions {
|
|
130
194
|
/** Cancels pending rows; in-flight rows complete (D140 pattern). */
|
|
131
195
|
readonly signal?: AbortSignal;
|
|
196
|
+
/** Durable, resumable per-row persistence (M6-1). Absent → no file is written. */
|
|
197
|
+
readonly persist?: EvalPersistOptions;
|
|
198
|
+
/**
|
|
199
|
+
* Optional taxonomy classifier applied to each completed row; the result is
|
|
200
|
+
* stored on `EvalRowResult.outcome` and persisted (M6-1).
|
|
201
|
+
*/
|
|
202
|
+
readonly classify?: (row: EvalRowResult) => string;
|
|
132
203
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theokit/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "TypeScript SDK for the Theo agent harness — same surface, local or cloud.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/usetheo/theokit-sdk#readme",
|
|
@@ -138,6 +138,16 @@
|
|
|
138
138
|
"default": "./dist/retry.cjs"
|
|
139
139
|
}
|
|
140
140
|
},
|
|
141
|
+
"./persistence": {
|
|
142
|
+
"import": {
|
|
143
|
+
"types": "./dist/persistence.d.ts",
|
|
144
|
+
"default": "./dist/persistence.js"
|
|
145
|
+
},
|
|
146
|
+
"require": {
|
|
147
|
+
"types": "./dist/persistence.d.cts",
|
|
148
|
+
"default": "./dist/persistence.cjs"
|
|
149
|
+
}
|
|
150
|
+
},
|
|
141
151
|
"./task-store": {
|
|
142
152
|
"import": {
|
|
143
153
|
"types": "./dist/task-store.d.ts",
|