fix(tasks): address task API review feedback
The public kill result used the awkward phrase already-terminal. Rename it to already-finished and keep the model-facing response aligned; not-alive would be inaccurate because a force-failed registry record can still correspond to orphaned producer work. Task kinds were open strings even though producer namespaces are an extension point. Add the merge-extensible TaskKindMap and derived TaskKind, cover consumer declarations in task and bundle tests, and retain the runtime non-empty check for untyped callers. With exactOptionalPropertyTypes, owner?: Agent | undefined allowed an explicit undefined value that no caller needs. Tighten the property to owner?: Agent so unowned work is expressed by omitting it. Record the requested task-service/backend split as a follow-up, using a systemd-backed runtime as a concrete candidate without guessing its durability and ownership contract in this PR. Regenerate the type and Cordis catalogs so public docs match the declarations.
This commit is contained in:
@@ -13,12 +13,14 @@ import { Context, Service } from 'cordis'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { deadline, timeoutOf } from '@deepseek-ai/dsh-timeout'
|
||||
import { TaskId } from './types.ts'
|
||||
import type { TaskDoneListener, TaskOutcome, TaskRead, TaskSnapshot, TaskStart, TaskStatus } from './types.ts'
|
||||
import type { TaskDoneListener, TaskKind, TaskOutcome, TaskRead, TaskSnapshot, TaskStart, TaskStatus } from './types.ts'
|
||||
|
||||
export { TaskId } from './types.ts'
|
||||
export type {
|
||||
TaskDoneListener,
|
||||
TaskHooks,
|
||||
TaskKind,
|
||||
TaskKindMap,
|
||||
TaskOutcome,
|
||||
TaskRead,
|
||||
TaskSnapshot,
|
||||
@@ -38,7 +40,7 @@ export const TASK_WAIT_TIMEOUT = 'TASK_WAIT_TIMEOUT'
|
||||
/** The registry's mutable per-task record (never handed out — see {@link TaskService.snapshot}). */
|
||||
interface TrackedTask {
|
||||
id: TaskId
|
||||
kind: string
|
||||
kind: TaskKind
|
||||
label: string
|
||||
/** Exact lifecycle owner; session-id authorization is derived from it. */
|
||||
owner: Agent | undefined
|
||||
@@ -69,6 +71,8 @@ function isTerminal(status: TaskStatus): boolean {
|
||||
* The `tasks` service: the runtime-global background task registry. See the
|
||||
* module doc for the ownership, isolation, and lifecycle contracts.
|
||||
*/
|
||||
// TODO(task-service-backend): Separate the service contract from this
|
||||
// process-local implementation when a second backend defines its lifecycle.
|
||||
export class TaskService extends Service {
|
||||
private store = new Map<TaskId, TrackedTask>()
|
||||
private counters = new Map<string, number>()
|
||||
@@ -191,14 +195,14 @@ export class TaskService extends Service {
|
||||
* @param id - task to cancel.
|
||||
* @param caller - killing agent checked against the owner.
|
||||
* @param reason - logged reason forwarded to the producer.
|
||||
* @returns `requested` for live work, otherwise `already-terminal`.
|
||||
* @returns `requested` for live work, otherwise `already-finished`.
|
||||
*/
|
||||
kill(id: TaskId, caller?: Agent, reason?: string): 'requested' | 'already-terminal' {
|
||||
kill(id: TaskId, caller?: Agent, reason?: string): 'requested' | 'already-finished' {
|
||||
const task = this.expect(id)
|
||||
this.assertAccess(task, caller)
|
||||
if (isTerminal(task.status)) {
|
||||
task.reported = true
|
||||
return 'already-terminal'
|
||||
return 'already-finished'
|
||||
}
|
||||
// Cancel first so a throw leaves both lifecycle and notice state unchanged.
|
||||
task.cancel(reason)
|
||||
|
||||
Reference in New Issue
Block a user