{"version":3,"file":"task.mjs","names":[],"sources":["../../../src/models/task.ts"],"sourcesContent":["/**\n * Domain types for the file-backed task store.\n *\n * The on-disk document is the durable source of truth (unlike rpiv-todo, which\n * replayed state from the transcript). Every field here round-trips through\n * JSON, so nothing may hold non-serializable values.\n */\n\nimport type { InlineAgent } from '@agimon-ai/doompi-core/delegation';\n\nexport type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'failed' | 'deleted';\n\nexport const DELETED_STATUS: TaskStatus = 'deleted';\n\nexport type TaskAction = 'upsert' | 'list' | 'get' | 'delete' | 'clear' | 'assign' | 'cancel';\n\nexport type DelegationState = 'requested' | 'running' | 'completed' | 'failed' | 'cancelled';\n\n/** Terminal result of a delegated subagent run, copied off the delegation response. */\nexport interface DelegationResult {\n  status: string;\n  output?: string;\n  outputPath?: string;\n  sessionFile?: string;\n  error?: string;\n  durationMs?: number;\n  toolCount?: number;\n}\n\n/**\n * Delegation bookkeeping attached to a task that was handed to a subagent.\n *\n * `pid` is the harness process that owns the run: it is the only way to tell an\n * genuinely-running delegation from one orphaned by a harness crash, since the\n * child dies with its parent but the file survives.\n */\nexport interface TaskDelegation {\n  requestId: string;\n  agent: string;\n  state: DelegationState;\n  sessionId?: string;\n  pid?: number;\n  runId?: string;\n  model?: string;\n  startedAt?: string;\n  endedAt?: string;\n  result?: DelegationResult;\n}\n\nexport interface Task {\n  id: number;\n  subject: string;\n  description?: string;\n  activeForm?: string;\n  status: TaskStatus;\n  blockedBy?: number[];\n  owner?: string;\n  metadata?: Record<string, unknown>;\n  createdAt?: string;\n  updatedAt?: string;\n  delegation?: TaskDelegation;\n}\n\nexport const STORE_SCHEMA_VERSION = 1;\n\n/** The complete on-disk document. `rev` increments on every committed write. */\nexport interface TaskDocument {\n  version: number;\n  rev: number;\n  nextId: number;\n  tasks: Task[];\n}\n\nexport function emptyDocument(): TaskDocument {\n  return { version: STORE_SCHEMA_VERSION, rev: 0, nextId: 1, tasks: [] };\n}\n\n/**\n * A dependency target inside an upsert item: an existing task id, or the `ref`\n * of a task created earlier in the same call.\n *\n * Refs must start with a letter (see `REF_PATTERN`), so a ref is structurally\n * unconfusable with a stringified id and the discrimination is just `typeof`.\n */\nexport type DepToken = number | string;\n\n/**\n * One entry of an `upsert` call. No `id` creates a task; an `id` updates one.\n *\n * `blockedBy` (absolute set) is create-only and `addBlockedBy`/`removeBlockedBy`\n * (additive merge) are update-only, matching the split the two former actions\n * had. A field used against the wrong kind fails that item rather than being\n * silently ignored.\n */\nexport interface TaskItemMutation {\n  id?: number;\n  ref?: string;\n  subject?: string;\n  description?: string;\n  activeForm?: string;\n  status?: TaskStatus;\n  blockedBy?: DepToken[];\n  addBlockedBy?: DepToken[];\n  removeBlockedBy?: DepToken[];\n  owner?: string;\n  metadata?: Record<string, unknown>;\n}\n\n/**\n * What one upsert item did. `index` is its position in the request array, so a\n * model reading a partial result can tell which entries still need resending.\n *\n * `message` is unprefixed: the `item[N] failed:` framing belongs to the\n * formatter, because the Task Space overlay shows this text on its own.\n */\nexport type UpsertItemOutcome =\n  | {\n      index: number;\n      kind: 'created';\n      id: number;\n      subject: string;\n      status: TaskStatus;\n      ref?: string;\n      blockedBy?: number[];\n    }\n  | { index: number; kind: 'updated'; id: number; fromStatus: TaskStatus; toStatus: TaskStatus }\n  | { index: number; kind: 'unchanged'; id: number; status: TaskStatus }\n  | { index: number; kind: 'failed'; message: string; id?: number; ref?: string };\n\n/** Which tasks an upsert touched, so renderResult names them instead of guessing. */\nexport interface UpsertSummary {\n  /** Ids of applied entries, in request order. A failed entry contributes nothing. */\n  applied: number[];\n  failed: number;\n}\n\n/** One task-to-agent handoff inside a native assignment batch. */\nexport interface TaskAssignment {\n  id: number;\n  agent: string;\n  inlineAgent?: InlineAgent;\n  instructions?: string;\n  relevantFiles?: string[];\n  priorFindings?: string;\n  model?: string;\n  context?: 'fresh' | 'fork';\n}\n\n/** Which assignment entries started, so renderResult can show one consolidated batch. */\nexport interface AssignmentSummary {\n  /** Ids of successful entries, in request order. A failed entry contributes nothing. */\n  assigned: number[];\n  failed: number;\n}\n\n/**\n * Open-shape input bag the reducer accepts. The index signature lets the\n * runtime pass a TypeBox `Static<typeof TaskParamsSchema>` through without\n * casting each field.\n *\n * Per-task fields live on `tasks[]`, not here: a top-level `subject` or\n * `status` would be ambiguous once one call can carry many tasks.\n */\nexport interface TaskMutationParams {\n  [key: string]: unknown;\n  tasks?: TaskItemMutation[];\n  /** `assign` only: independent task-to-agent handoffs dispatched by one tool call. */\n  assignments?: TaskAssignment[];\n  /** `list` filter only. To set a status, upsert the task by id. */\n  status?: TaskStatus;\n  /** `get`, `delete`, and `cancel` only. Assign ids live in `assignments[]`. */\n  id?: number;\n  includeDeleted?: boolean;\n}\n\n/** Snapshot returned under a tool result's `details` for renderResult. */\nexport interface TaskDetails {\n  action: TaskAction;\n  params: Record<string, unknown>;\n  tasks: Task[];\n  nextId: number;\n  rev: number;\n  error?: string;\n  upsert?: UpsertSummary;\n  assignment?: AssignmentSummary;\n}\n\n/**\n * A delegation is \"live\" while the owning run may still produce a response.\n *\n * Takes the delegation shape rather than `Task` so the cockpit's `WebTask`,\n * which carries the state as a plain string, answers the question the same way\n * the store does.\n */\nexport function isDelegationActive(task: { delegation?: { state?: string } }): boolean {\n  const state = task.delegation?.state;\n  return state === 'requested' || state === 'running';\n}\n"],"mappings":";AAYA,MAAa,iBAA6B;AAmD1C,MAAa,uBAAuB;AAUpC,SAAgB,gBAA8B;CAC5C,OAAO;EAAE,SAAA;EAA+B,KAAK;EAAG,QAAQ;EAAG,OAAO,CAAC;CAAE;AACvE;;;;;;;;AAuHA,SAAgB,mBAAmB,MAAoD;CACrF,MAAM,QAAQ,KAAK,YAAY;CAC/B,OAAO,UAAU,eAAe,UAAU;AAC5C"}