@theokit/sdk 2.0.1 → 2.1.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 +18 -0
- package/dist/a2a/index.cjs +232 -171
- package/dist/a2a/index.cjs.map +1 -1
- package/dist/a2a/index.js +232 -171
- package/dist/a2a/index.js.map +1 -1
- package/dist/concurrency.cjs +86 -0
- package/dist/concurrency.cjs.map +1 -0
- package/dist/concurrency.d.cts +13 -0
- package/dist/concurrency.d.ts +13 -0
- package/dist/concurrency.js +83 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/{cron-DFG9-W17.d.cts → cron-CSTqNZp9.d.cts} +1 -1
- package/dist/{cron-Bj8-Aq1O.d.ts → cron-Da6vF_2y.d.ts} +1 -1
- package/dist/cron.cjs +213 -169
- package/dist/cron.cjs.map +1 -1
- package/dist/cron.d.cts +1 -1
- package/dist/cron.d.ts +1 -1
- package/dist/cron.js +213 -169
- package/dist/cron.js.map +1 -1
- package/dist/{errors-DV9e0rcp.d.ts → errors--VP2qrGc.d.ts} +23 -1
- package/dist/{errors-ChqOmFH1.d.cts → errors-C9xkhNEF.d.cts} +23 -1
- package/dist/errors.cjs +17 -11
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +17 -12
- package/dist/errors.js.map +1 -1
- package/dist/eval.cjs +213 -169
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +213 -169
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +231 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +161 -119
- package/dist/index.d.ts +161 -119
- package/dist/index.js +231 -173
- package/dist/index.js.map +1 -1
- package/dist/internal/default-retriable.d.ts +1 -0
- package/dist/internal/persistence/index.cjs +75 -0
- package/dist/internal/persistence/index.cjs.map +1 -1
- package/dist/internal/persistence/index.d.cts +2 -0
- package/dist/internal/persistence/index.d.ts +2 -0
- package/dist/internal/persistence/index.js +74 -1
- package/dist/internal/persistence/index.js.map +1 -1
- package/dist/internal/persistence/sqlite-open.d.cts +47 -0
- package/dist/internal/persistence/sqlite-open.d.ts +47 -0
- package/dist/internal/providers/register-plugin-providers.d.ts +22 -0
- package/dist/internal/runtime/concurrency/map-with-concurrency.d.ts +28 -0
- package/dist/internal/runtime/retry/with-retry.d.ts +40 -0
- package/dist/internal/security/index.cjs +1 -0
- package/dist/internal/security/index.cjs.map +1 -1
- package/dist/internal/security/index.js +1 -0
- package/dist/internal/security/index.js.map +1 -1
- package/dist/path-safety.cjs +15 -0
- package/dist/path-safety.cjs.map +1 -1
- package/dist/path-safety.d.cts +1 -1
- package/dist/path-safety.d.ts +1 -1
- package/dist/path-safety.js +15 -1
- package/dist/path-safety.js.map +1 -1
- package/dist/retry.cjs +85 -0
- package/dist/retry.cjs.map +1 -0
- package/dist/retry.d.cts +9 -0
- package/dist/retry.d.ts +9 -0
- package/dist/retry.js +83 -0
- package/dist/retry.js.map +1 -0
- package/dist/server/errors-envelope.cjs +14 -12
- package/dist/server/errors-envelope.cjs.map +1 -1
- package/dist/server/errors-envelope.js +14 -12
- package/dist/server/errors-envelope.js.map +1 -1
- package/dist/subscription/index.cjs.map +1 -1
- package/dist/subscription/index.js.map +1 -1
- package/dist/task-store.cjs.map +1 -1
- package/dist/task-store.js.map +1 -1
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.js.map +1 -1
- package/package.json +21 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/subscription/define-subscription.ts","../../src/subscription/internal/sse-parser.ts","../../src/errors.ts","../../src/subscription/types.ts","../../src/subscription/theokit-subscribe.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @theokit/sdk — `defineSubscription` DSL (G8 public API).\n *\n * Per ADR D426 (AsyncGenerator handler + Zod input/output schemas).\n *\n * @public\n */\n\nimport type { SubscriptionCtx, SubscriptionDescriptor, TrackedEnvelope, ZodLike } from \"./types.js\";\n\n/**\n * Options accepted by {@link defineSubscription}.\n *\n * @public\n */\nexport interface DefineSubscriptionOptions<TInput, TOutput> {\n /** Optional explicit name. Falls back to filename derivation by the scanner. */\n name?: string;\n /** Zod schema validating subscription input. Required (typed RPC contract). */\n input: ZodLike<TInput>;\n /** Zod schema describing each output frame. Informational; not enforced per-frame. */\n output: ZodLike<TOutput>;\n /**\n * Handler that produces the subscription stream.\n *\n * Receives the validated input + {@link SubscriptionCtx}. Yielding a value\n * emits a plain output frame. Yielding a `tracked(id, payload)` envelope\n * additionally advertises a resume token to the client so reconnects can\n * resume from that point.\n */\n handler: (\n input: TInput,\n ctx: SubscriptionCtx,\n ) => AsyncGenerator<TOutput | TrackedEnvelope<TOutput>, void, void>;\n}\n\n/**\n * Define a typed subscription. Pair with `Theokit.subscribe(name, input)`\n * on the client side.\n *\n * Per ADR D426 — handler is `AsyncGenerator<TOutput | TrackedEnvelope<TOutput>>`.\n * The runtime validates input via `opts.input.safeParse` BEFORE invoking the\n * handler; failure throws {@link import('./types.js').SubscriptionInputError}.\n *\n * @example\n * ```ts\n * import { defineSubscription } from \"@theokit/sdk\";\n * import { z } from \"zod\";\n *\n * export default defineSubscription({\n * input: z.object({ topic: z.string() }),\n * output: z.object({ message: z.string(), ts: z.number() }),\n * async *handler(input, ctx) {\n * let cursor = ctx.lastEventId ?? \"0\";\n * while (!ctx.signal.aborted) {\n * const msgs = await fetchMessagesSince(input.topic, cursor);\n * for (const m of msgs) {\n * cursor = m.id;\n * yield ctx.tracked(m.id, { message: m.body, ts: m.ts });\n * }\n * await sleep(1000);\n * }\n * },\n * });\n * ```\n *\n * @public\n */\nexport function defineSubscription<TInput, TOutput>(\n opts: DefineSubscriptionOptions<TInput, TOutput>,\n): SubscriptionDescriptor<TInput, TOutput> {\n if (opts === null || typeof opts !== \"object\") {\n throw new TypeError(\"defineSubscription: options object is required\");\n }\n if (opts.input === undefined || typeof opts.input.safeParse !== \"function\") {\n throw new TypeError(\"defineSubscription: opts.input must be a Zod schema (or ZodLike)\");\n }\n if (opts.output === undefined || typeof opts.output.safeParse !== \"function\") {\n throw new TypeError(\"defineSubscription: opts.output must be a Zod schema (or ZodLike)\");\n }\n if (typeof opts.handler !== \"function\") {\n throw new TypeError(\"defineSubscription: opts.handler must be an async generator function\");\n }\n return {\n ...(opts.name !== undefined ? { name: opts.name } : {}),\n input: opts.input,\n output: opts.output,\n handler: opts.handler,\n };\n}\n","/**\n * @theokit/sdk — W3C SSE parser (G8 internal).\n *\n * Per ADR D428. Reads from `ReadableStream<Uint8Array>` or async iterable of\n * UTF-8 chunks; yields parsed {@link SSEvent} objects per W3C SSE spec.\n *\n * Independent of `internal/llm/sse.ts:parseSseStream` (LLM-specific Vercel AI\n * Data Stream v1; locked by D38). This parser handles the `id:` field needed\n * for tracked envelopes (G8 resume mechanism), which the LLM parser ignores.\n *\n * Reference: https://html.spec.whatwg.org/multipage/server-sent-events.html\n *\n * @internal\n */\n\nimport type { SSEvent } from \"./sse-encoder.js\";\n\nconst decoder = new TextDecoder(\"utf-8\");\n\n/**\n * Parse an async iterable of UTF-8 byte chunks into a stream of SSE events.\n *\n * Implements the W3C event stream parser:\n * - Lines are split by `\\n`, `\\r\\n`, or `\\r`\n * - Events are dispatched on blank line (no `:` and no field name)\n * - Fields: `event`, `data` (multi-line concat with `\\n`), `id`, `retry`\n * - Lines starting with `:` are comments (ignored, except dispatched as `comment`)\n * - Unknown fields silently ignored per spec\n *\n * Empty events (no fields) are NOT dispatched per spec.\n *\n * @internal\n */\nexport async function* parseSseW3C(\n source: AsyncIterable<Uint8Array>,\n): AsyncGenerator<SSEvent, void, void> {\n let buffer = \"\";\n let eventName: string | undefined;\n let dataLines: string[] = [];\n let id: string | undefined;\n let retry: number | undefined;\n let commentLines: string[] = [];\n let hasField = false;\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: dispatch builds SSEvent from per-field state per W3C spec — refactor candidate\n const dispatch = (): SSEvent | null => {\n if (!hasField) {\n // Blank line with no preceding fields → reset state, no dispatch (per spec § 9.2.6)\n eventName = undefined;\n dataLines = [];\n id = undefined;\n retry = undefined;\n commentLines = [];\n return null;\n }\n const chunk: SSEvent = {};\n if (eventName !== undefined) (chunk as { event?: string }).event = eventName;\n if (dataLines.length > 0) (chunk as { data?: string }).data = dataLines.join(\"\\n\");\n if (id !== undefined) (chunk as { id?: string }).id = id;\n if (retry !== undefined) (chunk as { retry?: number }).retry = retry;\n if (commentLines.length > 0) (chunk as { comment?: string }).comment = commentLines.join(\"\\n\");\n eventName = undefined;\n dataLines = [];\n id = undefined;\n retry = undefined;\n commentLines = [];\n hasField = false;\n return chunk;\n };\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: handleLine dispatches across all SSE field kinds per W3C spec — refactor candidate\n const handleLine = (line: string): SSEvent | null => {\n if (line === \"\") {\n return dispatch();\n }\n if (line.startsWith(\":\")) {\n // Comment line — per spec ignored, but we surface it for keepalive visibility.\n commentLines.push(line.slice(1).replace(/^ /, \"\"));\n hasField = true;\n return null;\n }\n const colonIdx = line.indexOf(\":\");\n let field: string;\n let value: string;\n if (colonIdx === -1) {\n field = line;\n value = \"\";\n } else {\n field = line.slice(0, colonIdx);\n value = line.slice(colonIdx + 1);\n // Strip ONE leading space if present (per spec § 9.2.6)\n if (value.startsWith(\" \")) value = value.slice(1);\n }\n switch (field) {\n case \"event\":\n eventName = value;\n hasField = true;\n break;\n case \"data\":\n dataLines.push(value);\n hasField = true;\n break;\n case \"id\":\n // Per spec: id with NULL is ignored\n if (!value.includes(\"\\0\")) {\n id = value;\n hasField = true;\n }\n break;\n case \"retry\":\n if (/^\\d+$/.test(value)) {\n retry = Number.parseInt(value, 10);\n hasField = true;\n }\n break;\n default:\n // Unknown field — ignored per spec\n break;\n }\n return null;\n };\n\n for await (const bytes of source) {\n // Normalize CRLF / CR to LF then split\n buffer += decoder.decode(bytes, { stream: true }).replace(/\\r\\n|\\r/g, \"\\n\");\n let idx: number;\n // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic while-assignment loop for buffered line parsing\n while ((idx = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 1);\n const event = handleLine(line);\n if (event !== null) yield event;\n }\n }\n // Flush remaining buffer as a final line (no trailing newline case)\n buffer += decoder.decode();\n if (buffer.length > 0) {\n const event = handleLine(buffer);\n if (event !== null) yield event;\n }\n // Final dispatch in case last event lacked terminating blank line\n const tail = dispatch();\n if (tail !== null) yield tail;\n}\n","import { 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 * D311 helper: choose a sensible default `isRetryable` value when the\n * caller did not supply `retriable` explicitly. Conservative defaults —\n * provider mappers override per-status when they know better.\n *\n * @internal\n */\nfunction defaultRetriableForCode(code: AgentRunErrorCode): boolean {\n switch (code) {\n case \"rate_limit\":\n case \"timeout\":\n case \"server_error\":\n case \"network\":\n case \"provider_unreachable\":\n return true;\n default:\n return false;\n }\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 * @theokit/sdk — Subscription type contract (G8 v1.7.0).\n *\n * Per ADRs D423 (resume token opaque), D424 (transport selection),\n * D426 (defineSubscription DSL shape).\n *\n * @public\n */\n\nimport { TheokitAgentError } from \"../errors.js\";\n\n/**\n * Transport selection for subscriptions.\n *\n * - `'ws'`: WebSocket (bidirectional). Required for inbound client→server frames.\n * v1.7 ships Node-canonical `ws` adapter; CF/Bun/Deno deferred to v1.8.x per ADR D425.\n * - `'sse'`: W3C Server-Sent Events (server→client only). Browser-native EventSource.\n * - `'auto'`: Prefer WS, fall back SSE on upgrade failure. Default.\n *\n * @public\n */\nexport type SubscriptionTransport = \"ws\" | \"sse\" | \"auto\";\n\n/**\n * Context passed to a subscription `handler` on each invocation.\n *\n * @public\n */\nexport interface SubscriptionCtx {\n /**\n * Opaque resume token forwarded by the client when reconnecting.\n * Handler defines its own semantics (monotonic int, ULID, timestamp,\n * encrypted cursor). SDK passes through unchanged. Per ADR D423.\n */\n readonly lastEventId?: string;\n\n /**\n * AbortSignal that fires when the subscription is cancelled\n * (client disconnect, consumer abort, server shutdown).\n * Handler MUST honor `signal.aborted` in long-running loops.\n */\n readonly signal: AbortSignal;\n\n /**\n * Stable identifier for the underlying transport connection.\n * Different from the subscription name — one connection may host\n * many subscriptions. Useful for logging / per-connection state.\n */\n readonly connectionId: string;\n\n /**\n * Force-close the underlying transport with optional WS close code + reason.\n * Used by consumer code (e.g., auth middleware revoking a session) to\n * terminate a subscription that should not continue.\n */\n disconnect(code?: number, reason?: string): void;\n\n /**\n * Helper to mint a tracked envelope `[id, payload]`.\n * Yielding tracked envelopes lets the client advance `lastEventId`\n * so reconnects can resume from that point.\n */\n tracked<TPayload>(id: string, payload: TPayload): TrackedEnvelope<TPayload>;\n}\n\n/**\n * Tracked envelope: `[id, payload]` tuple yielded by subscription handlers\n * to advertise the resume token associated with the payload.\n *\n * The encoder writes the `id` as an SSE `id:` field or WS payload metadata\n * so the client can echo it back on reconnect (`input.lastEventId`).\n *\n * Per ADR D423.\n *\n * @public\n */\nexport type TrackedEnvelope<T> = readonly [id: string, payload: T];\n\n/**\n * Type guard for {@link TrackedEnvelope}.\n *\n * @public\n */\nexport function isTrackedEnvelope(value: unknown): value is TrackedEnvelope<unknown> {\n return Array.isArray(value) && value.length === 2 && typeof value[0] === \"string\";\n}\n\n/**\n * Mint a tracked envelope. Equivalent to `[id, payload] as const`,\n * exported as a function so consumers don't repeat the tuple cast.\n *\n * @public\n */\nexport function tracked<T>(id: string, payload: T): TrackedEnvelope<T> {\n if (typeof id !== \"string\" || id.length === 0) {\n throw new TypeError(\"tracked(): id must be a non-empty string\");\n }\n return [id, payload] as const;\n}\n\n/**\n * Base error for the subscription subsystem. Extends {@link TheokitAgentError}\n * so existing `instanceof TheokitAgentError` branches keep working.\n *\n * @public\n */\nexport class SubscriptionError extends TheokitAgentError {\n override readonly name: string = \"SubscriptionError\";\n\n constructor(message: string, options: { code?: string; cause?: unknown } = {}) {\n super(message, { ...options, isRetryable: false });\n }\n}\n\n/**\n * Thrown when subscription input fails Zod schema validation.\n *\n * @public\n */\nexport class SubscriptionInputError extends SubscriptionError {\n override readonly name: string = \"SubscriptionInputError\";\n\n readonly issues: unknown;\n\n constructor(message: string, options: { issues: unknown; cause?: unknown }) {\n super(message, { code: \"subscription_input_invalid\", cause: options.cause });\n this.issues = options.issues;\n }\n}\n\n/**\n * Thrown when a subscription transport disconnects unexpectedly\n * AND the client did not opt out of reconnect.\n *\n * @public\n */\nexport class SubscriptionDisconnectError extends SubscriptionError {\n override readonly name: string = \"SubscriptionDisconnectError\";\n\n readonly closeCode?: number;\n readonly closeReason?: string;\n\n constructor(\n message: string,\n options: { closeCode?: number; closeReason?: string; cause?: unknown } = {},\n ) {\n super(message, { code: \"subscription_disconnected\", cause: options.cause });\n if (options.closeCode !== undefined) this.closeCode = options.closeCode;\n if (options.closeReason !== undefined) this.closeReason = options.closeReason;\n }\n}\n\n/**\n * Descriptor returned by {@link defineSubscription}. Carries the typed\n * input/output Zod schemas + handler factory.\n *\n * @public\n */\nexport interface SubscriptionDescriptor<TInput = unknown, TOutput = unknown> {\n /** Optional explicit name; the runtime may also derive name from file path. */\n readonly name?: string;\n /** Zod schema for the subscription input. Validated at dispatch boundary. */\n readonly input: ZodLike<TInput>;\n /** Zod schema for each output frame (informational; not enforced per-frame). */\n readonly output: ZodLike<TOutput>;\n /** Async handler that yields output frames (or tracked envelopes). */\n readonly handler: (\n input: TInput,\n ctx: SubscriptionCtx,\n ) => AsyncGenerator<TOutput | TrackedEnvelope<TOutput>, void, void>;\n}\n\n/**\n * Minimal structural Zod type used to avoid hard peer-dep import.\n * Mirrors the surface used by other SDK modules (e.g., `define-tool.ts`).\n *\n * @internal\n */\nexport interface ZodLike<T> {\n safeParse(value: unknown): { success: true; data: T } | { success: false; error: unknown };\n parse(value: unknown): T;\n}\n","/**\n * @theokit/sdk — `Theokit.subscribe` client (G8 public API).\n *\n * Per ADR D427 (Theokit.subscribe namespace) + D424 (transport selection)\n * + D423 (lastEventId opaque propagation).\n *\n * Returns an AsyncGenerator that yields server-sent values. Transparent\n * reconnect with exponential backoff. Propagates `lastEventId` from the last\n * received tracked envelope on each reconnect attempt.\n *\n * @public\n */\n\nimport { parseSseW3C } from \"./internal/sse-parser.js\";\nimport type { WireFrame } from \"./internal/subscription-runtime.js\";\nimport {\n SubscriptionDisconnectError,\n SubscriptionError,\n type SubscriptionTransport,\n} from \"./types.js\";\n\n/**\n * Options accepted by `Theokit.subscribe(name, input, opts?)`.\n *\n * @public\n */\nexport interface SubscribeOptions {\n /** Base URL of the server (e.g., `\"http://localhost:3000\"`). Required. */\n baseUrl: string;\n /** Preferred transport. Default `\"auto\"` (WS preferred, SSE fallback). */\n transport?: SubscriptionTransport;\n /** Max reconnect attempts (default 10). 0 disables reconnect. */\n maxReconnectAttempts?: number;\n /** Backoff strategy. Default exponential `min(30_000, 1000 * 2^attempt)` ms. */\n retryDelayMs?: (attempt: number) => number;\n /** Caller-supplied abort signal. Cancels the subscription entirely. */\n signal?: AbortSignal;\n /** Additional fetch-style headers (e.g., `Authorization`). */\n headers?: Record<string, string>;\n}\n\nconst defaultRetry = (attempt: number) => Math.min(30_000, 1000 * 2 ** attempt);\n\n/**\n * Subscribe to a typed subscription. Yields values produced by the server\n * handler. On disconnect with reconnect enabled, transparently re-establishes\n * the connection and forwards `lastEventId` from the last received tracked\n * envelope so the server handler can resume.\n *\n * @public\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: G8 subscribe orchestrates reconnect + transport switch + resume — refactor candidate tracked in subscription/theokit-subscribe.ts:52 followup\nexport async function* subscribe<TInput, TOutput>(\n name: string,\n input: TInput,\n opts: SubscribeOptions,\n): AsyncGenerator<TOutput, void, void> {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new SubscriptionError(\"Theokit.subscribe: name must be a non-empty string\", {\n code: \"subscribe_name_invalid\",\n });\n }\n if (opts === null || typeof opts !== \"object\" || typeof opts.baseUrl !== \"string\") {\n throw new SubscriptionError(\"Theokit.subscribe: opts.baseUrl is required\", {\n code: \"subscribe_baseUrl_missing\",\n });\n }\n const transport = opts.transport ?? \"auto\";\n const maxReconnects = opts.maxReconnectAttempts ?? 10;\n const retry = opts.retryDelayMs ?? defaultRetry;\n\n let lastEventId: string | undefined;\n let attempt = 0;\n\n while (true) {\n if (opts.signal?.aborted) return;\n try {\n const inputWithCursor = mergeLastEventId(input, lastEventId);\n const chosen = await selectTransport(transport, name, opts);\n if (chosen === \"ws\") {\n for await (const out of openWs<TOutput>(name, inputWithCursor, opts)) {\n if (out.lastEventId !== undefined) lastEventId = out.lastEventId;\n attempt = 0; // reset backoff on successful frame\n yield out.value;\n }\n } else {\n for await (const out of openSse<TOutput>(name, inputWithCursor, opts)) {\n if (out.lastEventId !== undefined) lastEventId = out.lastEventId;\n attempt = 0;\n yield out.value;\n }\n }\n // Clean end-of-stream — no reconnect.\n return;\n } catch (cause) {\n if (opts.signal?.aborted) return;\n if (maxReconnects === 0 || attempt >= maxReconnects) {\n if (cause instanceof SubscriptionError) throw cause;\n throw new SubscriptionDisconnectError(\n `subscription \"${name}\" disconnected; reconnect exhausted`,\n { cause },\n );\n }\n const delay = retry(attempt);\n attempt++;\n await sleep(delay, opts.signal);\n }\n }\n}\n\ninterface FrameOut<T> {\n readonly value: T;\n readonly lastEventId?: string;\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: openSse parses framed SSE stream with abort/error/retry branches — refactor candidate\nasync function* openSse<T>(\n name: string,\n input: unknown,\n opts: SubscribeOptions,\n): AsyncIterable<FrameOut<T>> {\n const url = `${stripTrailingSlash(opts.baseUrl)}/api/subscriptions/${encodeURIComponent(name)}?input=${encodeURIComponent(JSON.stringify(input))}`;\n const res = await fetch(url, {\n method: \"GET\",\n headers: {\n accept: \"text/event-stream\",\n ...(opts.headers ?? {}),\n },\n ...(opts.signal !== undefined ? { signal: opts.signal } : {}),\n });\n if (!res.ok || res.body === null) {\n throw new SubscriptionError(`SSE subscribe failed: HTTP ${res.status} ${res.statusText}`, {\n code: \"sse_http_error\",\n });\n }\n const events = parseSseW3C(streamToAsyncIterable(res.body));\n for await (const ev of events) {\n if (ev.event === \"end\") return;\n if (ev.event === \"error\") {\n throw new SubscriptionError(`SSE server emitted error: ${ev.data ?? \"(no body)\"}`, {\n code: \"sse_server_error\",\n });\n }\n if (ev.event === \"connected\") continue;\n if (ev.data === undefined) continue;\n const parsedValue = safeJsonParse<T>(ev.data);\n if (parsedValue === undefined) continue;\n yield {\n value: parsedValue,\n ...(ev.id !== undefined ? { lastEventId: ev.id } : {}),\n };\n }\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: openWs orchestrates WS lifecycle + envelope decode + cleanup — refactor candidate\nasync function* openWs<T>(\n name: string,\n input: unknown,\n opts: SubscribeOptions,\n): AsyncIterable<FrameOut<T>> {\n const baseUrl = stripTrailingSlash(opts.baseUrl);\n const wsUrl = baseUrl\n .replace(/^http:/, \"ws:\")\n .replace(/^https:/, \"wss:\")\n .concat(\"/api/ws\");\n\n const WS = (globalThis as { WebSocket?: typeof WebSocket }).WebSocket;\n if (WS === undefined) {\n throw new SubscriptionError(\n \"Theokit.subscribe(transport='ws'): no global WebSocket available. Use Node >=22 or install 'ws' as a runtime dep + assign to globalThis.WebSocket.\",\n { code: \"ws_global_missing\" },\n );\n }\n const ws = new WS(wsUrl);\n\n // Set up async iterator-like state.\n const incoming: WireFrame[] = [];\n // biome-ignore lint/suspicious/noConfusingVoidType: idiomatic event-callback signature; refactoring to undefined would mask the no-value semantics\n let waiter: ((value: void) => void) | null = null;\n let closed = false;\n let closeError: Error | undefined;\n\n ws.addEventListener(\"message\", (ev: MessageEvent) => {\n const data = typeof ev.data === \"string\" ? ev.data : \"\";\n const frame = safeJsonParse<WireFrame>(data);\n if (frame !== undefined) {\n incoming.push(frame);\n if (waiter !== null) {\n waiter();\n waiter = null;\n }\n }\n });\n ws.addEventListener(\"close\", () => {\n closed = true;\n if (waiter !== null) {\n waiter();\n waiter = null;\n }\n });\n ws.addEventListener(\"error\", () => {\n closed = true;\n closeError = new Error(\"WebSocket error\");\n if (waiter !== null) {\n waiter();\n waiter = null;\n }\n });\n\n await new Promise<void>((resolve, reject) => {\n ws.addEventListener(\"open\", () => resolve(), { once: true });\n ws.addEventListener(\"error\", () => reject(new Error(\"WS connect failed\")), { once: true });\n });\n\n // Send subscribe request after handshake.\n ws.send(JSON.stringify({ kind: \"subscribe\", name, input }));\n\n if (opts.signal !== undefined) {\n opts.signal.addEventListener(\"abort\", () => ws.close(), { once: true });\n }\n\n try {\n while (true) {\n if (incoming.length === 0) {\n if (closed) {\n if (closeError !== undefined) throw closeError;\n return;\n }\n await new Promise<void>((resolve) => {\n waiter = resolve;\n });\n continue;\n }\n const frame = incoming.shift() as WireFrame;\n if (frame.type === \"end\") return;\n if (frame.type === \"error\") {\n throw new SubscriptionError(\n `WS server emitted error: ${frame.error?.message ?? \"(unknown)\"}`,\n { code: \"ws_server_error\" },\n );\n }\n const value = frame.data as T;\n yield {\n value,\n ...(frame.id !== undefined ? { lastEventId: frame.id } : {}),\n };\n }\n } finally {\n try {\n ws.close();\n } catch {\n /* ignore */\n }\n }\n}\n\nasync function selectTransport(\n pref: SubscriptionTransport,\n _name: string,\n _opts: SubscribeOptions,\n): Promise<\"ws\" | \"sse\"> {\n if (pref === \"ws\") return \"ws\";\n if (pref === \"sse\") return \"sse\";\n // auto — prefer WS if available, else SSE.\n if (typeof (globalThis as { WebSocket?: unknown }).WebSocket === \"function\") {\n return \"ws\";\n }\n return \"sse\";\n}\n\nfunction mergeLastEventId(input: unknown, lastEventId: string | undefined): unknown {\n if (lastEventId === undefined) return input;\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n // Cannot merge into primitive — return as-is.\n return input;\n }\n return { ...(input as object), lastEventId };\n}\n\nfunction stripTrailingSlash(s: string): string {\n return s.endsWith(\"/\") ? s.slice(0, -1) : s;\n}\n\nfunction safeJsonParse<T>(s: string): T | undefined {\n try {\n return JSON.parse(s) as T;\n } catch {\n return undefined;\n }\n}\n\nasync function* streamToAsyncIterable(\n stream: ReadableStream<Uint8Array>,\n): AsyncIterable<Uint8Array> {\n const reader = stream.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) return;\n if (value !== undefined) yield value;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error(\"aborted\"));\n return;\n }\n const t = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(t);\n reject(new Error(\"aborted\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/subscription/define-subscription.ts","../../src/subscription/internal/sse-parser.ts","../../src/errors.ts","../../src/subscription/types.ts","../../src/subscription/theokit-subscribe.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @theokit/sdk — `defineSubscription` DSL (G8 public API).\n *\n * Per ADR D426 (AsyncGenerator handler + Zod input/output schemas).\n *\n * @public\n */\n\nimport type { SubscriptionCtx, SubscriptionDescriptor, TrackedEnvelope, ZodLike } from \"./types.js\";\n\n/**\n * Options accepted by {@link defineSubscription}.\n *\n * @public\n */\nexport interface DefineSubscriptionOptions<TInput, TOutput> {\n /** Optional explicit name. Falls back to filename derivation by the scanner. */\n name?: string;\n /** Zod schema validating subscription input. Required (typed RPC contract). */\n input: ZodLike<TInput>;\n /** Zod schema describing each output frame. Informational; not enforced per-frame. */\n output: ZodLike<TOutput>;\n /**\n * Handler that produces the subscription stream.\n *\n * Receives the validated input + {@link SubscriptionCtx}. Yielding a value\n * emits a plain output frame. Yielding a `tracked(id, payload)` envelope\n * additionally advertises a resume token to the client so reconnects can\n * resume from that point.\n */\n handler: (\n input: TInput,\n ctx: SubscriptionCtx,\n ) => AsyncGenerator<TOutput | TrackedEnvelope<TOutput>, void, void>;\n}\n\n/**\n * Define a typed subscription. Pair with `Theokit.subscribe(name, input)`\n * on the client side.\n *\n * Per ADR D426 — handler is `AsyncGenerator<TOutput | TrackedEnvelope<TOutput>>`.\n * The runtime validates input via `opts.input.safeParse` BEFORE invoking the\n * handler; failure throws {@link import('./types.js').SubscriptionInputError}.\n *\n * @example\n * ```ts\n * import { defineSubscription } from \"@theokit/sdk\";\n * import { z } from \"zod\";\n *\n * export default defineSubscription({\n * input: z.object({ topic: z.string() }),\n * output: z.object({ message: z.string(), ts: z.number() }),\n * async *handler(input, ctx) {\n * let cursor = ctx.lastEventId ?? \"0\";\n * while (!ctx.signal.aborted) {\n * const msgs = await fetchMessagesSince(input.topic, cursor);\n * for (const m of msgs) {\n * cursor = m.id;\n * yield ctx.tracked(m.id, { message: m.body, ts: m.ts });\n * }\n * await sleep(1000);\n * }\n * },\n * });\n * ```\n *\n * @public\n */\nexport function defineSubscription<TInput, TOutput>(\n opts: DefineSubscriptionOptions<TInput, TOutput>,\n): SubscriptionDescriptor<TInput, TOutput> {\n if (opts === null || typeof opts !== \"object\") {\n throw new TypeError(\"defineSubscription: options object is required\");\n }\n if (opts.input === undefined || typeof opts.input.safeParse !== \"function\") {\n throw new TypeError(\"defineSubscription: opts.input must be a Zod schema (or ZodLike)\");\n }\n if (opts.output === undefined || typeof opts.output.safeParse !== \"function\") {\n throw new TypeError(\"defineSubscription: opts.output must be a Zod schema (or ZodLike)\");\n }\n if (typeof opts.handler !== \"function\") {\n throw new TypeError(\"defineSubscription: opts.handler must be an async generator function\");\n }\n return {\n ...(opts.name !== undefined ? { name: opts.name } : {}),\n input: opts.input,\n output: opts.output,\n handler: opts.handler,\n };\n}\n","/**\n * @theokit/sdk — W3C SSE parser (G8 internal).\n *\n * Per ADR D428. Reads from `ReadableStream<Uint8Array>` or async iterable of\n * UTF-8 chunks; yields parsed {@link SSEvent} objects per W3C SSE spec.\n *\n * Independent of `internal/llm/sse.ts:parseSseStream` (LLM-specific Vercel AI\n * Data Stream v1; locked by D38). This parser handles the `id:` field needed\n * for tracked envelopes (G8 resume mechanism), which the LLM parser ignores.\n *\n * Reference: https://html.spec.whatwg.org/multipage/server-sent-events.html\n *\n * @internal\n */\n\nimport type { SSEvent } from \"./sse-encoder.js\";\n\nconst decoder = new TextDecoder(\"utf-8\");\n\n/**\n * Parse an async iterable of UTF-8 byte chunks into a stream of SSE events.\n *\n * Implements the W3C event stream parser:\n * - Lines are split by `\\n`, `\\r\\n`, or `\\r`\n * - Events are dispatched on blank line (no `:` and no field name)\n * - Fields: `event`, `data` (multi-line concat with `\\n`), `id`, `retry`\n * - Lines starting with `:` are comments (ignored, except dispatched as `comment`)\n * - Unknown fields silently ignored per spec\n *\n * Empty events (no fields) are NOT dispatched per spec.\n *\n * @internal\n */\nexport async function* parseSseW3C(\n source: AsyncIterable<Uint8Array>,\n): AsyncGenerator<SSEvent, void, void> {\n let buffer = \"\";\n let eventName: string | undefined;\n let dataLines: string[] = [];\n let id: string | undefined;\n let retry: number | undefined;\n let commentLines: string[] = [];\n let hasField = false;\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: dispatch builds SSEvent from per-field state per W3C spec — refactor candidate\n const dispatch = (): SSEvent | null => {\n if (!hasField) {\n // Blank line with no preceding fields → reset state, no dispatch (per spec § 9.2.6)\n eventName = undefined;\n dataLines = [];\n id = undefined;\n retry = undefined;\n commentLines = [];\n return null;\n }\n const chunk: SSEvent = {};\n if (eventName !== undefined) (chunk as { event?: string }).event = eventName;\n if (dataLines.length > 0) (chunk as { data?: string }).data = dataLines.join(\"\\n\");\n if (id !== undefined) (chunk as { id?: string }).id = id;\n if (retry !== undefined) (chunk as { retry?: number }).retry = retry;\n if (commentLines.length > 0) (chunk as { comment?: string }).comment = commentLines.join(\"\\n\");\n eventName = undefined;\n dataLines = [];\n id = undefined;\n retry = undefined;\n commentLines = [];\n hasField = false;\n return chunk;\n };\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: handleLine dispatches across all SSE field kinds per W3C spec — refactor candidate\n const handleLine = (line: string): SSEvent | null => {\n if (line === \"\") {\n return dispatch();\n }\n if (line.startsWith(\":\")) {\n // Comment line — per spec ignored, but we surface it for keepalive visibility.\n commentLines.push(line.slice(1).replace(/^ /, \"\"));\n hasField = true;\n return null;\n }\n const colonIdx = line.indexOf(\":\");\n let field: string;\n let value: string;\n if (colonIdx === -1) {\n field = line;\n value = \"\";\n } else {\n field = line.slice(0, colonIdx);\n value = line.slice(colonIdx + 1);\n // Strip ONE leading space if present (per spec § 9.2.6)\n if (value.startsWith(\" \")) value = value.slice(1);\n }\n switch (field) {\n case \"event\":\n eventName = value;\n hasField = true;\n break;\n case \"data\":\n dataLines.push(value);\n hasField = true;\n break;\n case \"id\":\n // Per spec: id with NULL is ignored\n if (!value.includes(\"\\0\")) {\n id = value;\n hasField = true;\n }\n break;\n case \"retry\":\n if (/^\\d+$/.test(value)) {\n retry = Number.parseInt(value, 10);\n hasField = true;\n }\n break;\n default:\n // Unknown field — ignored per spec\n break;\n }\n return null;\n };\n\n for await (const bytes of source) {\n // Normalize CRLF / CR to LF then split\n buffer += decoder.decode(bytes, { stream: true }).replace(/\\r\\n|\\r/g, \"\\n\");\n let idx: number;\n // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic while-assignment loop for buffered line parsing\n while ((idx = buffer.indexOf(\"\\n\")) !== -1) {\n const line = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 1);\n const event = handleLine(line);\n if (event !== null) yield event;\n }\n }\n // Flush remaining buffer as a final line (no trailing newline case)\n buffer += decoder.decode();\n if (buffer.length > 0) {\n const event = handleLine(buffer);\n if (event !== null) yield event;\n }\n // Final dispatch in case last event lacked terminating blank line\n const tail = dispatch();\n if (tail !== null) yield tail;\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 * @theokit/sdk — Subscription type contract (G8 v1.7.0).\n *\n * Per ADRs D423 (resume token opaque), D424 (transport selection),\n * D426 (defineSubscription DSL shape).\n *\n * @public\n */\n\nimport { TheokitAgentError } from \"../errors.js\";\n\n/**\n * Transport selection for subscriptions.\n *\n * - `'ws'`: WebSocket (bidirectional). Required for inbound client→server frames.\n * v1.7 ships Node-canonical `ws` adapter; CF/Bun/Deno deferred to v1.8.x per ADR D425.\n * - `'sse'`: W3C Server-Sent Events (server→client only). Browser-native EventSource.\n * - `'auto'`: Prefer WS, fall back SSE on upgrade failure. Default.\n *\n * @public\n */\nexport type SubscriptionTransport = \"ws\" | \"sse\" | \"auto\";\n\n/**\n * Context passed to a subscription `handler` on each invocation.\n *\n * @public\n */\nexport interface SubscriptionCtx {\n /**\n * Opaque resume token forwarded by the client when reconnecting.\n * Handler defines its own semantics (monotonic int, ULID, timestamp,\n * encrypted cursor). SDK passes through unchanged. Per ADR D423.\n */\n readonly lastEventId?: string;\n\n /**\n * AbortSignal that fires when the subscription is cancelled\n * (client disconnect, consumer abort, server shutdown).\n * Handler MUST honor `signal.aborted` in long-running loops.\n */\n readonly signal: AbortSignal;\n\n /**\n * Stable identifier for the underlying transport connection.\n * Different from the subscription name — one connection may host\n * many subscriptions. Useful for logging / per-connection state.\n */\n readonly connectionId: string;\n\n /**\n * Force-close the underlying transport with optional WS close code + reason.\n * Used by consumer code (e.g., auth middleware revoking a session) to\n * terminate a subscription that should not continue.\n */\n disconnect(code?: number, reason?: string): void;\n\n /**\n * Helper to mint a tracked envelope `[id, payload]`.\n * Yielding tracked envelopes lets the client advance `lastEventId`\n * so reconnects can resume from that point.\n */\n tracked<TPayload>(id: string, payload: TPayload): TrackedEnvelope<TPayload>;\n}\n\n/**\n * Tracked envelope: `[id, payload]` tuple yielded by subscription handlers\n * to advertise the resume token associated with the payload.\n *\n * The encoder writes the `id` as an SSE `id:` field or WS payload metadata\n * so the client can echo it back on reconnect (`input.lastEventId`).\n *\n * Per ADR D423.\n *\n * @public\n */\nexport type TrackedEnvelope<T> = readonly [id: string, payload: T];\n\n/**\n * Type guard for {@link TrackedEnvelope}.\n *\n * @public\n */\nexport function isTrackedEnvelope(value: unknown): value is TrackedEnvelope<unknown> {\n return Array.isArray(value) && value.length === 2 && typeof value[0] === \"string\";\n}\n\n/**\n * Mint a tracked envelope. Equivalent to `[id, payload] as const`,\n * exported as a function so consumers don't repeat the tuple cast.\n *\n * @public\n */\nexport function tracked<T>(id: string, payload: T): TrackedEnvelope<T> {\n if (typeof id !== \"string\" || id.length === 0) {\n throw new TypeError(\"tracked(): id must be a non-empty string\");\n }\n return [id, payload] as const;\n}\n\n/**\n * Base error for the subscription subsystem. Extends {@link TheokitAgentError}\n * so existing `instanceof TheokitAgentError` branches keep working.\n *\n * @public\n */\nexport class SubscriptionError extends TheokitAgentError {\n override readonly name: string = \"SubscriptionError\";\n\n constructor(message: string, options: { code?: string; cause?: unknown } = {}) {\n super(message, { ...options, isRetryable: false });\n }\n}\n\n/**\n * Thrown when subscription input fails Zod schema validation.\n *\n * @public\n */\nexport class SubscriptionInputError extends SubscriptionError {\n override readonly name: string = \"SubscriptionInputError\";\n\n readonly issues: unknown;\n\n constructor(message: string, options: { issues: unknown; cause?: unknown }) {\n super(message, { code: \"subscription_input_invalid\", cause: options.cause });\n this.issues = options.issues;\n }\n}\n\n/**\n * Thrown when a subscription transport disconnects unexpectedly\n * AND the client did not opt out of reconnect.\n *\n * @public\n */\nexport class SubscriptionDisconnectError extends SubscriptionError {\n override readonly name: string = \"SubscriptionDisconnectError\";\n\n readonly closeCode?: number;\n readonly closeReason?: string;\n\n constructor(\n message: string,\n options: { closeCode?: number; closeReason?: string; cause?: unknown } = {},\n ) {\n super(message, { code: \"subscription_disconnected\", cause: options.cause });\n if (options.closeCode !== undefined) this.closeCode = options.closeCode;\n if (options.closeReason !== undefined) this.closeReason = options.closeReason;\n }\n}\n\n/**\n * Descriptor returned by {@link defineSubscription}. Carries the typed\n * input/output Zod schemas + handler factory.\n *\n * @public\n */\nexport interface SubscriptionDescriptor<TInput = unknown, TOutput = unknown> {\n /** Optional explicit name; the runtime may also derive name from file path. */\n readonly name?: string;\n /** Zod schema for the subscription input. Validated at dispatch boundary. */\n readonly input: ZodLike<TInput>;\n /** Zod schema for each output frame (informational; not enforced per-frame). */\n readonly output: ZodLike<TOutput>;\n /** Async handler that yields output frames (or tracked envelopes). */\n readonly handler: (\n input: TInput,\n ctx: SubscriptionCtx,\n ) => AsyncGenerator<TOutput | TrackedEnvelope<TOutput>, void, void>;\n}\n\n/**\n * Minimal structural Zod type used to avoid hard peer-dep import.\n * Mirrors the surface used by other SDK modules (e.g., `define-tool.ts`).\n *\n * @internal\n */\nexport interface ZodLike<T> {\n safeParse(value: unknown): { success: true; data: T } | { success: false; error: unknown };\n parse(value: unknown): T;\n}\n","/**\n * @theokit/sdk — `Theokit.subscribe` client (G8 public API).\n *\n * Per ADR D427 (Theokit.subscribe namespace) + D424 (transport selection)\n * + D423 (lastEventId opaque propagation).\n *\n * Returns an AsyncGenerator that yields server-sent values. Transparent\n * reconnect with exponential backoff. Propagates `lastEventId` from the last\n * received tracked envelope on each reconnect attempt.\n *\n * @public\n */\n\nimport { parseSseW3C } from \"./internal/sse-parser.js\";\nimport type { WireFrame } from \"./internal/subscription-runtime.js\";\nimport {\n SubscriptionDisconnectError,\n SubscriptionError,\n type SubscriptionTransport,\n} from \"./types.js\";\n\n/**\n * Options accepted by `Theokit.subscribe(name, input, opts?)`.\n *\n * @public\n */\nexport interface SubscribeOptions {\n /** Base URL of the server (e.g., `\"http://localhost:3000\"`). Required. */\n baseUrl: string;\n /** Preferred transport. Default `\"auto\"` (WS preferred, SSE fallback). */\n transport?: SubscriptionTransport;\n /** Max reconnect attempts (default 10). 0 disables reconnect. */\n maxReconnectAttempts?: number;\n /** Backoff strategy. Default exponential `min(30_000, 1000 * 2^attempt)` ms. */\n retryDelayMs?: (attempt: number) => number;\n /** Caller-supplied abort signal. Cancels the subscription entirely. */\n signal?: AbortSignal;\n /** Additional fetch-style headers (e.g., `Authorization`). */\n headers?: Record<string, string>;\n}\n\nconst defaultRetry = (attempt: number) => Math.min(30_000, 1000 * 2 ** attempt);\n\n/**\n * Subscribe to a typed subscription. Yields values produced by the server\n * handler. On disconnect with reconnect enabled, transparently re-establishes\n * the connection and forwards `lastEventId` from the last received tracked\n * envelope so the server handler can resume.\n *\n * @public\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: G8 subscribe orchestrates reconnect + transport switch + resume — refactor candidate tracked in subscription/theokit-subscribe.ts:52 followup\nexport async function* subscribe<TInput, TOutput>(\n name: string,\n input: TInput,\n opts: SubscribeOptions,\n): AsyncGenerator<TOutput, void, void> {\n if (typeof name !== \"string\" || name.length === 0) {\n throw new SubscriptionError(\"Theokit.subscribe: name must be a non-empty string\", {\n code: \"subscribe_name_invalid\",\n });\n }\n if (opts === null || typeof opts !== \"object\" || typeof opts.baseUrl !== \"string\") {\n throw new SubscriptionError(\"Theokit.subscribe: opts.baseUrl is required\", {\n code: \"subscribe_baseUrl_missing\",\n });\n }\n const transport = opts.transport ?? \"auto\";\n const maxReconnects = opts.maxReconnectAttempts ?? 10;\n const retry = opts.retryDelayMs ?? defaultRetry;\n\n let lastEventId: string | undefined;\n let attempt = 0;\n\n while (true) {\n if (opts.signal?.aborted) return;\n try {\n const inputWithCursor = mergeLastEventId(input, lastEventId);\n const chosen = await selectTransport(transport, name, opts);\n if (chosen === \"ws\") {\n for await (const out of openWs<TOutput>(name, inputWithCursor, opts)) {\n if (out.lastEventId !== undefined) lastEventId = out.lastEventId;\n attempt = 0; // reset backoff on successful frame\n yield out.value;\n }\n } else {\n for await (const out of openSse<TOutput>(name, inputWithCursor, opts)) {\n if (out.lastEventId !== undefined) lastEventId = out.lastEventId;\n attempt = 0;\n yield out.value;\n }\n }\n // Clean end-of-stream — no reconnect.\n return;\n } catch (cause) {\n if (opts.signal?.aborted) return;\n if (maxReconnects === 0 || attempt >= maxReconnects) {\n if (cause instanceof SubscriptionError) throw cause;\n throw new SubscriptionDisconnectError(\n `subscription \"${name}\" disconnected; reconnect exhausted`,\n { cause },\n );\n }\n const delay = retry(attempt);\n attempt++;\n await sleep(delay, opts.signal);\n }\n }\n}\n\ninterface FrameOut<T> {\n readonly value: T;\n readonly lastEventId?: string;\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: openSse parses framed SSE stream with abort/error/retry branches — refactor candidate\nasync function* openSse<T>(\n name: string,\n input: unknown,\n opts: SubscribeOptions,\n): AsyncIterable<FrameOut<T>> {\n const url = `${stripTrailingSlash(opts.baseUrl)}/api/subscriptions/${encodeURIComponent(name)}?input=${encodeURIComponent(JSON.stringify(input))}`;\n const res = await fetch(url, {\n method: \"GET\",\n headers: {\n accept: \"text/event-stream\",\n ...(opts.headers ?? {}),\n },\n ...(opts.signal !== undefined ? { signal: opts.signal } : {}),\n });\n if (!res.ok || res.body === null) {\n throw new SubscriptionError(`SSE subscribe failed: HTTP ${res.status} ${res.statusText}`, {\n code: \"sse_http_error\",\n });\n }\n const events = parseSseW3C(streamToAsyncIterable(res.body));\n for await (const ev of events) {\n if (ev.event === \"end\") return;\n if (ev.event === \"error\") {\n throw new SubscriptionError(`SSE server emitted error: ${ev.data ?? \"(no body)\"}`, {\n code: \"sse_server_error\",\n });\n }\n if (ev.event === \"connected\") continue;\n if (ev.data === undefined) continue;\n const parsedValue = safeJsonParse<T>(ev.data);\n if (parsedValue === undefined) continue;\n yield {\n value: parsedValue,\n ...(ev.id !== undefined ? { lastEventId: ev.id } : {}),\n };\n }\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: openWs orchestrates WS lifecycle + envelope decode + cleanup — refactor candidate\nasync function* openWs<T>(\n name: string,\n input: unknown,\n opts: SubscribeOptions,\n): AsyncIterable<FrameOut<T>> {\n const baseUrl = stripTrailingSlash(opts.baseUrl);\n const wsUrl = baseUrl\n .replace(/^http:/, \"ws:\")\n .replace(/^https:/, \"wss:\")\n .concat(\"/api/ws\");\n\n const WS = (globalThis as { WebSocket?: typeof WebSocket }).WebSocket;\n if (WS === undefined) {\n throw new SubscriptionError(\n \"Theokit.subscribe(transport='ws'): no global WebSocket available. Use Node >=22 or install 'ws' as a runtime dep + assign to globalThis.WebSocket.\",\n { code: \"ws_global_missing\" },\n );\n }\n const ws = new WS(wsUrl);\n\n // Set up async iterator-like state.\n const incoming: WireFrame[] = [];\n // biome-ignore lint/suspicious/noConfusingVoidType: idiomatic event-callback signature; refactoring to undefined would mask the no-value semantics\n let waiter: ((value: void) => void) | null = null;\n let closed = false;\n let closeError: Error | undefined;\n\n ws.addEventListener(\"message\", (ev: MessageEvent) => {\n const data = typeof ev.data === \"string\" ? ev.data : \"\";\n const frame = safeJsonParse<WireFrame>(data);\n if (frame !== undefined) {\n incoming.push(frame);\n if (waiter !== null) {\n waiter();\n waiter = null;\n }\n }\n });\n ws.addEventListener(\"close\", () => {\n closed = true;\n if (waiter !== null) {\n waiter();\n waiter = null;\n }\n });\n ws.addEventListener(\"error\", () => {\n closed = true;\n closeError = new Error(\"WebSocket error\");\n if (waiter !== null) {\n waiter();\n waiter = null;\n }\n });\n\n await new Promise<void>((resolve, reject) => {\n ws.addEventListener(\"open\", () => resolve(), { once: true });\n ws.addEventListener(\"error\", () => reject(new Error(\"WS connect failed\")), { once: true });\n });\n\n // Send subscribe request after handshake.\n ws.send(JSON.stringify({ kind: \"subscribe\", name, input }));\n\n if (opts.signal !== undefined) {\n opts.signal.addEventListener(\"abort\", () => ws.close(), { once: true });\n }\n\n try {\n while (true) {\n if (incoming.length === 0) {\n if (closed) {\n if (closeError !== undefined) throw closeError;\n return;\n }\n await new Promise<void>((resolve) => {\n waiter = resolve;\n });\n continue;\n }\n const frame = incoming.shift() as WireFrame;\n if (frame.type === \"end\") return;\n if (frame.type === \"error\") {\n throw new SubscriptionError(\n `WS server emitted error: ${frame.error?.message ?? \"(unknown)\"}`,\n { code: \"ws_server_error\" },\n );\n }\n const value = frame.data as T;\n yield {\n value,\n ...(frame.id !== undefined ? { lastEventId: frame.id } : {}),\n };\n }\n } finally {\n try {\n ws.close();\n } catch {\n /* ignore */\n }\n }\n}\n\nasync function selectTransport(\n pref: SubscriptionTransport,\n _name: string,\n _opts: SubscribeOptions,\n): Promise<\"ws\" | \"sse\"> {\n if (pref === \"ws\") return \"ws\";\n if (pref === \"sse\") return \"sse\";\n // auto — prefer WS if available, else SSE.\n if (typeof (globalThis as { WebSocket?: unknown }).WebSocket === \"function\") {\n return \"ws\";\n }\n return \"sse\";\n}\n\nfunction mergeLastEventId(input: unknown, lastEventId: string | undefined): unknown {\n if (lastEventId === undefined) return input;\n if (input === null || typeof input !== \"object\" || Array.isArray(input)) {\n // Cannot merge into primitive — return as-is.\n return input;\n }\n return { ...(input as object), lastEventId };\n}\n\nfunction stripTrailingSlash(s: string): string {\n return s.endsWith(\"/\") ? s.slice(0, -1) : s;\n}\n\nfunction safeJsonParse<T>(s: string): T | undefined {\n try {\n return JSON.parse(s) as T;\n } catch {\n return undefined;\n }\n}\n\nasync function* streamToAsyncIterable(\n stream: ReadableStream<Uint8Array>,\n): AsyncIterable<Uint8Array> {\n const reader = stream.getReader();\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) return;\n if (value !== undefined) yield value;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error(\"aborted\"));\n return;\n }\n const t = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n clearTimeout(t);\n reject(new Error(\"aborted\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n"]}
|
package/dist/task-store.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/types/task.ts","../src/internal/persistence/atomic-write.ts","../src/internal/task/store.ts"],"names":["statfs","dirname","randomBytes","open","rename","unlink","mkdir","mkdirSync","join","readFile","readdirSync"],"mappings":"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","file":"task-store.cjs","sourcesContent":["import { 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 * D311 helper: choose a sensible default `isRetryable` value when the\n * caller did not supply `retriable` explicitly. Conservative defaults —\n * provider mappers override per-status when they know better.\n *\n * @internal\n */\nfunction defaultRetriableForCode(code: AgentRunErrorCode): boolean {\n switch (code) {\n case \"rate_limit\":\n case \"timeout\":\n case \"server_error\":\n case \"network\":\n case \"provider_unreachable\":\n return true;\n default:\n return false;\n }\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 * Public type contract for the Task observability registry (ADRs D361-D374).\n *\n * Tasks are an opt-in observability layer over async work in the SDK\n * (`Agent.send`, `Agent.batch`, `Workflow.run`, `Cron` fires). They have\n * a closed 5-state lifecycle (D362), discriminated events (D366), and a\n * pluggable store (D364). The `Task` facade in `task.ts` is the public\n * surface; consumers import these types from `@theokit/sdk`.\n *\n * @public\n */\n\n/**\n * Closed enum of the 5 lifecycle states (D362).\n *\n * Transitions are acyclic:\n * queued → running → (finished | error | cancelled)\n * queued → cancelled (direct, no run started)\n */\nexport type TaskState = \"queued\" | \"running\" | \"finished\" | \"error\" | \"cancelled\";\n\n/** Discriminator of the runtime that produced a task (D374). */\nexport type TaskKind = \"run\" | \"batch\" | \"workflow\" | \"cron\" | \"custom\";\n\n/** Discriminated union of task lifecycle events (D366). */\nexport type TaskEvent =\n | {\n readonly type: \"submitted\";\n readonly taskId: string;\n readonly kind: TaskKind;\n readonly submittedAt: number;\n readonly meta?: Record<string, unknown>;\n /** D372 — flag set on the first yielded event when the ring buffer was at cap. */\n readonly truncated?: boolean;\n }\n | { readonly type: \"started\"; readonly taskId: string; readonly startedAt: number }\n | {\n readonly type: \"progress\";\n readonly taskId: string;\n readonly at: number;\n readonly payload: unknown;\n }\n | {\n readonly type: \"finished\";\n readonly taskId: string;\n readonly finishedAt: number;\n readonly result: unknown;\n }\n | {\n readonly type: \"errored\";\n readonly taskId: string;\n readonly erroredAt: number;\n readonly error: { readonly code: string; readonly message: string };\n }\n | {\n readonly type: \"cancelled\";\n readonly taskId: string;\n readonly cancelledAt: number;\n readonly reason?: string;\n };\n\n/** Public read-only view of a task entry in the registry. */\nexport interface TaskHandle {\n readonly id: string;\n readonly kind: TaskKind;\n readonly state: TaskState;\n readonly submittedAt: number;\n readonly startedAt?: number;\n readonly finishedAt?: number;\n readonly cancelledAt?: number;\n readonly erroredAt?: number;\n readonly result?: unknown;\n readonly error?: { readonly code: string; readonly message: string };\n readonly meta?: Record<string, unknown>;\n /**\n * EC-7 — cross-process best-effort cancel flag. Set by CLI\n * `theokit tasks cancel` via JsonFileTaskStore. The owning process\n * polls at checkpoints and honors via AbortController. Always\n * `undefined` for in-process cancel paths (which go directly through\n * AbortController).\n */\n readonly cancelRequested?: boolean;\n}\n\n/** Query filter for `Task.list`. */\nexport interface TaskFilter {\n readonly state?: TaskState | readonly TaskState[];\n readonly kind?: TaskKind | readonly TaskKind[];\n readonly submittedAfter?: number;\n readonly submittedBefore?: number;\n /** Defaults to 100. JsonFileTaskStore hard-caps loaded entries at 256 (D364). */\n readonly limit?: number;\n}\n\n/** Options for `Task.submit`. */\nexport interface TaskSubmitOptions {\n /**\n * Optional user-supplied ID. Must match grammar `^[a-z0-9][a-z0-9_-]*$`\n * and MUST NOT start with the reserved prefixes `wf-` / `b-` / `cron-`\n * (D368, EC-5). When omitted, `crypto.randomUUID()` is used.\n */\n readonly id?: string;\n readonly meta?: Record<string, unknown>;\n /**\n * Optional caller-provided AbortSignal. If already aborted at submit\n * time, the registry short-circuits to `cancelled` without acquiring\n * a semaphore slot (EC-4).\n */\n readonly signal?: AbortSignal;\n}\n\n/** Options shape for `TaskStore` factory (D364). */\nexport type TaskStoreOptions =\n | { readonly backend: \"memory\" }\n | { readonly backend: \"json\"; readonly dir: string };\n\n/** Result of `Task.cancel` (D365 — idempotent). */\nexport interface TaskCancelResult {\n readonly cancelled: boolean;\n readonly alreadyTerminal: boolean;\n}\n\n/**\n * Grammar for user-supplied task IDs (D368).\n * `crypto.randomUUID()` outputs do NOT match this (UUIDs have dashes\n * AND uppercase letters in their canonical form on some Node versions),\n * but the registry normalizes auto-generated IDs to lowercase before\n * insertion, so they pass the same regex.\n */\nconst TASK_ID_GRAMMAR = /^[a-z0-9][a-z0-9_-]*$/;\n\n/** Reserved prefixes for adapter-generated IDs (D368, EC-5). */\nconst RESERVED_PREFIXES = [\"wf-\", \"b-\", \"cron-\"] as const;\n\n/**\n * Validates a task ID against the public grammar + reserved prefixes.\n * Throws `InvalidTaskIdError` from `../errors.js` on rejection.\n *\n * Adapter callers (workflow/batch/cron) MUST set `allowReserved: true`\n * to register their own IDs; user-facing surfaces (`Task.submit`,\n * `agent.send({ task: { id } })`) leave it false.\n */\nexport function isValidTaskId(id: string, allowReserved: boolean): boolean {\n if (!TASK_ID_GRAMMAR.test(id)) return false;\n if (allowReserved) return true;\n for (const prefix of RESERVED_PREFIXES) {\n if (id.startsWith(prefix)) return false;\n }\n return true;\n}\n\n/** Re-exported for adapter implementations + tests. */\nexport const TASK_RESERVED_PREFIXES: readonly string[] = RESERVED_PREFIXES;\n","import { randomBytes } from \"node:crypto\";\nimport { mkdir, open, rename, statfs, unlink } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\n// T5.8 — Linux filesystem magic numbers (from `<linux/magic.h>`).\n// Used by `detectNetworkFsName` to identify the parent directory's\n// filesystem type from a `statfs()` return value. The four entries\n// below cover the network/FUSE cases where `rename()` is best-effort\n// rather than strictly atomic; everything else is treated as local.\nconst NETWORK_FS_MAGIC: ReadonlyMap<number, string> = new Map([\n [0x6969, \"nfs\"],\n [0x517b, \"smb\"],\n [0xff534d42, \"cifs\"],\n [0x65735546, \"fuse\"],\n]);\n\n/**\n * T5.8 — Map a `statfs().type` magic number to a network-FS label, or\n * `null` for local filesystems. Pure function — exported via the\n * `__TESTING__` seam so unit tests can drive the parse logic without\n * needing a network mount.\n *\n * @internal\n */\nfunction detectNetworkFsName(typeMagic: number): string | null {\n return NETWORK_FS_MAGIC.get(typeMagic) ?? null;\n}\n\nconst warnedNfsDirs = new Set<string>();\n\n/**\n * T5.8 — Best-effort one-shot stderr warning when `dirPath` lives on a\n * network/FUSE filesystem. Silent no-op on local filesystems, on\n * statfs failure (Windows / Node < 18.15 / EACCES), or after the\n * first warning per (dir + label) pair. Mirrors the `sqlite-wal.ts`\n * warn-once-per-label pattern (D63).\n *\n * @internal\n */\nasync function warnOnNetworkFsOnce(dirPath: string, label: string): Promise<void> {\n const key = `${dirPath}\\0${label}`;\n if (warnedNfsDirs.has(key)) return;\n warnedNfsDirs.add(key);\n try {\n const info = await statfs(dirPath);\n const fsName = detectNetworkFsName(info.type);\n if (fsName === null) return;\n process.stderr.write(\n `[theokit-sdk] ${label}: detected network fs (${fsName}) at ${dirPath} — ` +\n \"rename() atomicity guarantees may be weaker than expected.\\n\",\n );\n } catch {\n // statfs unavailable (Windows / Node < 18.15) or unreadable —\n // silent fallback. The warning is purely informational.\n }\n}\n\n/**\n * T5.8 — Test seam exposing the pure detection function so unit tests\n * can assert magic-number coverage without spinning up a network FS.\n * NOT included in the public barrel.\n *\n * @internal\n */\nexport function __TESTING__detectNetworkFsName(typeMagic: number): string | null {\n return detectNetworkFsName(typeMagic);\n}\n\n/**\n * T5.8 — Test seam: clear the per-directory warn-once registry between\n * tests so warning-emission tests stay deterministic.\n *\n * @internal\n */\nexport function __TESTING__resetNfsWarnings(): void {\n warnedNfsDirs.clear();\n}\n\n/**\n * Atomic file replacement: write content to a per-call unique tmp path,\n * fsync, then rename over the target. Crash mid-write leaves either the old\n * file intact or the new file complete — never a half-written file.\n *\n * The tmp suffix is `<pid>.<rand>.tmp` so parallel processes (and concurrent\n * burst writes within one process) never collide on the same tmp path — a\n * race that would manifest as `ENOENT` on `rename` after the rival process\n * already moved its tmp into place.\n *\n * Mirrors OpenClaw's `replaceFileAtomic` from\n * `referencia/openclaw/packages/memory-host-sdk/src/host/fs-utils.ts` with\n * the multi-writer robustness fix.\n *\n * @internal\n */\nexport async function replaceFileAtomic(filePath: string, content: string): Promise<void> {\n // T5.8 — warn once per parent directory if it lives on a network /\n // FUSE filesystem where `rename()` atomicity is best-effort. The\n // write proceeds unchanged; the warning is purely informational so\n // operators can spot the case in stderr / log aggregators.\n await warnOnNetworkFsOnce(dirname(filePath), \"atomic-write\");\n // T5.7 — crypto-random tmp suffix (CSPRNG, 64 bits of entropy)\n // replaces the predictable `Math.random().toString(36)` source. An\n // attacker observing the process can no longer predict the next\n // tmp path and pre-stage a hostile file to be renamed into place.\n const suffix = randomBytes(8).toString(\"hex\");\n const tmp = `${filePath}.${process.pid}.${suffix}.tmp`;\n // T5.7 — mode 0o600 on the tmp file (owner read+write only). The\n // tmp file holds the FULL in-flight content (credential snapshots,\n // OAuth tokens) before the rename. World-readable default would\n // expose secrets during the ms-window between open and rename\n // (TOCTOU). On modern Linux the post-rename target inherits the\n // tmp's permission bits, so the final file is also 0o600.\n const handle = await open(tmp, \"w\", 0o600);\n try {\n await handle.writeFile(content, \"utf8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n try {\n await rename(tmp, filePath);\n } catch (cause) {\n // Cleanup tmp on rename failure so we don't leak stale .tmp files.\n await unlink(tmp).catch(() => undefined);\n throw cause;\n }\n}\n\n/**\n * Options for `atomicWriteJson`.\n *\n * @internal\n */\nexport interface AtomicWriteJsonOptions {\n /** Indent passed to `JSON.stringify`. Default: 2. */\n indent?: number;\n /** Whether to append a trailing newline (POSIX convention). Default: true. */\n trailingNewline?: boolean;\n}\n\n/**\n * Typed JSON atomic write helper.\n *\n * Serializes `data` to JSON, then delegates to `replaceFileAtomic`. The\n * parent directory is auto-created (recursive `mkdir`) to make this helper\n * safe for callers who haven't ensured the directory exists (EC-4 in the\n * persistence-state-hardening plan).\n *\n * Throws `TypeError` on circular refs or `undefined` data (propagates from\n * `JSON.stringify`).\n *\n * @internal\n */\nexport async function atomicWriteJson<T>(\n filePath: string,\n data: T,\n options?: AtomicWriteJsonOptions,\n): Promise<void> {\n const indent = options?.indent ?? 2;\n const trailingNewline = options?.trailingNewline ?? true;\n const json = JSON.stringify(data, null, indent);\n if (json === undefined) {\n throw new TypeError(\"atomicWriteJson: cannot serialize undefined\");\n }\n const content = trailingNewline ? `${json}\\n` : json;\n await mkdir(dirname(filePath), { recursive: true });\n await replaceFileAtomic(filePath, content);\n}\n\n/**\n * Atomic text write. Same crash-safety guarantees as `replaceFileAtomic` +\n * auto-mkdir of the parent directory. Used by `theokit-migrate-config`\n * (T4.1, EC-2 MUST FIX) so a crash mid-migration leaves previous MD files\n * intact rather than corrupting them.\n *\n * @internal\n */\nexport async function atomicWriteText(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n await replaceFileAtomic(filePath, content);\n}\n","/**\n * `TaskStore` interface + 2 implementations (ADR D364).\n *\n * - `InMemoryTaskStore` — default, transient, single-process.\n * - `JsonFileTaskStore` — opt-in, one JSON file per task under a\n * dedicated dir; single-process invariant (EC-15 documented).\n *\n * Edge cases absorbed:\n * - EC-1: constructor auto-creates dir (mkdirSync recursive idempotent).\n * - EC-2: every method validates the task id against the public\n * grammar (D368) BEFORE doing any path I/O — path-traversal defense.\n * - EC-6: `list()` returns `[]` on ENOENT (fresh install path).\n * - EC-8: `list()` skips `.tmp.*` orphan files left by interrupted\n * atomic writes.\n * - EC-14 (DOCUMENT): `list()` JSDoc documents the 256-row hard cap\n * and the `submittedBefore` paging idiom.\n * - EC-15 (DOCUMENT): `JsonFileTaskStore` JSDoc documents the\n * single-process invariant; v0.2 SQLite covers cross-process.\n *\n * @internal\n */\n\nimport { mkdirSync, readdirSync } from \"node:fs\";\nimport { readFile, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { InvalidTaskIdError } from \"../../errors.js\";\nimport {\n isValidTaskId,\n type TaskFilter,\n type TaskHandle,\n type TaskState,\n type TaskStoreOptions,\n} from \"../../types/task.js\";\nimport { atomicWriteText } from \"../persistence/atomic-write.js\";\n\nconst JSON_LOAD_CAP = 256;\nconst DEFAULT_LIST_LIMIT = 100;\n\n/** Storage interface used by `TaskRegistry`. */\nexport interface TaskStore {\n insert(handle: TaskHandle): Promise<void>;\n update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined>;\n get(id: string): Promise<TaskHandle | undefined>;\n /**\n * Returns at most `filter.limit ?? 100` matching handles. JsonFile\n * backend hard-caps loaded entries at 256 — callers needing larger\n * pages must use `submittedBefore` to walk the timeline.\n */\n list(filter: TaskFilter): Promise<TaskHandle[]>;\n delete(id: string): Promise<boolean>;\n /** Removes terminal handles whose terminal-timestamp is older than `epochMs`. */\n evictTerminalOlderThan(epochMs: number): Promise<number>;\n}\n\nfunction assertValidIdForStore(id: string): void {\n if (!isValidTaskId(id, /* allowReserved */ true)) {\n throw new InvalidTaskIdError(`store rejects invalid task id: ${id}`, id);\n }\n}\n\nfunction terminalTimestamp(h: TaskHandle): number | undefined {\n if (h.state === \"finished\") return h.finishedAt;\n if (h.state === \"error\") return h.erroredAt;\n if (h.state === \"cancelled\") return h.cancelledAt;\n return undefined;\n}\n\nfunction isTerminal(state: TaskState): boolean {\n return state === \"finished\" || state === \"error\" || state === \"cancelled\";\n}\n\nfunction matchesState(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.state === undefined) return true;\n const states = Array.isArray(filter.state) ? filter.state : [filter.state];\n return states.includes(h.state);\n}\nfunction matchesKind(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.kind === undefined) return true;\n const kinds = Array.isArray(filter.kind) ? filter.kind : [filter.kind];\n return kinds.includes(h.kind);\n}\nfunction matchesTime(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.submittedAfter !== undefined && h.submittedAt <= filter.submittedAfter) return false;\n if (filter.submittedBefore !== undefined && h.submittedAt >= filter.submittedBefore) return false;\n return true;\n}\nfunction matchesFilter(h: TaskHandle, filter: TaskFilter): boolean {\n return matchesState(h, filter) && matchesKind(h, filter) && matchesTime(h, filter);\n}\n\nfunction applyFilter(values: Iterable<TaskHandle>, filter: TaskFilter): TaskHandle[] {\n const limit = filter.limit ?? DEFAULT_LIST_LIMIT;\n const out: TaskHandle[] = [];\n for (const h of values) {\n if (matchesFilter(h, filter)) {\n out.push(h);\n if (out.length >= limit) break;\n }\n }\n return out;\n}\n\n/* ─── InMemory ─── */\n\nexport class InMemoryTaskStore implements TaskStore {\n private readonly map = new Map<string, TaskHandle>();\n\n async insert(handle: TaskHandle): Promise<void> {\n assertValidIdForStore(handle.id);\n this.map.set(handle.id, handle);\n }\n\n async update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n const existing = this.map.get(id);\n if (existing === undefined) return undefined;\n const next = mutate(existing);\n this.map.set(id, next);\n return next;\n }\n\n async get(id: string): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n return this.map.get(id);\n }\n\n async list(filter: TaskFilter): Promise<TaskHandle[]> {\n return applyFilter(this.map.values(), filter);\n }\n\n async delete(id: string): Promise<boolean> {\n assertValidIdForStore(id);\n return this.map.delete(id);\n }\n\n async evictTerminalOlderThan(epochMs: number): Promise<number> {\n let count = 0;\n for (const [id, h] of this.map.entries()) {\n if (!isTerminal(h.state)) continue;\n const ts = terminalTimestamp(h);\n if (ts !== undefined && ts < epochMs) {\n this.map.delete(id);\n count++;\n }\n }\n return count;\n }\n}\n\n/* ─── JsonFile (opt-in) ───\n *\n * IMPORTANT (EC-15): `JsonFileTaskStore` is **single-process**.\n * Concurrent writers from multiple Node processes against the same\n * directory may corrupt entries (atomic-write protects per-file, but\n * the `list()` scan + `update()` read-modify-write cycle is not\n * serialised across processes). Use only when the `TaskRegistry`\n * runs in exactly one process. v0.2 will add a SQLite backend with\n * the same interface for cross-process scenarios (D364 + D61).\n */\nexport class JsonFileTaskStore implements TaskStore {\n constructor(private readonly dir: string) {\n // EC-1: idempotent mkdir; ignore EEXIST.\n try {\n mkdirSync(dir, { recursive: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n }\n\n private filePath(id: string): string {\n return join(this.dir, `${id}.json`);\n }\n\n async insert(handle: TaskHandle): Promise<void> {\n assertValidIdForStore(handle.id);\n await atomicWriteText(this.filePath(handle.id), JSON.stringify(handle));\n }\n\n async update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n const existing = await this.get(id);\n if (existing === undefined) return undefined;\n const next = mutate(existing);\n await atomicWriteText(this.filePath(id), JSON.stringify(next));\n return next;\n }\n\n async get(id: string): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n try {\n const raw = await readFile(this.filePath(id), \"utf8\");\n return JSON.parse(raw) as TaskHandle;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return undefined;\n // Corrupt JSON or other I/O error — log + degrade gracefully (D50/EC-7 cache pattern).\n process.stderr.write(`[task-store] failed to read ${id}: ${(err as Error).message}\\n`);\n return undefined;\n }\n }\n\n async list(filter: TaskFilter): Promise<TaskHandle[]> {\n let entries: string[];\n try {\n entries = readdirSync(this.dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return []; // EC-6\n throw err;\n }\n // EC-8: skip orphan .tmp files left by interrupted atomic writes.\n const candidates = entries\n .filter((name) => name.endsWith(\".json\") && !name.includes(\".tmp\"))\n .slice(0, JSON_LOAD_CAP);\n\n const loaded = await Promise.all(\n candidates.map(async (name) => {\n const id = name.slice(0, -\".json\".length);\n if (!isValidTaskId(id, true)) return undefined;\n return this.get(id);\n }),\n );\n const handles = loaded.filter((h): h is TaskHandle => h !== undefined);\n return applyFilter(handles, filter);\n }\n\n async delete(id: string): Promise<boolean> {\n assertValidIdForStore(id);\n try {\n await unlink(this.filePath(id));\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n }\n\n async evictTerminalOlderThan(epochMs: number): Promise<number> {\n const handles = await this.list({ state: [\"finished\", \"error\", \"cancelled\"], limit: 256 });\n let count = 0;\n for (const h of handles) {\n const ts = terminalTimestamp(h);\n if (ts !== undefined && ts < epochMs) {\n if (await this.delete(h.id)) count++;\n }\n }\n return count;\n }\n}\n\n/** Factory used by `TaskRegistry.configure` (D364). */\nexport function getTaskStoreFor(options: TaskStoreOptions): TaskStore {\n if (options.backend === \"memory\") return new InMemoryTaskStore();\n return new JsonFileTaskStore(options.dir);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/types/task.ts","../src/internal/persistence/atomic-write.ts","../src/internal/task/store.ts"],"names":["statfs","dirname","randomBytes","open","rename","unlink","mkdir","mkdirSync","join","readFile","readdirSync"],"mappings":"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","file":"task-store.cjs","sourcesContent":["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 * Public type contract for the Task observability registry (ADRs D361-D374).\n *\n * Tasks are an opt-in observability layer over async work in the SDK\n * (`Agent.send`, `Agent.batch`, `Workflow.run`, `Cron` fires). They have\n * a closed 5-state lifecycle (D362), discriminated events (D366), and a\n * pluggable store (D364). The `Task` facade in `task.ts` is the public\n * surface; consumers import these types from `@theokit/sdk`.\n *\n * @public\n */\n\n/**\n * Closed enum of the 5 lifecycle states (D362).\n *\n * Transitions are acyclic:\n * queued → running → (finished | error | cancelled)\n * queued → cancelled (direct, no run started)\n */\nexport type TaskState = \"queued\" | \"running\" | \"finished\" | \"error\" | \"cancelled\";\n\n/** Discriminator of the runtime that produced a task (D374). */\nexport type TaskKind = \"run\" | \"batch\" | \"workflow\" | \"cron\" | \"custom\";\n\n/** Discriminated union of task lifecycle events (D366). */\nexport type TaskEvent =\n | {\n readonly type: \"submitted\";\n readonly taskId: string;\n readonly kind: TaskKind;\n readonly submittedAt: number;\n readonly meta?: Record<string, unknown>;\n /** D372 — flag set on the first yielded event when the ring buffer was at cap. */\n readonly truncated?: boolean;\n }\n | { readonly type: \"started\"; readonly taskId: string; readonly startedAt: number }\n | {\n readonly type: \"progress\";\n readonly taskId: string;\n readonly at: number;\n readonly payload: unknown;\n }\n | {\n readonly type: \"finished\";\n readonly taskId: string;\n readonly finishedAt: number;\n readonly result: unknown;\n }\n | {\n readonly type: \"errored\";\n readonly taskId: string;\n readonly erroredAt: number;\n readonly error: { readonly code: string; readonly message: string };\n }\n | {\n readonly type: \"cancelled\";\n readonly taskId: string;\n readonly cancelledAt: number;\n readonly reason?: string;\n };\n\n/** Public read-only view of a task entry in the registry. */\nexport interface TaskHandle {\n readonly id: string;\n readonly kind: TaskKind;\n readonly state: TaskState;\n readonly submittedAt: number;\n readonly startedAt?: number;\n readonly finishedAt?: number;\n readonly cancelledAt?: number;\n readonly erroredAt?: number;\n readonly result?: unknown;\n readonly error?: { readonly code: string; readonly message: string };\n readonly meta?: Record<string, unknown>;\n /**\n * EC-7 — cross-process best-effort cancel flag. Set by CLI\n * `theokit tasks cancel` via JsonFileTaskStore. The owning process\n * polls at checkpoints and honors via AbortController. Always\n * `undefined` for in-process cancel paths (which go directly through\n * AbortController).\n */\n readonly cancelRequested?: boolean;\n}\n\n/** Query filter for `Task.list`. */\nexport interface TaskFilter {\n readonly state?: TaskState | readonly TaskState[];\n readonly kind?: TaskKind | readonly TaskKind[];\n readonly submittedAfter?: number;\n readonly submittedBefore?: number;\n /** Defaults to 100. JsonFileTaskStore hard-caps loaded entries at 256 (D364). */\n readonly limit?: number;\n}\n\n/** Options for `Task.submit`. */\nexport interface TaskSubmitOptions {\n /**\n * Optional user-supplied ID. Must match grammar `^[a-z0-9][a-z0-9_-]*$`\n * and MUST NOT start with the reserved prefixes `wf-` / `b-` / `cron-`\n * (D368, EC-5). When omitted, `crypto.randomUUID()` is used.\n */\n readonly id?: string;\n readonly meta?: Record<string, unknown>;\n /**\n * Optional caller-provided AbortSignal. If already aborted at submit\n * time, the registry short-circuits to `cancelled` without acquiring\n * a semaphore slot (EC-4).\n */\n readonly signal?: AbortSignal;\n}\n\n/** Options shape for `TaskStore` factory (D364). */\nexport type TaskStoreOptions =\n | { readonly backend: \"memory\" }\n | { readonly backend: \"json\"; readonly dir: string };\n\n/** Result of `Task.cancel` (D365 — idempotent). */\nexport interface TaskCancelResult {\n readonly cancelled: boolean;\n readonly alreadyTerminal: boolean;\n}\n\n/**\n * Grammar for user-supplied task IDs (D368).\n * `crypto.randomUUID()` outputs do NOT match this (UUIDs have dashes\n * AND uppercase letters in their canonical form on some Node versions),\n * but the registry normalizes auto-generated IDs to lowercase before\n * insertion, so they pass the same regex.\n */\nconst TASK_ID_GRAMMAR = /^[a-z0-9][a-z0-9_-]*$/;\n\n/** Reserved prefixes for adapter-generated IDs (D368, EC-5). */\nconst RESERVED_PREFIXES = [\"wf-\", \"b-\", \"cron-\"] as const;\n\n/**\n * Validates a task ID against the public grammar + reserved prefixes.\n * Throws `InvalidTaskIdError` from `../errors.js` on rejection.\n *\n * Adapter callers (workflow/batch/cron) MUST set `allowReserved: true`\n * to register their own IDs; user-facing surfaces (`Task.submit`,\n * `agent.send({ task: { id } })`) leave it false.\n */\nexport function isValidTaskId(id: string, allowReserved: boolean): boolean {\n if (!TASK_ID_GRAMMAR.test(id)) return false;\n if (allowReserved) return true;\n for (const prefix of RESERVED_PREFIXES) {\n if (id.startsWith(prefix)) return false;\n }\n return true;\n}\n\n/** Re-exported for adapter implementations + tests. */\nexport const TASK_RESERVED_PREFIXES: readonly string[] = RESERVED_PREFIXES;\n","import { randomBytes } from \"node:crypto\";\nimport { mkdir, open, rename, statfs, unlink } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\n// T5.8 — Linux filesystem magic numbers (from `<linux/magic.h>`).\n// Used by `detectNetworkFsName` to identify the parent directory's\n// filesystem type from a `statfs()` return value. The four entries\n// below cover the network/FUSE cases where `rename()` is best-effort\n// rather than strictly atomic; everything else is treated as local.\nconst NETWORK_FS_MAGIC: ReadonlyMap<number, string> = new Map([\n [0x6969, \"nfs\"],\n [0x517b, \"smb\"],\n [0xff534d42, \"cifs\"],\n [0x65735546, \"fuse\"],\n]);\n\n/**\n * T5.8 — Map a `statfs().type` magic number to a network-FS label, or\n * `null` for local filesystems. Pure function — exported via the\n * `__TESTING__` seam so unit tests can drive the parse logic without\n * needing a network mount.\n *\n * @internal\n */\nfunction detectNetworkFsName(typeMagic: number): string | null {\n return NETWORK_FS_MAGIC.get(typeMagic) ?? null;\n}\n\nconst warnedNfsDirs = new Set<string>();\n\n/**\n * T5.8 — Best-effort one-shot stderr warning when `dirPath` lives on a\n * network/FUSE filesystem. Silent no-op on local filesystems, on\n * statfs failure (Windows / Node < 18.15 / EACCES), or after the\n * first warning per (dir + label) pair. Mirrors the `sqlite-wal.ts`\n * warn-once-per-label pattern (D63).\n *\n * @internal\n */\nasync function warnOnNetworkFsOnce(dirPath: string, label: string): Promise<void> {\n const key = `${dirPath}\\0${label}`;\n if (warnedNfsDirs.has(key)) return;\n warnedNfsDirs.add(key);\n try {\n const info = await statfs(dirPath);\n const fsName = detectNetworkFsName(info.type);\n if (fsName === null) return;\n process.stderr.write(\n `[theokit-sdk] ${label}: detected network fs (${fsName}) at ${dirPath} — ` +\n \"rename() atomicity guarantees may be weaker than expected.\\n\",\n );\n } catch {\n // statfs unavailable (Windows / Node < 18.15) or unreadable —\n // silent fallback. The warning is purely informational.\n }\n}\n\n/**\n * T5.8 — Test seam exposing the pure detection function so unit tests\n * can assert magic-number coverage without spinning up a network FS.\n * NOT included in the public barrel.\n *\n * @internal\n */\nexport function __TESTING__detectNetworkFsName(typeMagic: number): string | null {\n return detectNetworkFsName(typeMagic);\n}\n\n/**\n * T5.8 — Test seam: clear the per-directory warn-once registry between\n * tests so warning-emission tests stay deterministic.\n *\n * @internal\n */\nexport function __TESTING__resetNfsWarnings(): void {\n warnedNfsDirs.clear();\n}\n\n/**\n * Atomic file replacement: write content to a per-call unique tmp path,\n * fsync, then rename over the target. Crash mid-write leaves either the old\n * file intact or the new file complete — never a half-written file.\n *\n * The tmp suffix is `<pid>.<rand>.tmp` so parallel processes (and concurrent\n * burst writes within one process) never collide on the same tmp path — a\n * race that would manifest as `ENOENT` on `rename` after the rival process\n * already moved its tmp into place.\n *\n * Mirrors OpenClaw's `replaceFileAtomic` from\n * `referencia/openclaw/packages/memory-host-sdk/src/host/fs-utils.ts` with\n * the multi-writer robustness fix.\n *\n * @internal\n */\nexport async function replaceFileAtomic(filePath: string, content: string): Promise<void> {\n // T5.8 — warn once per parent directory if it lives on a network /\n // FUSE filesystem where `rename()` atomicity is best-effort. The\n // write proceeds unchanged; the warning is purely informational so\n // operators can spot the case in stderr / log aggregators.\n await warnOnNetworkFsOnce(dirname(filePath), \"atomic-write\");\n // T5.7 — crypto-random tmp suffix (CSPRNG, 64 bits of entropy)\n // replaces the predictable `Math.random().toString(36)` source. An\n // attacker observing the process can no longer predict the next\n // tmp path and pre-stage a hostile file to be renamed into place.\n const suffix = randomBytes(8).toString(\"hex\");\n const tmp = `${filePath}.${process.pid}.${suffix}.tmp`;\n // T5.7 — mode 0o600 on the tmp file (owner read+write only). The\n // tmp file holds the FULL in-flight content (credential snapshots,\n // OAuth tokens) before the rename. World-readable default would\n // expose secrets during the ms-window between open and rename\n // (TOCTOU). On modern Linux the post-rename target inherits the\n // tmp's permission bits, so the final file is also 0o600.\n const handle = await open(tmp, \"w\", 0o600);\n try {\n await handle.writeFile(content, \"utf8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n try {\n await rename(tmp, filePath);\n } catch (cause) {\n // Cleanup tmp on rename failure so we don't leak stale .tmp files.\n await unlink(tmp).catch(() => undefined);\n throw cause;\n }\n}\n\n/**\n * Options for `atomicWriteJson`.\n *\n * @internal\n */\nexport interface AtomicWriteJsonOptions {\n /** Indent passed to `JSON.stringify`. Default: 2. */\n indent?: number;\n /** Whether to append a trailing newline (POSIX convention). Default: true. */\n trailingNewline?: boolean;\n}\n\n/**\n * Typed JSON atomic write helper.\n *\n * Serializes `data` to JSON, then delegates to `replaceFileAtomic`. The\n * parent directory is auto-created (recursive `mkdir`) to make this helper\n * safe for callers who haven't ensured the directory exists (EC-4 in the\n * persistence-state-hardening plan).\n *\n * Throws `TypeError` on circular refs or `undefined` data (propagates from\n * `JSON.stringify`).\n *\n * @internal\n */\nexport async function atomicWriteJson<T>(\n filePath: string,\n data: T,\n options?: AtomicWriteJsonOptions,\n): Promise<void> {\n const indent = options?.indent ?? 2;\n const trailingNewline = options?.trailingNewline ?? true;\n const json = JSON.stringify(data, null, indent);\n if (json === undefined) {\n throw new TypeError(\"atomicWriteJson: cannot serialize undefined\");\n }\n const content = trailingNewline ? `${json}\\n` : json;\n await mkdir(dirname(filePath), { recursive: true });\n await replaceFileAtomic(filePath, content);\n}\n\n/**\n * Atomic text write. Same crash-safety guarantees as `replaceFileAtomic` +\n * auto-mkdir of the parent directory. Used by `theokit-migrate-config`\n * (T4.1, EC-2 MUST FIX) so a crash mid-migration leaves previous MD files\n * intact rather than corrupting them.\n *\n * @internal\n */\nexport async function atomicWriteText(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n await replaceFileAtomic(filePath, content);\n}\n","/**\n * `TaskStore` interface + 2 implementations (ADR D364).\n *\n * - `InMemoryTaskStore` — default, transient, single-process.\n * - `JsonFileTaskStore` — opt-in, one JSON file per task under a\n * dedicated dir; single-process invariant (EC-15 documented).\n *\n * Edge cases absorbed:\n * - EC-1: constructor auto-creates dir (mkdirSync recursive idempotent).\n * - EC-2: every method validates the task id against the public\n * grammar (D368) BEFORE doing any path I/O — path-traversal defense.\n * - EC-6: `list()` returns `[]` on ENOENT (fresh install path).\n * - EC-8: `list()` skips `.tmp.*` orphan files left by interrupted\n * atomic writes.\n * - EC-14 (DOCUMENT): `list()` JSDoc documents the 256-row hard cap\n * and the `submittedBefore` paging idiom.\n * - EC-15 (DOCUMENT): `JsonFileTaskStore` JSDoc documents the\n * single-process invariant; v0.2 SQLite covers cross-process.\n *\n * @internal\n */\n\nimport { mkdirSync, readdirSync } from \"node:fs\";\nimport { readFile, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { InvalidTaskIdError } from \"../../errors.js\";\nimport {\n isValidTaskId,\n type TaskFilter,\n type TaskHandle,\n type TaskState,\n type TaskStoreOptions,\n} from \"../../types/task.js\";\nimport { atomicWriteText } from \"../persistence/atomic-write.js\";\n\nconst JSON_LOAD_CAP = 256;\nconst DEFAULT_LIST_LIMIT = 100;\n\n/** Storage interface used by `TaskRegistry`. */\nexport interface TaskStore {\n insert(handle: TaskHandle): Promise<void>;\n update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined>;\n get(id: string): Promise<TaskHandle | undefined>;\n /**\n * Returns at most `filter.limit ?? 100` matching handles. JsonFile\n * backend hard-caps loaded entries at 256 — callers needing larger\n * pages must use `submittedBefore` to walk the timeline.\n */\n list(filter: TaskFilter): Promise<TaskHandle[]>;\n delete(id: string): Promise<boolean>;\n /** Removes terminal handles whose terminal-timestamp is older than `epochMs`. */\n evictTerminalOlderThan(epochMs: number): Promise<number>;\n}\n\nfunction assertValidIdForStore(id: string): void {\n if (!isValidTaskId(id, /* allowReserved */ true)) {\n throw new InvalidTaskIdError(`store rejects invalid task id: ${id}`, id);\n }\n}\n\nfunction terminalTimestamp(h: TaskHandle): number | undefined {\n if (h.state === \"finished\") return h.finishedAt;\n if (h.state === \"error\") return h.erroredAt;\n if (h.state === \"cancelled\") return h.cancelledAt;\n return undefined;\n}\n\nfunction isTerminal(state: TaskState): boolean {\n return state === \"finished\" || state === \"error\" || state === \"cancelled\";\n}\n\nfunction matchesState(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.state === undefined) return true;\n const states = Array.isArray(filter.state) ? filter.state : [filter.state];\n return states.includes(h.state);\n}\nfunction matchesKind(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.kind === undefined) return true;\n const kinds = Array.isArray(filter.kind) ? filter.kind : [filter.kind];\n return kinds.includes(h.kind);\n}\nfunction matchesTime(h: TaskHandle, filter: TaskFilter): boolean {\n if (filter.submittedAfter !== undefined && h.submittedAt <= filter.submittedAfter) return false;\n if (filter.submittedBefore !== undefined && h.submittedAt >= filter.submittedBefore) return false;\n return true;\n}\nfunction matchesFilter(h: TaskHandle, filter: TaskFilter): boolean {\n return matchesState(h, filter) && matchesKind(h, filter) && matchesTime(h, filter);\n}\n\nfunction applyFilter(values: Iterable<TaskHandle>, filter: TaskFilter): TaskHandle[] {\n const limit = filter.limit ?? DEFAULT_LIST_LIMIT;\n const out: TaskHandle[] = [];\n for (const h of values) {\n if (matchesFilter(h, filter)) {\n out.push(h);\n if (out.length >= limit) break;\n }\n }\n return out;\n}\n\n/* ─── InMemory ─── */\n\nexport class InMemoryTaskStore implements TaskStore {\n private readonly map = new Map<string, TaskHandle>();\n\n async insert(handle: TaskHandle): Promise<void> {\n assertValidIdForStore(handle.id);\n this.map.set(handle.id, handle);\n }\n\n async update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n const existing = this.map.get(id);\n if (existing === undefined) return undefined;\n const next = mutate(existing);\n this.map.set(id, next);\n return next;\n }\n\n async get(id: string): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n return this.map.get(id);\n }\n\n async list(filter: TaskFilter): Promise<TaskHandle[]> {\n return applyFilter(this.map.values(), filter);\n }\n\n async delete(id: string): Promise<boolean> {\n assertValidIdForStore(id);\n return this.map.delete(id);\n }\n\n async evictTerminalOlderThan(epochMs: number): Promise<number> {\n let count = 0;\n for (const [id, h] of this.map.entries()) {\n if (!isTerminal(h.state)) continue;\n const ts = terminalTimestamp(h);\n if (ts !== undefined && ts < epochMs) {\n this.map.delete(id);\n count++;\n }\n }\n return count;\n }\n}\n\n/* ─── JsonFile (opt-in) ───\n *\n * IMPORTANT (EC-15): `JsonFileTaskStore` is **single-process**.\n * Concurrent writers from multiple Node processes against the same\n * directory may corrupt entries (atomic-write protects per-file, but\n * the `list()` scan + `update()` read-modify-write cycle is not\n * serialised across processes). Use only when the `TaskRegistry`\n * runs in exactly one process. v0.2 will add a SQLite backend with\n * the same interface for cross-process scenarios (D364 + D61).\n */\nexport class JsonFileTaskStore implements TaskStore {\n constructor(private readonly dir: string) {\n // EC-1: idempotent mkdir; ignore EEXIST.\n try {\n mkdirSync(dir, { recursive: true });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n }\n\n private filePath(id: string): string {\n return join(this.dir, `${id}.json`);\n }\n\n async insert(handle: TaskHandle): Promise<void> {\n assertValidIdForStore(handle.id);\n await atomicWriteText(this.filePath(handle.id), JSON.stringify(handle));\n }\n\n async update(id: string, mutate: (h: TaskHandle) => TaskHandle): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n const existing = await this.get(id);\n if (existing === undefined) return undefined;\n const next = mutate(existing);\n await atomicWriteText(this.filePath(id), JSON.stringify(next));\n return next;\n }\n\n async get(id: string): Promise<TaskHandle | undefined> {\n assertValidIdForStore(id);\n try {\n const raw = await readFile(this.filePath(id), \"utf8\");\n return JSON.parse(raw) as TaskHandle;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") return undefined;\n // Corrupt JSON or other I/O error — log + degrade gracefully (D50/EC-7 cache pattern).\n process.stderr.write(`[task-store] failed to read ${id}: ${(err as Error).message}\\n`);\n return undefined;\n }\n }\n\n async list(filter: TaskFilter): Promise<TaskHandle[]> {\n let entries: string[];\n try {\n entries = readdirSync(this.dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return []; // EC-6\n throw err;\n }\n // EC-8: skip orphan .tmp files left by interrupted atomic writes.\n const candidates = entries\n .filter((name) => name.endsWith(\".json\") && !name.includes(\".tmp\"))\n .slice(0, JSON_LOAD_CAP);\n\n const loaded = await Promise.all(\n candidates.map(async (name) => {\n const id = name.slice(0, -\".json\".length);\n if (!isValidTaskId(id, true)) return undefined;\n return this.get(id);\n }),\n );\n const handles = loaded.filter((h): h is TaskHandle => h !== undefined);\n return applyFilter(handles, filter);\n }\n\n async delete(id: string): Promise<boolean> {\n assertValidIdForStore(id);\n try {\n await unlink(this.filePath(id));\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n }\n\n async evictTerminalOlderThan(epochMs: number): Promise<number> {\n const handles = await this.list({ state: [\"finished\", \"error\", \"cancelled\"], limit: 256 });\n let count = 0;\n for (const h of handles) {\n const ts = terminalTimestamp(h);\n if (ts !== undefined && ts < epochMs) {\n if (await this.delete(h.id)) count++;\n }\n }\n return count;\n }\n}\n\n/** Factory used by `TaskRegistry.configure` (D364). */\nexport function getTaskStoreFor(options: TaskStoreOptions): TaskStore {\n if (options.backend === \"memory\") return new InMemoryTaskStore();\n return new JsonFileTaskStore(options.dir);\n}\n"]}
|