@workerdeck/queue 1.0.0 → 1.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/build/index.d.mts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { CreateJobRequest, CreateSessionRequest, JobEvent, JobInfo, QueueStats } from "@workerdeck/protocol";
2
2
  import { Runner, SessionRunnerConfig } from "@workerdeck/core";
3
-
4
3
  //#region src/adapter.d.ts
5
4
  type JobRecord = {
6
5
  info: JobInfo;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["#jobs","#dailyTokens","#options","#adapter","#offWork","#pump","#sweepTimer","#sweep","#closed","#emit","#bySession","#running","#parked","#kill","#recordPark","#effectiveDurationLimit","#handleEvent","#recordResume","#finalize","#abortPatch","#retryTimers","#pumping","#start","#effectiveTokenLimit","#progress","#deliver"],"sources":["../src/adapter.ts","../src/queue.ts"],"sourcesContent":["import type { CreateJobRequest, JobInfo } from '@workerdeck/protocol'\n\nexport type JobRecord = {\n info: JobInfo\n request: CreateJobRequest\n}\n\nexport interface QueueAdapter {\n add(job: JobRecord): Promise<void>\n claimNext(): Promise<JobRecord | null>\n get(id: string): Promise<JobRecord | null>\n list(): Promise<JobRecord[]>\n update(id: string, patch: Partial<JobInfo>): Promise<JobRecord | null>\n prune(olderThanMs: number): Promise<number>\n addDailyTokens(dayKey: string, tokens: number): Promise<number>\n dailyTokens(dayKey: string): Promise<number>\n onWork?(listener: () => void): () => void\n}\n\nexport class InMemoryQueueAdapter implements QueueAdapter {\n #jobs = new Map<string, JobRecord>()\n #dailyTokens = new Map<string, number>()\n\n add(job: JobRecord): Promise<void> {\n this.#jobs.set(job.info.id, job)\n return Promise.resolve()\n }\n\n claimNext(): Promise<JobRecord | null> {\n const now = Date.now()\n for (const job of this.#jobs.values()) {\n if (job.info.status === 'queued' && (job.info.nextRunAt === undefined || job.info.nextRunAt <= now)) {\n job.info = { ...job.info, status: 'running' }\n return Promise.resolve(job)\n }\n }\n return Promise.resolve(null)\n }\n\n get(id: string): Promise<JobRecord | null> {\n return Promise.resolve(this.#jobs.get(id) ?? null)\n }\n\n list(): Promise<JobRecord[]> {\n return Promise.resolve([...this.#jobs.values()])\n }\n\n update(id: string, patch: Partial<JobInfo>): Promise<JobRecord | null> {\n const job = this.#jobs.get(id)\n if (!job) {\n return Promise.resolve(null)\n }\n job.info = { ...job.info, ...patch }\n return Promise.resolve(job)\n }\n\n prune(olderThanMs: number): Promise<number> {\n const cutoff = Date.now() - olderThanMs\n let removed = 0\n for (const [id, job] of this.#jobs) {\n const { status, finishedAt } = job.info\n const terminal = status === 'succeeded' || status === 'failed' || status === 'canceled'\n if (terminal && (finishedAt ?? 0) <= cutoff) {\n this.#jobs.delete(id)\n removed++\n }\n }\n return Promise.resolve(removed)\n }\n\n addDailyTokens(dayKey: string, tokens: number): Promise<number> {\n const next = (this.#dailyTokens.get(dayKey) ?? 0) + tokens\n this.#dailyTokens.set(dayKey, next)\n return Promise.resolve(next)\n }\n\n dailyTokens(dayKey: string): Promise<number> {\n return Promise.resolve(this.#dailyTokens.get(dayKey) ?? 0)\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { Runner, SessionRunnerConfig } from '@workerdeck/core'\nimport type {\n ApiMessage,\n CreateJobRequest,\n CreateSessionRequest,\n JobEvent,\n JobInfo,\n JobProgress,\n QueueStats,\n SessionEvent,\n} from '@workerdeck/protocol'\nimport { InMemoryQueueAdapter, type JobRecord, type QueueAdapter } from './adapter.ts'\n\nexport type JobQueueOptions = {\n createRunner: (config: SessionRunnerConfig) => Runner | Promise<Runner>\n adapter?: QueueAdapter\n maxConcurrency?: number\n sessionTokenLimit?: number\n dailyTokenLimit?: number\n maxJobDurationMs?: number\n maxParkedDurationMs?: number\n killGraceMs?: number\n retention?: { maxAgeMs: number; sweepIntervalMs?: number }\n buildRunnerConfig?: (req: CreateSessionRequest) => SessionRunnerConfig\n discardSession?: (sessionId: string) => void | Promise<void>\n fetchImpl?: typeof fetch\n webhookAttempts?: number\n webhookRetryDelayMs?: number\n onEvent?: (event: JobEvent) => void\n}\n\ntype RunningJob = {\n record: JobRecord\n runner: Runner\n unsubscribe: () => void\n lastSeq: number\n estimatedTokens: number\n killReason?: string\n canceled: boolean\n finalized: boolean\n deliveries: Promise<void>\n durationTimer?: ReturnType<typeof setTimeout>\n forceTimer?: ReturnType<typeof setTimeout>\n runningMs: number\n legStartedAt: number\n parkedAt?: number\n parkedExecutionId?: string\n parkTimer?: ReturnType<typeof setTimeout>\n parkedMs: number\n}\n\nfunction dayKey(epochMs: number): string {\n return new Date(epochMs).toISOString().slice(0, 10)\n}\n\nfunction sumUsage(usage: unknown): number {\n if (typeof usage !== 'object' || usage === null) {\n return 0\n }\n const u = usage as Record<string, unknown>\n return (\n (typeof u.input_tokens === 'number' ? u.input_tokens : 0) +\n (typeof u.output_tokens === 'number' ? u.output_tokens : 0) +\n (typeof u.cache_creation_input_tokens === 'number' ? u.cache_creation_input_tokens : 0) +\n (typeof u.cache_read_input_tokens === 'number' ? u.cache_read_input_tokens : 0)\n )\n}\n\nfunction textPreview(message: ApiMessage, max = 140): JobProgress | null {\n const blocks = typeof message.content === 'string' ? [{ type: 'text', text: message.content }] : message.content\n for (const block of blocks) {\n if (block.type === 'tool_use') {\n return { kind: 'tool_use', preview: (block as { name?: string }).name }\n }\n if (block.type === 'text') {\n const text = (block as { text?: string }).text ?? ''\n if (text.trim()) {\n return {\n kind: 'assistant_text',\n preview: text.length > max ? text.slice(0, max - 1) + '…' : text,\n }\n }\n }\n }\n return null\n}\n\nexport class JobQueue {\n #options: JobQueueOptions\n #adapter: QueueAdapter\n #running = new Map<string, RunningJob>()\n #parked = new Map<string, RunningJob>()\n #pumping = false\n #closed = false\n #offWork: (() => void) | undefined\n #sweepTimer: ReturnType<typeof setInterval> | undefined\n #retryTimers = new Set<ReturnType<typeof setTimeout>>()\n\n constructor(options: JobQueueOptions) {\n this.#options = options\n this.#adapter = options.adapter ?? new InMemoryQueueAdapter()\n this.#offWork = this.#adapter.onWork?.(() => void this.#pump())\n const retention = options.retention\n if (retention) {\n const interval = retention.sweepIntervalMs ?? Math.min(retention.maxAgeMs, 60_000)\n this.#sweepTimer = setInterval(() => this.#sweep(), interval)\n this.#sweepTimer.unref?.()\n }\n }\n\n async submit(request: CreateJobRequest): Promise<JobInfo> {\n if (this.#closed) {\n throw new Error('queue is closed')\n }\n if (!request.session?.prompt?.trim()) {\n throw new Error('session.prompt is required')\n }\n if (request.session.resume || request.session.forkSession) {\n throw new Error('resume/forkSession are not supported for queued jobs')\n }\n const attempts = request.attempts ?? 1\n if (!Number.isInteger(attempts) || attempts < 1) {\n throw new Error('attempts must be a positive integer')\n }\n if (request.retryDelayMs !== undefined && !(request.retryDelayMs >= 0)) {\n throw new Error('retryDelayMs must be >= 0')\n }\n const info: JobInfo = {\n id: randomUUID(),\n status: 'queued',\n cwd: request.session.cwd ?? '',\n profile: request.session.profile,\n prompt: request.session.prompt,\n createdAt: Date.now(),\n attempt: 1,\n maxAttempts: attempts,\n usage: { tokens: 0, totalCostUsd: 0, numTurns: 0 },\n meta: request.meta,\n scope: request.session.scope,\n }\n const record: JobRecord = { info, request }\n await this.#adapter.add(record)\n this.#emit(record, { type: 'job_submitted', job: info, ts: Date.now() }, undefined, {\n skipWebhook: true,\n })\n void this.#pump()\n return info\n }\n\n async get(id: string): Promise<JobInfo | null> {\n return (await this.#adapter.get(id))?.info ?? null\n }\n\n async list(): Promise<JobInfo[]> {\n return (await this.#adapter.list()).map((j) => j.info)\n }\n\n onSessionParking(sessionId: string, executionId: string): boolean {\n const job = this.#bySession(this.#running, sessionId)\n if (!job) {\n return true\n }\n if (job.finalized || job.killReason) {\n return false\n }\n const now = Date.now()\n job.unsubscribe()\n job.runningMs += now - job.legStartedAt\n clearTimeout(job.durationTimer)\n job.durationTimer = undefined\n job.parkedAt = now\n job.parkedExecutionId = executionId\n this.#running.delete(job.record.info.id)\n this.#parked.set(job.record.info.id, job)\n const parkedLimit = this.#options.maxParkedDurationMs\n if (parkedLimit !== undefined) {\n job.parkTimer = setTimeout(\n () => this.#kill(job, `job exceeded max parked duration (${parkedLimit}ms)`),\n Math.max(0, parkedLimit - job.parkedMs),\n )\n job.parkTimer.unref?.()\n }\n void this.#recordPark(job, executionId)\n return true\n }\n\n async #recordPark(job: RunningJob, executionId: string): Promise<void> {\n const updated = await this.#adapter.update(job.record.info.id, {\n status: 'parked',\n parkedAt: job.parkedAt,\n parkedExecutionId: executionId,\n })\n if (updated) {\n job.record = updated\n }\n this.#emit(job.record, { type: 'job_parked', job: job.record.info, executionId, ts: Date.now() }, job)\n void this.#pump()\n }\n\n onSessionResumed(sessionId: string, runner: Runner): void {\n const job = this.#bySession(this.#parked, sessionId)\n if (!job || job.finalized) {\n return\n }\n const now = Date.now()\n const executionId = job.parkedExecutionId ?? ''\n clearTimeout(job.parkTimer)\n job.parkTimer = undefined\n job.parkedMs += now - (job.parkedAt ?? now)\n job.parkedAt = undefined\n job.parkedExecutionId = undefined\n job.legStartedAt = now\n job.runner = runner\n this.#parked.delete(job.record.info.id)\n this.#running.set(job.record.info.id, job)\n const durationLimit = this.#effectiveDurationLimit(job.record.request)\n if (durationLimit !== undefined) {\n job.durationTimer = setTimeout(\n () => this.#kill(job, `job exceeded max duration (${durationLimit}ms)`),\n Math.max(0, durationLimit - job.runningMs),\n )\n job.durationTimer.unref?.()\n }\n job.unsubscribe = runner.subscribe((event) => void this.#handleEvent(job, event), job.lastSeq)\n void this.#recordResume(job, executionId)\n }\n\n async #recordResume(job: RunningJob, executionId: string): Promise<void> {\n const updated = await this.#adapter.update(job.record.info.id, {\n status: 'running',\n parkedAt: undefined,\n parkedExecutionId: undefined,\n })\n if (updated) {\n job.record = updated\n }\n this.#emit(job.record, { type: 'job_resumed', job: job.record.info, executionId, ts: Date.now() }, job)\n }\n\n #bySession(jobs: Map<string, RunningJob>, sessionId: string): RunningJob | undefined {\n for (const job of jobs.values()) {\n if (job.record.info.sessionId === sessionId) {\n return job\n }\n }\n return undefined\n }\n\n async cancel(id: string): Promise<JobInfo | null> {\n const record = await this.#adapter.get(id)\n if (!record) {\n return null\n }\n const running = this.#running.get(id) ?? this.#parked.get(id)\n if (running) {\n running.canceled = true\n running.killReason = 'canceled'\n await this.#finalize(running, this.#abortPatch(running, 'canceled'))\n return running.record.info\n }\n if (record.info.status !== 'queued') {\n return record.info\n }\n const updated = await this.#adapter.update(id, {\n status: 'canceled',\n finishedAt: Date.now(),\n error: 'canceled',\n })\n if (updated) {\n this.#emit(updated, { type: 'job_completed', job: updated.info, ts: Date.now() })\n }\n return updated?.info ?? null\n }\n\n async stats(): Promise<QueueStats> {\n const jobs = await this.#adapter.list()\n const dailyTokensUsed = await this.#adapter.dailyTokens(dayKey(Date.now()))\n const dailyTokenLimit = this.#options.dailyTokenLimit\n return {\n maxConcurrency: this.#options.maxConcurrency ?? 1,\n running: this.#running.size,\n parked: this.#parked.size,\n queued: jobs.filter((j) => j.info.status === 'queued').length,\n sessionTokenLimit: this.#options.sessionTokenLimit,\n dailyTokenLimit,\n dailyTokensUsed,\n paused: dailyTokenLimit !== undefined && dailyTokensUsed >= dailyTokenLimit,\n }\n }\n\n close(): void {\n this.#closed = true\n this.#offWork?.()\n clearInterval(this.#sweepTimer)\n for (const timer of this.#retryTimers) {\n clearTimeout(timer)\n }\n this.#retryTimers.clear()\n }\n\n #sweep(): void {\n const retention = this.#options.retention\n if (!retention) {\n return\n }\n this.#adapter.prune(retention.maxAgeMs).catch(() => {})\n }\n\n async #pump(): Promise<void> {\n if (this.#pumping || this.#closed) {\n return\n }\n this.#pumping = true\n try {\n const maxConcurrency = this.#options.maxConcurrency ?? 1\n while (this.#running.size < maxConcurrency) {\n const limit = this.#options.dailyTokenLimit\n if (limit !== undefined && (await this.#adapter.dailyTokens(dayKey(Date.now()))) >= limit) {\n return\n }\n const record = await this.#adapter.claimNext()\n if (!record) {\n return\n }\n await this.#start(record)\n }\n } finally {\n this.#pumping = false\n }\n }\n\n async #start(record: JobRecord): Promise<void> {\n const id = record.info.id\n const build = this.#options.buildRunnerConfig ?? ((req: CreateSessionRequest) => req)\n let runner: Runner\n try {\n const request: CreateSessionRequest = {\n ...record.request.session,\n meta: { ...record.request.session.meta, jobId: id },\n }\n runner = await this.#options.createRunner(build(request))\n } catch (error) {\n const failed = await this.#adapter.update(id, {\n status: 'failed',\n finishedAt: Date.now(),\n error: error instanceof Error ? error.message : String(error),\n })\n if (failed) {\n this.#emit(failed, { type: 'job_completed', job: failed.info, ts: Date.now() })\n }\n return\n }\n const job: RunningJob = {\n record,\n runner,\n unsubscribe: () => {},\n lastSeq: 0,\n estimatedTokens: 0,\n canceled: false,\n finalized: false,\n deliveries: Promise.resolve(),\n runningMs: 0,\n legStartedAt: Date.now(),\n parkedMs: 0,\n }\n this.#running.set(id, job)\n const updated = await this.#adapter.update(id, {\n startedAt: Date.now(),\n sessionId: runner.id,\n })\n if (updated) {\n job.record = updated\n }\n this.#emit(job.record, { type: 'job_started', job: job.record.info, ts: Date.now() })\n const durationLimit = this.#effectiveDurationLimit(record.request)\n if (durationLimit !== undefined) {\n job.durationTimer = setTimeout(() => this.#kill(job, `job exceeded max duration (${durationLimit}ms)`), durationLimit)\n job.durationTimer.unref?.()\n }\n job.unsubscribe = runner.subscribe((event) => void this.#handleEvent(job, event))\n }\n\n /**\n * How a run that never produced a `turn_result` is written down. There is no authoritative\n * usage for such a run, so it reports the tokens counted from the message stream, no cost\n * and no turns. `turn_result` is the one path that does NOT come through here — it has real\n * numbers — which is exactly the distinction five inline copies of this used to hide.\n */\n #abortPatch(job: RunningJob, error: string): Partial<JobInfo> {\n return {\n usage: { tokens: job.estimatedTokens, totalCostUsd: 0, numTurns: 0 },\n status: job.canceled ? 'canceled' : 'failed',\n error,\n }\n }\n\n #kill(job: RunningJob, reason: string): void {\n if (job.finalized || job.killReason) {\n return\n }\n job.killReason = reason\n if (job.parkedAt !== undefined) {\n void this.#finalize(job, this.#abortPatch(job, reason))\n return\n }\n void job.runner.interrupt().catch(() => {})\n job.forceTimer = setTimeout(() => {\n void this.#finalize(job, this.#abortPatch(job, reason))\n }, this.#options.killGraceMs ?? 5000)\n job.forceTimer.unref?.()\n }\n\n async #handleEvent(job: RunningJob, event: SessionEvent): Promise<void> {\n if (job.finalized) {\n return\n }\n job.lastSeq = Math.max(job.lastSeq, event.seq)\n switch (event.type) {\n case 'system_init': {\n await this.#adapter.update(job.record.info.id, { sdkSessionId: event.sdkSessionId })\n return\n }\n case 'assistant_message': {\n if (event.replay) {\n return\n }\n job.estimatedTokens += sumUsage(event.message.usage)\n const limit = this.#effectiveTokenLimit(job.record.request)\n if (limit !== undefined && job.estimatedTokens > limit) {\n this.#kill(job, `session token limit exceeded (${job.estimatedTokens} > ${limit})`)\n }\n const progress = textPreview(event.message)\n if (progress) {\n this.#progress(job, progress)\n }\n return\n }\n case 'permission_requested': {\n this.#progress(job, {\n kind: 'permission_requested',\n preview: event.request.title ?? event.request.toolName,\n request: event.request,\n })\n return\n }\n case 'permission_resolved': {\n this.#progress(job, { kind: 'permission_resolved', preview: event.behavior })\n return\n }\n case 'turn_result': {\n const tokens = sumUsage(event.usage) || job.estimatedTokens\n await this.#finalize(job, {\n usage: {\n tokens,\n totalCostUsd: event.totalCostUsd,\n numTurns: event.numTurns,\n },\n result: {\n subtype: event.subtype,\n isError: event.isError,\n result: event.result,\n errors: event.errors,\n durationMs: event.durationMs,\n },\n status: job.killReason ? (job.canceled ? 'canceled' : 'failed') : event.isError ? 'failed' : 'succeeded',\n error: job.killReason ?? (event.isError ? event.errors?.join('; ') || event.subtype : undefined),\n })\n return\n }\n case 'session_error': {\n await this.#finalize(job, this.#abortPatch(job, job.killReason ?? event.message))\n return\n }\n case 'session_closed': {\n await this.#finalize(job, this.#abortPatch(job, job.killReason ?? 'session closed before completing'))\n return\n }\n default: {\n return\n }\n }\n }\n\n #effectiveTokenLimit(request: CreateJobRequest): number | undefined {\n const limits = [request.maxTokens, this.#options.sessionTokenLimit].filter((n): n is number => typeof n === 'number')\n return limits.length > 0 ? Math.min(...limits) : undefined\n }\n\n #effectiveDurationLimit(request: CreateJobRequest): number | undefined {\n const limits = [request.maxDurationMs, this.#options.maxJobDurationMs].filter((n): n is number => typeof n === 'number')\n return limits.length > 0 ? Math.min(...limits) : undefined\n }\n\n async #finalize(job: RunningJob, patch: Partial<JobInfo>): Promise<void> {\n if (job.finalized) {\n return\n }\n job.finalized = true\n job.unsubscribe()\n clearTimeout(job.durationTimer)\n clearTimeout(job.forceTimer)\n clearTimeout(job.parkTimer)\n this.#running.delete(job.record.info.id)\n const wasParked = this.#parked.delete(job.record.info.id)\n job.runner.close('server')\n if (wasParked && job.record.info.sessionId) {\n try {\n void Promise.resolve(this.#options.discardSession?.(job.record.info.sessionId)).catch(() => {})\n } catch {}\n }\n const attemptUsage = patch.usage ?? { tokens: 0, totalCostUsd: 0, numTurns: 0 }\n if (attemptUsage.tokens > 0) {\n await this.#adapter.addDailyTokens(dayKey(Date.now()), attemptUsage.tokens)\n }\n const prior = job.record.info.usage\n const usage = {\n tokens: prior.tokens + attemptUsage.tokens,\n totalCostUsd: prior.totalCostUsd + attemptUsage.totalCostUsd,\n numTurns: prior.numTurns + attemptUsage.numTurns,\n }\n const attempt = job.record.info.attempt ?? 1\n const maxAttempts = job.record.request.attempts ?? 1\n if (patch.status === 'failed' && attempt < maxAttempts && !this.#closed) {\n const baseDelay = job.record.request.retryDelayMs ?? 5000\n const delay = baseDelay * 2 ** (attempt - 1)\n const updated = await this.#adapter.update(job.record.info.id, {\n status: 'queued',\n attempt: attempt + 1,\n nextRunAt: Date.now() + delay,\n error: patch.error,\n usage,\n sessionId: undefined,\n sdkSessionId: undefined,\n startedAt: undefined,\n result: undefined,\n })\n if (updated) {\n job.record = updated\n this.#emit(updated, { type: 'job_retrying', job: updated.info, ts: Date.now() }, job)\n const timer = setTimeout(() => {\n this.#retryTimers.delete(timer)\n void this.#pump()\n }, delay)\n timer.unref?.()\n this.#retryTimers.add(timer)\n }\n void this.#pump()\n return\n }\n const updated = await this.#adapter.update(job.record.info.id, {\n ...patch,\n usage,\n nextRunAt: undefined,\n finishedAt: Date.now(),\n })\n if (updated) {\n job.record = updated\n this.#emit(job.record, { type: 'job_completed', job: updated.info, ts: Date.now() }, job)\n }\n this.#sweep()\n void this.#pump()\n }\n\n #progress(job: RunningJob, progress: JobProgress): void {\n const event: JobEvent = { type: 'job_progress', job: job.record.info, progress, ts: Date.now() }\n if (job.record.request.webhook?.progress === 'completion') {\n try {\n this.#options.onEvent?.(event)\n } catch {}\n return\n }\n this.#emit(job.record, event, job)\n }\n\n #emit(record: JobRecord, event: JobEvent, chainOwner?: RunningJob, { skipWebhook = false }: { skipWebhook?: boolean } = {}): void {\n try {\n this.#options.onEvent?.(event)\n } catch {}\n const webhook = record.request.webhook\n if (!webhook || skipWebhook) {\n return\n }\n const running = chainOwner ?? this.#running.get(record.info.id)\n const deliver = () => this.#deliver(webhook.url, webhook.headers, event)\n if (running) {\n running.deliveries = running.deliveries.then(deliver)\n } else {\n void deliver()\n }\n }\n\n async #deliver(url: string, headers: Record<string, string> | undefined, event: JobEvent): Promise<void> {\n const fetchImpl = this.#options.fetchImpl ?? fetch\n const attempts = this.#options.webhookAttempts ?? 3\n const baseDelay = this.#options.webhookRetryDelayMs ?? 500\n for (let attempt = 0; attempt < attempts; attempt++) {\n try {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...headers },\n body: JSON.stringify(event),\n })\n if (res.ok) {\n return\n }\n } catch {}\n if (attempt < attempts - 1) {\n await new Promise((resolve) => setTimeout(resolve, baseDelay * 2 ** attempt))\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/adapter.ts","../src/queue.ts"],"sourcesContent":["import type { CreateJobRequest, JobInfo } from '@workerdeck/protocol'\n\nexport type JobRecord = {\n info: JobInfo\n request: CreateJobRequest\n}\n\nexport interface QueueAdapter {\n add(job: JobRecord): Promise<void>\n claimNext(): Promise<JobRecord | null>\n get(id: string): Promise<JobRecord | null>\n list(): Promise<JobRecord[]>\n update(id: string, patch: Partial<JobInfo>): Promise<JobRecord | null>\n prune(olderThanMs: number): Promise<number>\n addDailyTokens(dayKey: string, tokens: number): Promise<number>\n dailyTokens(dayKey: string): Promise<number>\n onWork?(listener: () => void): () => void\n}\n\nexport class InMemoryQueueAdapter implements QueueAdapter {\n #jobs = new Map<string, JobRecord>()\n #dailyTokens = new Map<string, number>()\n\n add(job: JobRecord): Promise<void> {\n this.#jobs.set(job.info.id, job)\n return Promise.resolve()\n }\n\n claimNext(): Promise<JobRecord | null> {\n const now = Date.now()\n for (const job of this.#jobs.values()) {\n if (job.info.status === 'queued' && (job.info.nextRunAt === undefined || job.info.nextRunAt <= now)) {\n job.info = { ...job.info, status: 'running' }\n return Promise.resolve(job)\n }\n }\n return Promise.resolve(null)\n }\n\n get(id: string): Promise<JobRecord | null> {\n return Promise.resolve(this.#jobs.get(id) ?? null)\n }\n\n list(): Promise<JobRecord[]> {\n return Promise.resolve([...this.#jobs.values()])\n }\n\n update(id: string, patch: Partial<JobInfo>): Promise<JobRecord | null> {\n const job = this.#jobs.get(id)\n if (!job) {\n return Promise.resolve(null)\n }\n job.info = { ...job.info, ...patch }\n return Promise.resolve(job)\n }\n\n prune(olderThanMs: number): Promise<number> {\n const cutoff = Date.now() - olderThanMs\n let removed = 0\n for (const [id, job] of this.#jobs) {\n const { status, finishedAt } = job.info\n const terminal = status === 'succeeded' || status === 'failed' || status === 'canceled'\n if (terminal && (finishedAt ?? 0) <= cutoff) {\n this.#jobs.delete(id)\n removed++\n }\n }\n return Promise.resolve(removed)\n }\n\n addDailyTokens(dayKey: string, tokens: number): Promise<number> {\n const next = (this.#dailyTokens.get(dayKey) ?? 0) + tokens\n this.#dailyTokens.set(dayKey, next)\n return Promise.resolve(next)\n }\n\n dailyTokens(dayKey: string): Promise<number> {\n return Promise.resolve(this.#dailyTokens.get(dayKey) ?? 0)\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type { Runner, SessionRunnerConfig } from '@workerdeck/core'\nimport type {\n ApiMessage,\n CreateJobRequest,\n CreateSessionRequest,\n JobEvent,\n JobInfo,\n JobProgress,\n QueueStats,\n SessionEvent,\n} from '@workerdeck/protocol'\nimport { InMemoryQueueAdapter, type JobRecord, type QueueAdapter } from './adapter.ts'\n\nexport type JobQueueOptions = {\n createRunner: (config: SessionRunnerConfig) => Runner | Promise<Runner>\n adapter?: QueueAdapter\n maxConcurrency?: number\n sessionTokenLimit?: number\n dailyTokenLimit?: number\n maxJobDurationMs?: number\n maxParkedDurationMs?: number\n killGraceMs?: number\n retention?: { maxAgeMs: number; sweepIntervalMs?: number }\n buildRunnerConfig?: (req: CreateSessionRequest) => SessionRunnerConfig\n discardSession?: (sessionId: string) => void | Promise<void>\n fetchImpl?: typeof fetch\n webhookAttempts?: number\n webhookRetryDelayMs?: number\n onEvent?: (event: JobEvent) => void\n}\n\ntype RunningJob = {\n record: JobRecord\n runner: Runner\n unsubscribe: () => void\n lastSeq: number\n estimatedTokens: number\n killReason?: string\n canceled: boolean\n finalized: boolean\n deliveries: Promise<void>\n durationTimer?: ReturnType<typeof setTimeout>\n forceTimer?: ReturnType<typeof setTimeout>\n runningMs: number\n legStartedAt: number\n parkedAt?: number\n parkedExecutionId?: string\n parkTimer?: ReturnType<typeof setTimeout>\n parkedMs: number\n}\n\nfunction dayKey(epochMs: number): string {\n return new Date(epochMs).toISOString().slice(0, 10)\n}\n\nfunction sumUsage(usage: unknown): number {\n if (typeof usage !== 'object' || usage === null) {\n return 0\n }\n const u = usage as Record<string, unknown>\n return (\n (typeof u.input_tokens === 'number' ? u.input_tokens : 0) +\n (typeof u.output_tokens === 'number' ? u.output_tokens : 0) +\n (typeof u.cache_creation_input_tokens === 'number' ? u.cache_creation_input_tokens : 0) +\n (typeof u.cache_read_input_tokens === 'number' ? u.cache_read_input_tokens : 0)\n )\n}\n\nfunction textPreview(message: ApiMessage, max = 140): JobProgress | null {\n const blocks = typeof message.content === 'string' ? [{ type: 'text', text: message.content }] : message.content\n for (const block of blocks) {\n if (block.type === 'tool_use') {\n return { kind: 'tool_use', preview: (block as { name?: string }).name }\n }\n if (block.type === 'text') {\n const text = (block as { text?: string }).text ?? ''\n if (text.trim()) {\n return {\n kind: 'assistant_text',\n preview: text.length > max ? text.slice(0, max - 1) + '…' : text,\n }\n }\n }\n }\n return null\n}\n\nexport class JobQueue {\n #options: JobQueueOptions\n #adapter: QueueAdapter\n #running = new Map<string, RunningJob>()\n #parked = new Map<string, RunningJob>()\n #pumping = false\n #closed = false\n #offWork: (() => void) | undefined\n #sweepTimer: ReturnType<typeof setInterval> | undefined\n #retryTimers = new Set<ReturnType<typeof setTimeout>>()\n\n constructor(options: JobQueueOptions) {\n this.#options = options\n this.#adapter = options.adapter ?? new InMemoryQueueAdapter()\n this.#offWork = this.#adapter.onWork?.(() => void this.#pump())\n const retention = options.retention\n if (retention) {\n const interval = retention.sweepIntervalMs ?? Math.min(retention.maxAgeMs, 60_000)\n this.#sweepTimer = setInterval(() => this.#sweep(), interval)\n this.#sweepTimer.unref?.()\n }\n }\n\n async submit(request: CreateJobRequest): Promise<JobInfo> {\n if (this.#closed) {\n throw new Error('queue is closed')\n }\n if (!request.session?.prompt?.trim()) {\n throw new Error('session.prompt is required')\n }\n if (request.session.resume || request.session.forkSession) {\n throw new Error('resume/forkSession are not supported for queued jobs')\n }\n const attempts = request.attempts ?? 1\n if (!Number.isInteger(attempts) || attempts < 1) {\n throw new Error('attempts must be a positive integer')\n }\n if (request.retryDelayMs !== undefined && !(request.retryDelayMs >= 0)) {\n throw new Error('retryDelayMs must be >= 0')\n }\n const info: JobInfo = {\n id: randomUUID(),\n status: 'queued',\n cwd: request.session.cwd ?? '',\n profile: request.session.profile,\n prompt: request.session.prompt,\n createdAt: Date.now(),\n attempt: 1,\n maxAttempts: attempts,\n usage: { tokens: 0, totalCostUsd: 0, numTurns: 0 },\n meta: request.meta,\n scope: request.session.scope,\n }\n const record: JobRecord = { info, request }\n await this.#adapter.add(record)\n this.#emit(record, { type: 'job_submitted', job: info, ts: Date.now() }, undefined, {\n skipWebhook: true,\n })\n void this.#pump()\n return info\n }\n\n async get(id: string): Promise<JobInfo | null> {\n return (await this.#adapter.get(id))?.info ?? null\n }\n\n async list(): Promise<JobInfo[]> {\n return (await this.#adapter.list()).map((j) => j.info)\n }\n\n onSessionParking(sessionId: string, executionId: string): boolean {\n const job = this.#bySession(this.#running, sessionId)\n if (!job) {\n return true\n }\n if (job.finalized || job.killReason) {\n return false\n }\n const now = Date.now()\n job.unsubscribe()\n job.runningMs += now - job.legStartedAt\n clearTimeout(job.durationTimer)\n job.durationTimer = undefined\n job.parkedAt = now\n job.parkedExecutionId = executionId\n this.#running.delete(job.record.info.id)\n this.#parked.set(job.record.info.id, job)\n const parkedLimit = this.#options.maxParkedDurationMs\n if (parkedLimit !== undefined) {\n job.parkTimer = setTimeout(\n () => this.#kill(job, `job exceeded max parked duration (${parkedLimit}ms)`),\n Math.max(0, parkedLimit - job.parkedMs),\n )\n job.parkTimer.unref?.()\n }\n void this.#recordPark(job, executionId)\n return true\n }\n\n async #recordPark(job: RunningJob, executionId: string): Promise<void> {\n const updated = await this.#adapter.update(job.record.info.id, {\n status: 'parked',\n parkedAt: job.parkedAt,\n parkedExecutionId: executionId,\n })\n if (updated) {\n job.record = updated\n }\n this.#emit(job.record, { type: 'job_parked', job: job.record.info, executionId, ts: Date.now() }, job)\n void this.#pump()\n }\n\n onSessionResumed(sessionId: string, runner: Runner): void {\n const job = this.#bySession(this.#parked, sessionId)\n if (!job || job.finalized) {\n return\n }\n const now = Date.now()\n const executionId = job.parkedExecutionId ?? ''\n clearTimeout(job.parkTimer)\n job.parkTimer = undefined\n job.parkedMs += now - (job.parkedAt ?? now)\n job.parkedAt = undefined\n job.parkedExecutionId = undefined\n job.legStartedAt = now\n job.runner = runner\n this.#parked.delete(job.record.info.id)\n this.#running.set(job.record.info.id, job)\n const durationLimit = this.#effectiveDurationLimit(job.record.request)\n if (durationLimit !== undefined) {\n job.durationTimer = setTimeout(\n () => this.#kill(job, `job exceeded max duration (${durationLimit}ms)`),\n Math.max(0, durationLimit - job.runningMs),\n )\n job.durationTimer.unref?.()\n }\n job.unsubscribe = runner.subscribe((event) => void this.#handleEvent(job, event), job.lastSeq)\n void this.#recordResume(job, executionId)\n }\n\n async #recordResume(job: RunningJob, executionId: string): Promise<void> {\n const updated = await this.#adapter.update(job.record.info.id, {\n status: 'running',\n parkedAt: undefined,\n parkedExecutionId: undefined,\n })\n if (updated) {\n job.record = updated\n }\n this.#emit(job.record, { type: 'job_resumed', job: job.record.info, executionId, ts: Date.now() }, job)\n }\n\n #bySession(jobs: Map<string, RunningJob>, sessionId: string): RunningJob | undefined {\n for (const job of jobs.values()) {\n if (job.record.info.sessionId === sessionId) {\n return job\n }\n }\n return undefined\n }\n\n async cancel(id: string): Promise<JobInfo | null> {\n const record = await this.#adapter.get(id)\n if (!record) {\n return null\n }\n const running = this.#running.get(id) ?? this.#parked.get(id)\n if (running) {\n running.canceled = true\n running.killReason = 'canceled'\n await this.#finalize(running, this.#abortPatch(running, 'canceled'))\n return running.record.info\n }\n if (record.info.status !== 'queued') {\n return record.info\n }\n const updated = await this.#adapter.update(id, {\n status: 'canceled',\n finishedAt: Date.now(),\n error: 'canceled',\n })\n if (updated) {\n this.#emit(updated, { type: 'job_completed', job: updated.info, ts: Date.now() })\n }\n return updated?.info ?? null\n }\n\n async stats(): Promise<QueueStats> {\n const jobs = await this.#adapter.list()\n const dailyTokensUsed = await this.#adapter.dailyTokens(dayKey(Date.now()))\n const dailyTokenLimit = this.#options.dailyTokenLimit\n return {\n maxConcurrency: this.#options.maxConcurrency ?? 1,\n running: this.#running.size,\n parked: this.#parked.size,\n queued: jobs.filter((j) => j.info.status === 'queued').length,\n sessionTokenLimit: this.#options.sessionTokenLimit,\n dailyTokenLimit,\n dailyTokensUsed,\n paused: dailyTokenLimit !== undefined && dailyTokensUsed >= dailyTokenLimit,\n }\n }\n\n close(): void {\n this.#closed = true\n this.#offWork?.()\n clearInterval(this.#sweepTimer)\n for (const timer of this.#retryTimers) {\n clearTimeout(timer)\n }\n this.#retryTimers.clear()\n }\n\n #sweep(): void {\n const retention = this.#options.retention\n if (!retention) {\n return\n }\n this.#adapter.prune(retention.maxAgeMs).catch(() => {})\n }\n\n async #pump(): Promise<void> {\n if (this.#pumping || this.#closed) {\n return\n }\n this.#pumping = true\n try {\n const maxConcurrency = this.#options.maxConcurrency ?? 1\n while (this.#running.size < maxConcurrency) {\n const limit = this.#options.dailyTokenLimit\n if (limit !== undefined && (await this.#adapter.dailyTokens(dayKey(Date.now()))) >= limit) {\n return\n }\n const record = await this.#adapter.claimNext()\n if (!record) {\n return\n }\n await this.#start(record)\n }\n } finally {\n this.#pumping = false\n }\n }\n\n async #start(record: JobRecord): Promise<void> {\n const id = record.info.id\n const build = this.#options.buildRunnerConfig ?? ((req: CreateSessionRequest) => req)\n let runner: Runner\n try {\n const request: CreateSessionRequest = {\n ...record.request.session,\n meta: { ...record.request.session.meta, jobId: id },\n }\n runner = await this.#options.createRunner(build(request))\n } catch (error) {\n const failed = await this.#adapter.update(id, {\n status: 'failed',\n finishedAt: Date.now(),\n error: error instanceof Error ? error.message : String(error),\n })\n if (failed) {\n this.#emit(failed, { type: 'job_completed', job: failed.info, ts: Date.now() })\n }\n return\n }\n const job: RunningJob = {\n record,\n runner,\n unsubscribe: () => {},\n lastSeq: 0,\n estimatedTokens: 0,\n canceled: false,\n finalized: false,\n deliveries: Promise.resolve(),\n runningMs: 0,\n legStartedAt: Date.now(),\n parkedMs: 0,\n }\n this.#running.set(id, job)\n const updated = await this.#adapter.update(id, {\n startedAt: Date.now(),\n sessionId: runner.id,\n })\n if (updated) {\n job.record = updated\n }\n this.#emit(job.record, { type: 'job_started', job: job.record.info, ts: Date.now() })\n const durationLimit = this.#effectiveDurationLimit(record.request)\n if (durationLimit !== undefined) {\n job.durationTimer = setTimeout(() => this.#kill(job, `job exceeded max duration (${durationLimit}ms)`), durationLimit)\n job.durationTimer.unref?.()\n }\n job.unsubscribe = runner.subscribe((event) => void this.#handleEvent(job, event))\n }\n\n /**\n * How a run that never produced a `turn_result` is written down. There is no authoritative\n * usage for such a run, so it reports the tokens counted from the message stream, no cost\n * and no turns. `turn_result` is the one path that does NOT come through here — it has real\n * numbers — which is exactly the distinction five inline copies of this used to hide.\n */\n #abortPatch(job: RunningJob, error: string): Partial<JobInfo> {\n return {\n usage: { tokens: job.estimatedTokens, totalCostUsd: 0, numTurns: 0 },\n status: job.canceled ? 'canceled' : 'failed',\n error,\n }\n }\n\n #kill(job: RunningJob, reason: string): void {\n if (job.finalized || job.killReason) {\n return\n }\n job.killReason = reason\n if (job.parkedAt !== undefined) {\n void this.#finalize(job, this.#abortPatch(job, reason))\n return\n }\n void job.runner.interrupt().catch(() => {})\n job.forceTimer = setTimeout(() => {\n void this.#finalize(job, this.#abortPatch(job, reason))\n }, this.#options.killGraceMs ?? 5000)\n job.forceTimer.unref?.()\n }\n\n async #handleEvent(job: RunningJob, event: SessionEvent): Promise<void> {\n if (job.finalized) {\n return\n }\n job.lastSeq = Math.max(job.lastSeq, event.seq)\n switch (event.type) {\n case 'system_init': {\n await this.#adapter.update(job.record.info.id, { sdkSessionId: event.sdkSessionId })\n return\n }\n case 'assistant_message': {\n if (event.replay) {\n return\n }\n job.estimatedTokens += sumUsage(event.message.usage)\n const limit = this.#effectiveTokenLimit(job.record.request)\n if (limit !== undefined && job.estimatedTokens > limit) {\n this.#kill(job, `session token limit exceeded (${job.estimatedTokens} > ${limit})`)\n }\n const progress = textPreview(event.message)\n if (progress) {\n this.#progress(job, progress)\n }\n return\n }\n case 'permission_requested': {\n this.#progress(job, {\n kind: 'permission_requested',\n preview: event.request.title ?? event.request.toolName,\n request: event.request,\n })\n return\n }\n case 'permission_resolved': {\n this.#progress(job, { kind: 'permission_resolved', preview: event.behavior })\n return\n }\n case 'turn_result': {\n const tokens = sumUsage(event.usage) || job.estimatedTokens\n await this.#finalize(job, {\n usage: {\n tokens,\n totalCostUsd: event.totalCostUsd,\n numTurns: event.numTurns,\n },\n result: {\n subtype: event.subtype,\n isError: event.isError,\n result: event.result,\n errors: event.errors,\n durationMs: event.durationMs,\n },\n status: job.killReason ? (job.canceled ? 'canceled' : 'failed') : event.isError ? 'failed' : 'succeeded',\n error: job.killReason ?? (event.isError ? event.errors?.join('; ') || event.subtype : undefined),\n })\n return\n }\n case 'session_error': {\n await this.#finalize(job, this.#abortPatch(job, job.killReason ?? event.message))\n return\n }\n case 'session_closed': {\n await this.#finalize(job, this.#abortPatch(job, job.killReason ?? 'session closed before completing'))\n return\n }\n default: {\n return\n }\n }\n }\n\n #effectiveTokenLimit(request: CreateJobRequest): number | undefined {\n const limits = [request.maxTokens, this.#options.sessionTokenLimit].filter((n): n is number => typeof n === 'number')\n return limits.length > 0 ? Math.min(...limits) : undefined\n }\n\n #effectiveDurationLimit(request: CreateJobRequest): number | undefined {\n const limits = [request.maxDurationMs, this.#options.maxJobDurationMs].filter((n): n is number => typeof n === 'number')\n return limits.length > 0 ? Math.min(...limits) : undefined\n }\n\n async #finalize(job: RunningJob, patch: Partial<JobInfo>): Promise<void> {\n if (job.finalized) {\n return\n }\n job.finalized = true\n job.unsubscribe()\n clearTimeout(job.durationTimer)\n clearTimeout(job.forceTimer)\n clearTimeout(job.parkTimer)\n this.#running.delete(job.record.info.id)\n const wasParked = this.#parked.delete(job.record.info.id)\n job.runner.close('server')\n if (wasParked && job.record.info.sessionId) {\n try {\n void Promise.resolve(this.#options.discardSession?.(job.record.info.sessionId)).catch(() => {})\n } catch {}\n }\n const attemptUsage = patch.usage ?? { tokens: 0, totalCostUsd: 0, numTurns: 0 }\n if (attemptUsage.tokens > 0) {\n await this.#adapter.addDailyTokens(dayKey(Date.now()), attemptUsage.tokens)\n }\n const prior = job.record.info.usage\n const usage = {\n tokens: prior.tokens + attemptUsage.tokens,\n totalCostUsd: prior.totalCostUsd + attemptUsage.totalCostUsd,\n numTurns: prior.numTurns + attemptUsage.numTurns,\n }\n const attempt = job.record.info.attempt ?? 1\n const maxAttempts = job.record.request.attempts ?? 1\n if (patch.status === 'failed' && attempt < maxAttempts && !this.#closed) {\n const baseDelay = job.record.request.retryDelayMs ?? 5000\n const delay = baseDelay * 2 ** (attempt - 1)\n const updated = await this.#adapter.update(job.record.info.id, {\n status: 'queued',\n attempt: attempt + 1,\n nextRunAt: Date.now() + delay,\n error: patch.error,\n usage,\n sessionId: undefined,\n sdkSessionId: undefined,\n startedAt: undefined,\n result: undefined,\n })\n if (updated) {\n job.record = updated\n this.#emit(updated, { type: 'job_retrying', job: updated.info, ts: Date.now() }, job)\n const timer = setTimeout(() => {\n this.#retryTimers.delete(timer)\n void this.#pump()\n }, delay)\n timer.unref?.()\n this.#retryTimers.add(timer)\n }\n void this.#pump()\n return\n }\n const updated = await this.#adapter.update(job.record.info.id, {\n ...patch,\n usage,\n nextRunAt: undefined,\n finishedAt: Date.now(),\n })\n if (updated) {\n job.record = updated\n this.#emit(job.record, { type: 'job_completed', job: updated.info, ts: Date.now() }, job)\n }\n this.#sweep()\n void this.#pump()\n }\n\n #progress(job: RunningJob, progress: JobProgress): void {\n const event: JobEvent = { type: 'job_progress', job: job.record.info, progress, ts: Date.now() }\n if (job.record.request.webhook?.progress === 'completion') {\n try {\n this.#options.onEvent?.(event)\n } catch {}\n return\n }\n this.#emit(job.record, event, job)\n }\n\n #emit(record: JobRecord, event: JobEvent, chainOwner?: RunningJob, { skipWebhook = false }: { skipWebhook?: boolean } = {}): void {\n try {\n this.#options.onEvent?.(event)\n } catch {}\n const webhook = record.request.webhook\n if (!webhook || skipWebhook) {\n return\n }\n const running = chainOwner ?? this.#running.get(record.info.id)\n const deliver = () => this.#deliver(webhook.url, webhook.headers, event)\n if (running) {\n running.deliveries = running.deliveries.then(deliver)\n } else {\n void deliver()\n }\n }\n\n async #deliver(url: string, headers: Record<string, string> | undefined, event: JobEvent): Promise<void> {\n const fetchImpl = this.#options.fetchImpl ?? fetch\n const attempts = this.#options.webhookAttempts ?? 3\n const baseDelay = this.#options.webhookRetryDelayMs ?? 500\n for (let attempt = 0; attempt < attempts; attempt++) {\n try {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...headers },\n body: JSON.stringify(event),\n })\n if (res.ok) {\n return\n }\n } catch {}\n if (attempt < attempts - 1) {\n await new Promise((resolve) => setTimeout(resolve, baseDelay * 2 ** attempt))\n }\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workerdeck/queue",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Job queue over the WorkerDeck session runner: remote services schedule one-shot runs; the queue executes them as sessions with bounded concurrency and token budgets, delivering progress and completion via webhooks. Pluggable adapter interface (in-memory bundled; redis/bullmq/pubsub adapters can implement the same contract).",
6
6
  "license": "MIT",
@@ -17,15 +17,15 @@
17
17
  }
18
18
  },
19
19
  "dependencies": {
20
- "@workerdeck/core": "1.0.0",
21
- "@workerdeck/protocol": "1.0.0"
20
+ "@workerdeck/core": "1.1.0",
21
+ "@workerdeck/protocol": "1.1.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@anthropic-ai/claude-agent-sdk": "^0.3.221",
25
- "@types/node": "^22.10.0",
24
+ "@anthropic-ai/claude-agent-sdk": "^0.3.258",
25
+ "@types/node": "^26.4.0",
26
26
  "rimraf": "^6.1.3",
27
- "tsdown": "^0.21.10",
28
- "vitest": "^3.2.0"
27
+ "tsdown": "^0.22.14",
28
+ "vitest": "^4.1.11"
29
29
  },
30
30
  "author": "Tobias Strebitzer",
31
31
  "repository": {