@ordewell/core 0.5.3 → 0.5.5

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.
Files changed (44) hide show
  1. package/dist/{ITerminalRunner-Bd-vAJnw.d.ts → ITerminalRunner-BV9Rd2o9.d.ts} +3 -1
  2. package/dist/{ITerminalRunner-ByeoLF57.d.mts → ITerminalRunner-C77ZNZS9.d.mts} +3 -1
  3. package/dist/{ModeResolver-DVJ7HV3k.d.mts → ModeResolver-D-SUFRNF.d.mts} +1 -1
  4. package/dist/{ModeResolver-Dkig8ghQ.d.ts → ModeResolver-D3XO0fT9.d.ts} +1 -1
  5. package/dist/{Task-BxQkPlXO.d.mts → Task-Vl5Zq_D-.d.mts} +168 -7
  6. package/dist/{Task-BxQkPlXO.d.ts → Task-Vl5Zq_D-.d.ts} +168 -7
  7. package/dist/{chunk-JVMDEHRQ.mjs → chunk-HD2FWPRV.mjs} +72 -37
  8. package/dist/chunk-HD2FWPRV.mjs.map +1 -0
  9. package/dist/{chunk-GWPIYDQW.mjs → chunk-KLN7ELXO.mjs} +897 -18
  10. package/dist/chunk-KLN7ELXO.mjs.map +1 -0
  11. package/dist/{chunk-XWOUIA6A.mjs → chunk-UUBGVCGJ.mjs} +2 -2
  12. package/dist/index.d.mts +320 -60
  13. package/dist/index.d.ts +320 -60
  14. package/dist/index.js +2497 -672
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs +1605 -659
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/order-labels.d.mts +1 -1
  19. package/dist/order-labels.d.ts +1 -1
  20. package/dist/{parsing-CF_grC29.d.ts → parsing-BTP4bwkk.d.ts} +11 -3
  21. package/dist/{parsing-DRp4dPC0.d.mts → parsing-CDtRSxBY.d.mts} +11 -3
  22. package/dist/parsing.d.mts +3 -3
  23. package/dist/parsing.d.ts +3 -3
  24. package/dist/parsing.js.map +1 -1
  25. package/dist/parsing.mjs +2 -2
  26. package/dist/plan-utils-BFaPo-IT.d.ts +708 -0
  27. package/dist/plan-utils-pE4TBwxl.d.mts +708 -0
  28. package/dist/plan-utils.d.mts +3 -3
  29. package/dist/plan-utils.d.ts +3 -3
  30. package/dist/plan-utils.js +668 -7
  31. package/dist/plan-utils.js.map +1 -1
  32. package/dist/plan-utils.mjs +38 -4
  33. package/dist/testing.d.mts +5 -3
  34. package/dist/testing.d.ts +5 -3
  35. package/dist/testing.js +3 -0
  36. package/dist/testing.js.map +1 -1
  37. package/dist/testing.mjs +3 -0
  38. package/dist/testing.mjs.map +1 -1
  39. package/package.json +1 -1
  40. package/dist/chunk-GWPIYDQW.mjs.map +0 -1
  41. package/dist/chunk-JVMDEHRQ.mjs.map +0 -1
  42. package/dist/plan-utils-CkNbqAmS.d.ts +0 -329
  43. package/dist/plan-utils-CtB3_Ovf.d.mts +0 -329
  44. /package/dist/{chunk-XWOUIA6A.mjs.map → chunk-UUBGVCGJ.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/models/Task.ts","../src/services/JsonExtractor.ts"],"sourcesContent":["import { v4 as uuidv4 } from 'uuid';\nimport type { PlanIsolation } from '../interfaces/IWorktreeIsolation';\n\nexport interface UserStep {\n order: number;\n instruction: string;\n completed: boolean;\n}\n\n/** One deterministic signal gathered while verifying a completed task. */\nexport interface VerificationCheck {\n name: 'exit_code' | 'completion_marker' | 'manual';\n passed: boolean;\n /** A check that did not apply. Skipped checks don't affect the verdict. */\n skipped: boolean;\n detail: string;\n}\n\n/** Evidence-based verdict for a completed task. Single end-to-end outcome produced by verification. */\nexport interface Verdict {\n outcome: 'pass' | 'fail';\n reason: string;\n checks: VerificationCheck[];\n decidedAt: string;\n}\n\nexport interface TaskOutputSummary {\n reviewReason: string;\n logTail: string;\n capturedAt: string;\n}\n\nexport type TaskType = 'ai' | 'user';\nexport type TaskStatus = 'pending' | 'approved' | 'in_progress' | 'completed' | 'failed' | 'blocked' | 'awaiting_user';\nexport type TaskMode = string;\n\nexport interface TaskModelAssignment {\n modelId: string;\n modelLabel: string;\n thinkingEffort?: string;\n /**\n * All variant ids the model offered when this assignment was made. Carried\n * on the assignment because runners need it at spawn time (opencode's TUI\n * only honors an assigned variant when the others are config-disabled) and\n * the discovery catalog isn't available there.\n */\n availableVariants?: string[];\n}\n\nexport type RunnerId = string;\n\nexport interface Task {\n id: string;\n order: number;\n title: string;\n description: string;\n type: TaskType;\n status: TaskStatus;\n dependencies: string[];\n prompt?: string;\n userSteps?: UserStep[];\n subtasks: Task[];\n verdict?: Verdict;\n outputSummary?: TaskOutputSummary;\n assignedModel?: TaskModelAssignment;\n assignedRunner: RunnerId;\n thinkingEffort?: string;\n taskMode?: TaskMode;\n completionMarker: string;\n autonomy?: 'AFK' | 'HITL';\n sliceType?: 'HITL' | 'AFK';\n userStoriesCovered?: string[];\n}\n\nexport interface DiscoveredMode {\n id: string;\n label: string;\n description: string;\n}\n\nexport type ResearchToolType =\n | 'read_file' | 'read_files' | 'glob' | 'grep' | 'find_symbol' | 'list_dir'\n | 'bash' | 'fetch' | 'web_search' | 'spawn_research_agent'\n /**\n * A tool belonging to a harness planner's own toolbox (ADR-0009) that has no\n * Ordewell equivalent — Edit, WebFetch, TodoWrite, whatever a coding agent\n * ships next. The real name travels in `toolLabel` rather than being\n * relabelled as a tool it is not; the union stays closed so the\n * exhaustiveness checks in every surface's icon/label switch survive.\n */\n | 'agent_tool';\n\n/**\n * What happened when a research tool call ran, for honest per-surface\n * rendering. The broadcast seam carries this on every `research_step_done` so\n * surfaces do not have to pattern-match refusal text to tell a refused `rm`\n * from a successful `rm` — the old render path flipped a `✓` for both.\n */\nexport type ResearchStepOutcome = 'success' | 'failure' | 'refused' | 'denied' | 'not_executed';\n\nexport interface ResearchStep {\n id: string;\n tool: ResearchToolType;\n /** The tool's own name when it came from a harness planner — always set for `agent_tool`. */\n toolLabel?: string;\n args: string;\n result: string;\n success: boolean;\n outcome: ResearchStepOutcome;\n /** The model's tool_call id, so a surface can match `tool_result` to the\n * pending `tool_call` it announced — robust under parallel same-tool rounds. */\n toolCallId?: string;\n timestamp: string;\n thinkingText?: string;\n}\n\nexport interface UserPromptEntry {\n id: string;\n type: 'user_prompt' | 'system';\n content: string;\n timestamp: string;\n}\n\nexport type ResearchLogEntry = ResearchStep | UserPromptEntry;\n\nexport interface ResearchProgress {\n // 'liveness' carries no content: a raw-line signal from the harness process\n // (ADR-0009) that reaches a surface even when a turn is producing nothing\n // visible — a subagent's filtered chatter, most often — so an idle watchdog\n // downstream doesn't mistake \"nothing to show\" for \"nothing happening\".\n type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted' | 'liveness';\n text?: string;\n tool?: string;\n /** Harness planners (ADR-0009): the agent's own name for a tool Ordewell has no member for. */\n toolLabel?: string;\n toolArgs?: string;\n toolResult?: string;\n planToken?: string;\n step?: ResearchStep;\n /** The model's tool_call id, threaded on tool_call and tool_result so a\n * surface can match the result to its pending call — robust under parallel\n * same-tool rounds where LIFO-by-name matching mislabels summaries. */\n toolCallId?: string;\n /** Present when this event originates from (or reports on) one spawned research subagent (issue #34). */\n subagentId?: string;\n}\n\nexport interface ThinkingBlock {\n id: string;\n text: string;\n}\n\nexport interface StreamThinkingEvent {\n type: 'thinking';\n block: ThinkingBlock;\n}\n\nexport interface StreamStepEvent {\n type: 'step';\n step: ResearchStep;\n}\n\nexport type StreamEvent = StreamThinkingEvent | StreamStepEvent;\n\nexport interface DiscoveredModel {\n modelId: string;\n modelLabel: string;\n runnerProvider?: string;\n /**\n * Human-facing provider name as the runner itself reports it (e.g.\n * \"OpenCode Zen\" for `runnerProvider: 'opencode'`). Populated from the\n * runner's own provider catalog when available; when absent the UI derives a\n * label from `runnerProvider` by title-casing.\n */\n runnerProviderLabel?: string;\n /**\n * The runner whose catalog listed this model. Stamped once, at the single\n * `ModelDiscovery.discover` choke point, so a flat cross-runner list can\n * still say where each entry came from — `runnerProvider` alone cannot:\n * OpenCode reports most of its catalog as `openrouter`, which names the\n * serving backend, not the agent Ordewell would spawn.\n */\n runnerId?: string;\n /** The runner's display name (`OpenCode`), from its manifest. */\n runnerLabel?: string;\n variants: { id: string; label: string }[];\n}\n\nexport type PlanStatus = 'draft' | 'approved' | 'rejected' | 'running' | 'completed';\n\n/**\n * One entry of the planner's persisted dialogue (ADR-0002). The single source\n * of truth for both UI redisplay and conversational context. Tool-call results\n * are NOT stored here — they live in the AI service's tool-use history;\n * `researchLog` remains the persisted tool trace for the UI.\n */\nexport interface ConversationMessage {\n role: 'user' | 'assistant';\n content: string;\n timestamp: string;\n /**\n * Timeline marker: 'plan_generated' records the point in the dialogue where\n * the plan was committed (the UI anchors the plan card there on restore);\n * 'system' is a host-injected notice; 'compaction' is the summary a\n * user-triggered compaction left in place of the earlier messages — always\n * the transcript's first entry. Absent for ordinary chat turns, so\n * sessions saved before markers existed degrade gracefully.\n */\n kind?: 'plan_generated' | 'system' | 'compaction';\n}\n\nexport interface QueuedMessage {\n id: string;\n text: string;\n timestamp: string;\n}\n\nexport interface PlanModificationWarnings {\n deletedCompleted: string[];\n changedCompleted: string[];\n deletedInProgress: string[];\n modifiedInProgress: string[];\n brokenDependencies: string[];\n}\n\nexport function emptyWarnings(): PlanModificationWarnings {\n return { deletedCompleted: [], changedCompleted: [], deletedInProgress: [], modifiedInProgress: [], brokenDependencies: [] };\n}\n\nexport interface LegacyPlanState {\n tasks: Task[];\n generatedAt: string;\n status: PlanStatus;\n runners: RunnerId[];\n lastUpdated: string;\n researchLog?: ResearchLogEntry[];\n /** The planner dialogue — user messages and assistant messages, in order (ADR-0002). */\n conversationHistory?: ConversationMessage[];\n /** Full markdown PRD once written by the planner (PRD mode), also saved to .scratch/<slug>/PRD.md. */\n prdMarkdown?: string;\n /** Follow-ups queued while tasks execute — applied as plan modifications between batches. */\n queuedMessages?: QueuedMessage[];\n /**\n * The plan's isolation run (ADR-0013), written from the orchestrator at\n * persist time and read back only when a saved plan is adopted. It names\n * branches and worktrees that belong to this plan alone: a fork of the plan\n * must leave it behind rather than share it.\n */\n isolation?: PlanIsolation;\n}\n\nexport interface Message {\n id: string;\n role: 'user' | 'planner' | 'system';\n content: string;\n timestamp: number;\n}\n\nexport interface TaskSnapshot extends Task {\n completedAt: number;\n verdict?: Verdict;\n retryCount: number;\n finalized: boolean;\n}\n\nexport type PlanState =\n | {\n phase: 'planning';\n history: Message[];\n message: string;\n pendingTasks: Task[];\n }\n | {\n phase: 'executing';\n history: Message[];\n message: string;\n executionLog: TaskSnapshot[];\n pendingTasks: Task[];\n goal: string;\n runners: string[];\n status: PlanStatus;\n };\n\nfunction hasPhase(raw: unknown): raw is PlanState {\n return typeof raw === 'object' && raw !== null && 'phase' in raw;\n}\n\nfunction hasTasks(raw: unknown): raw is LegacyPlanState {\n return typeof raw === 'object' && raw !== null && 'tasks' in raw && Array.isArray((raw as Record<string, unknown>).tasks);\n}\n\nfunction migrateHistory(raw: LegacyPlanState): Message[] {\n const messages: Message[] = [];\n\n if (raw.researchLog) {\n for (const entry of raw.researchLog) {\n if ('type' in entry && entry.type === 'user_prompt') {\n messages.push({\n id: entry.id,\n role: 'user',\n content: (entry as UserPromptEntry).content,\n timestamp: new Date(entry.timestamp).getTime(),\n });\n } else {\n const step = entry as ResearchStep;\n messages.push({\n id: step.id,\n role: 'system',\n content: JSON.stringify({ tool: step.tool, args: step.args, result: step.result }),\n timestamp: new Date(step.timestamp).getTime(),\n });\n }\n }\n }\n\n if (raw.queuedMessages) {\n for (const qm of raw.queuedMessages) {\n messages.push({\n id: qm.id,\n role: 'system',\n content: qm.text,\n timestamp: new Date(qm.timestamp).getTime(),\n });\n }\n }\n\n return messages;\n}\n\nexport function migratePlanState(raw: unknown): PlanState {\n if (hasPhase(raw)) return raw;\n\n if (!hasTasks(raw)) {\n return {\n phase: 'planning',\n history: [],\n message: '',\n pendingTasks: [],\n };\n }\n\n const hasExecution = raw.tasks.some(\n (t) => t.status === 'completed' || t.status === 'failed',\n );\n\n const messages = migrateHistory(raw);\n\n if (hasExecution) {\n const executionLog: TaskSnapshot[] = raw.tasks\n .filter((t) => t.status === 'completed' || t.status === 'failed')\n .map((t) => ({\n ...t,\n completedAt: new Date(raw.lastUpdated).getTime(),\n verdict: t.verdict,\n retryCount: 0,\n finalized: true,\n }));\n\n const pendingTasks = raw.tasks.filter(\n (t) => t.status !== 'completed' && t.status !== 'failed' && t.status !== 'blocked',\n );\n\n return {\n phase: 'executing',\n history: messages,\n message: '',\n executionLog,\n pendingTasks,\n goal: '',\n runners: raw.runners,\n status: raw.status,\n };\n }\n\n return {\n phase: 'planning',\n history: messages,\n message: '',\n pendingTasks: raw.tasks,\n };\n}\n\nexport function migrateLegacyPlan(legacy: LegacyPlanState): PlanState {\n const messages: Message[] = [];\n\n if (legacy.researchLog) {\n for (const entry of legacy.researchLog) {\n if ('type' in entry && entry.type === 'user_prompt') {\n messages.push({\n id: entry.id,\n role: 'user',\n content: (entry as UserPromptEntry).content,\n timestamp: new Date(entry.timestamp).getTime(),\n });\n } else {\n const step = entry as ResearchStep;\n messages.push({\n id: step.id,\n role: 'system',\n content: JSON.stringify({ tool: step.tool, args: step.args, result: step.result }),\n timestamp: new Date(step.timestamp).getTime(),\n });\n }\n }\n }\n\n if (legacy.queuedMessages) {\n for (const qm of legacy.queuedMessages) {\n messages.push({\n id: qm.id,\n role: 'system',\n content: qm.text,\n timestamp: new Date(qm.timestamp).getTime(),\n });\n }\n }\n\n const isPlanning = legacy.status === 'draft' || legacy.status === 'approved' || legacy.status === 'rejected';\n\n if (isPlanning) {\n return {\n phase: 'planning',\n history: messages,\n message: 'Plan generation',\n pendingTasks: legacy.tasks.filter((t) => t.status === 'pending' || t.status === ('draft' as never)),\n };\n }\n\n const nonCompletedTasks = legacy.tasks.filter(\n (t) => t.status !== 'completed' && t.status !== 'failed' && t.status !== 'blocked'\n );\n\n const completedSnapshot: TaskSnapshot[] = legacy.tasks\n .filter((t) => t.status === 'completed' || t.status === 'failed')\n .map((t) => ({\n ...t,\n completedAt: new Date(legacy.lastUpdated).getTime(),\n verdict: t.verdict,\n retryCount: 0,\n finalized: true,\n }));\n\n return {\n phase: 'executing',\n history: messages,\n message: 'Plan migration',\n executionLog: completedSnapshot,\n pendingTasks: nonCompletedTasks,\n goal: 'Plan migration',\n runners: legacy.runners,\n status: legacy.status,\n };\n}\n\nexport { taskOrderLabel, resolveOrderLabel } from '../order-labels';\n\nexport function createTask(overrides: Partial<Task> = {}): Task {\n return {\n id: overrides.id ?? uuidv4(),\n order: overrides.order ?? 0,\n title: overrides.title ?? '',\n description: overrides.description ?? '',\n type: overrides.type ?? 'ai',\n status: overrides.status ?? 'pending',\n dependencies: overrides.dependencies ?? [],\n prompt: overrides.prompt,\n userSteps: overrides.userSteps,\n subtasks: overrides.subtasks ?? [],\n verdict: overrides.verdict,\n outputSummary: overrides.outputSummary,\n assignedModel: overrides.assignedModel,\n assignedRunner: overrides.assignedRunner ?? 'claude-code',\n thinkingEffort: overrides.thinkingEffort,\n taskMode: overrides.taskMode ?? 'build',\n completionMarker: overrides.completionMarker ?? uuidv4(),\n autonomy: overrides.autonomy,\n sliceType: overrides.sliceType,\n userStoriesCovered: overrides.userStoriesCovered,\n };\n}\n\nexport function createEmptyPlan(): LegacyPlanState {\n return {\n tasks: [],\n generatedAt: new Date().toISOString(),\n status: 'draft',\n runners: ['claude-code'],\n lastUpdated: new Date().toISOString(),\n };\n}\n\nexport function flattenTasks(tasks: Task[]): Task[] {\n return tasks.flatMap((t) => [t, ...flattenTasks(t.subtasks ?? [])]);\n}\n\n/** A flattened task with the parent it hangs under, null for a top-level task. */\nexport interface TaskWithParent {\n task: Task;\n parent: Task | null;\n}\n\nexport function flattenTasksWithParents(tasks: Task[]): TaskWithParent[] {\n const rows: TaskWithParent[] = [];\n const walk = (t: Task, parent: Task | null) => {\n rows.push({ task: t, parent });\n for (const sub of t.subtasks ?? []) walk(sub, t);\n };\n for (const t of tasks) walk(t, null);\n return rows;\n}\n\nexport function migrateTask(task: Record<string, unknown>): Task {\n if (!task.assignedModel) {\n task.assignedModel = undefined;\n }\n if (!task.assignedRunner) {\n task.assignedRunner = 'claude-code';\n }\n if (!task.thinkingEffort) {\n task.thinkingEffort = undefined;\n }\n if (!task.completionMarker) {\n task.completionMarker = uuidv4();\n }\n if (task.verdict && task.verification) {\n delete task.verification;\n }\n if (!task.autonomy) {\n task.autonomy = undefined;\n }\n if (!task.sliceType) {\n task.sliceType = undefined;\n }\n if (!task.userStoriesCovered) {\n task.userStoriesCovered = undefined;\n }\n return task as unknown as Task;\n}\n\nexport function addTaskToPlan(tasks: Task[], partial: Partial<Task>): Task[] {\n const maxOrder = tasks.reduce((max, t) => Math.max(max, t.order), 0);\n const newTask = createTask({\n ...partial,\n order: partial.order ?? maxOrder + 1,\n status: 'pending',\n });\n return renumberTasks([...tasks, newTask]);\n}\n\nexport function removeTaskFromPlan(tasks: Task[], taskId: string): Task[] {\n const result = tasks\n .filter((t) => t.id !== taskId)\n .map((t) => ({\n ...t,\n dependencies: t.dependencies.filter((depId) => depId !== taskId),\n subtasks: removeTaskFromPlan(t.subtasks, taskId),\n }));\n return renumberTasks(result);\n}\n\nexport function updateTaskInPlan(tasks: Task[], taskId: string, changes: Partial<Task>): Task[] {\n return renumberTasks(\n tasks.map((t) => {\n if (t.id === taskId) return { ...t, ...changes, id: t.id };\n return { ...t, subtasks: updateTaskInPlan(t.subtasks, taskId, changes) };\n })\n );\n}\n\nexport function renumberTasks(tasks: Task[]): Task[] {\n return tasks.map((t, i) => ({\n ...t,\n order: i + 1,\n subtasks: renumberTasks(t.subtasks),\n }));\n}\n\nexport function validateModifiedPlan(original: Task[], modified: Task[]): PlanModificationWarnings {\n const allOriginal = flattenTasks(original);\n const allModified = flattenTasks(modified);\n const warnings = emptyWarnings();\n const originalMap = new Map(allOriginal.map((t) => [t.id, t]));\n const modifiedIds = new Set(allModified.map((t) => t.id));\n\n for (const ot of originalMap.values()) {\n if (ot.status === 'completed' && !modifiedIds.has(ot.id)) {\n warnings.deletedCompleted.push(ot.title);\n } else if (ot.status === 'in_progress' && !modifiedIds.has(ot.id)) {\n warnings.deletedInProgress.push(ot.title);\n }\n }\n\n for (const mt of allModified) {\n const orig = originalMap.get(mt.id);\n if (orig) {\n if (orig.status === 'completed' && mt.status !== 'completed') {\n warnings.changedCompleted.push(orig.title);\n }\n if (orig.status === 'in_progress' && (mt.status !== 'in_progress' || mt.prompt !== orig.prompt)) {\n warnings.modifiedInProgress.push(orig.title);\n }\n if (orig.status === 'failed' && mt.status === 'pending') {\n for (const dt of allModified) {\n if (dt.dependencies.includes(mt.id) && dt.status === 'blocked') {\n dt.status = 'pending';\n }\n }\n }\n }\n }\n\n for (const t of allModified) {\n for (const depId of t.dependencies) {\n if (!modifiedIds.has(depId)) {\n warnings.brokenDependencies.push(`${t.title} → ${depId}`);\n }\n }\n }\n\n return warnings;\n}\n\nexport interface ActiveTaskSession {\n id: string;\n taskId: string;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: string[];\n}\n\nexport interface ValidationContext {\n executionLog: TaskSnapshot[];\n oldPending: Task[];\n newPending: Task[];\n activeSessions: Map<string, ActiveTaskSession>;\n}\n\nexport type ValidationCheck = (ctx: ValidationContext) => ValidationResult;\n\nexport function warningsText(w: PlanModificationWarnings): string | null {\n const lines: string[] = [];\n if (w.deletedCompleted.length) lines.push(`Completed tasks deleted: ${w.deletedCompleted.join(', ')}`);\n if (w.changedCompleted.length) lines.push(`Completed tasks modified: ${w.changedCompleted.join(', ')}`);\n if (w.deletedInProgress.length) lines.push(`In-progress tasks deleted: ${w.deletedInProgress.join(', ')}`);\n if (w.modifiedInProgress.length) lines.push(`In-progress tasks modified: ${w.modifiedInProgress.join(', ')}`);\n if (w.brokenDependencies.length) lines.push(`Broken dependencies: ${w.brokenDependencies.join(', ')}`);\n return lines.length > 0 ? lines.join('\\n') : null;\n}\n","/** The envelope key a full plan reply is keyed by: `{\"tasks\":[...]}`. */\nexport const PLAN_ENVELOPE_KEY = 'tasks';\n/** The envelope key a targeted-edits reply is keyed by: `{\"taskOps\":[...]}`. */\nexport const TASK_OPS_ENVELOPE_KEY = 'taskOps';\n/** The envelope key a read-only detail request is keyed by: `{\"taskQuery\":{...}}`. */\nexport const TASK_QUERY_ENVELOPE_KEY = 'taskQuery';\n\n/** Thrown when an LLM plan response cannot be parsed into a valid plan. Carries the raw text so callers can log it or retry. */\nexport class PlanParseError extends Error {\n readonly raw: string;\n /**\n * True when the reply was cut off mid-object (output-token limit) rather\n * than malformed — the two need different repairs: a truncated emission will\n * be cut at the same point again unless the retry frees context or asks for\n * terser output.\n */\n readonly truncated: boolean;\n /**\n * True when the JSON parsed fine and a *shape* rule rejected it. The generic\n * \"could not be parsed as JSON\" corrective is a lie for these, so the model\n * re-sends the same object; a semantic rejection must be repaired by naming\n * the rule it broke.\n */\n readonly semantic: boolean;\n constructor(message: string, raw: string, opts?: { truncated?: boolean; semantic?: boolean }) {\n super(message);\n this.name = 'PlanParseError';\n this.raw = raw;\n this.truncated = opts?.truncated ?? false;\n this.semantic = opts?.semantic ?? false;\n }\n}\n/**\n * Strip the noise reasoning models wrap around their answer: markdown code\n * fences and `<think>…</think>` chain-of-thought blocks (which routinely contain\n * stray braces that would otherwise derail brace-balancing). Reasoning captured\n * out-of-band by the service never reaches here, but a model that inlines its\n * thinking in the content channel still must not corrupt parsing.\n */\nexport function stripModelNoise(raw: string): string {\n return raw\n .replace(/<think>[\\s\\S]*?<\\/think>/gi, '')\n .replace(/```json\\s*/gi, '')\n .replace(/```\\s*/g, '')\n .trim();\n}\n\n/**\n * Scan from `start` (which must point at a `{`) for the matching balanced close,\n * ignoring braces inside strings. Returns the object substring and whether it was\n * balanced; an unbalanced result means the input was truncated mid-object.\n */\nfunction balancedObjectAt(text: string, start: number): { json: string; balanced: boolean } {\n let depth = 0;\n let inString = false;\n let escaped = false;\n for (let i = start; i < text.length; i++) {\n const ch = text[i];\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') inString = true;\n else if (ch === '{') depth++;\n else if (ch === '}') {\n depth--;\n if (depth === 0) return { json: text.slice(start, i + 1), balanced: true };\n }\n }\n return { json: text.slice(start), balanced: false };\n}\n\n/**\n * Scan an LLM response for every balanced `{…}` object carrying a `\"<key>\":`\n * entry. Strips fences and `<think>` blocks first. A matching object is\n * skipped over wholesale, so its nested braces don't produce fragment\n * candidates; non-matching objects are descended into, so a match wrapped in\n * an unrelated outer object is still found. `fallback` preserves the legacy\n * behavior for callers with no keyed match: the first balanced object, else\n * the stripped text so a JSON.parse error still surfaces.\n */\nexport function extractObjectsWithKey(raw: string, key: string): {\n matches: string[];\n fallback: { json: string; balanced: boolean };\n sawBrace: boolean;\n /** An object ran to EOF unclosed — the classic signature of truncated output. */\n sawUnbalanced: boolean;\n} {\n const stripped = stripModelNoise(raw);\n const keyRe = new RegExp(`\"${key}\"\\\\s*:`);\n\n const matches: string[] = [];\n let firstObject: string | null = null;\n let sawBrace = false;\n let sawUnbalanced = false;\n let inString = false;\n let escaped = false;\n for (let i = 0; i < stripped.length; i++) {\n const ch = stripped[i];\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') { inString = true; continue; }\n if (ch !== '{') continue;\n\n sawBrace = true;\n const obj = balancedObjectAt(stripped, i);\n // A stray brace in a reasoning preamble produces an unbalanced candidate,\n // which we skip so it can't swallow the real object that follows.\n if (obj.balanced && keyRe.test(obj.json)) {\n matches.push(obj.json);\n i += obj.json.length - 1;\n continue;\n }\n if (!obj.balanced) sawUnbalanced = true;\n if (!firstObject && obj.balanced) firstObject = obj.json;\n }\n\n return {\n matches,\n fallback: firstObject !== null ? { json: firstObject, balanced: true } : { json: stripped, balanced: false },\n sawBrace,\n sawUnbalanced,\n };\n}\n\n/**\n * Pull the JSON object out of an LLM response and report whether it was balanced.\n * Strips fences and `<think>` blocks, then walks every top-level `{…}` and\n * returns the FIRST one that contains a `\"tasks\"` key — so a brace appearing\n * earlier in reasoning (or an unrelated preamble object) can't make us parse the\n * wrong thing. Falls back to the first balanced object and, failing that, the\n * first `{` onwards so a genuine `JSON.parse` error still surfaces.\n */\nexport function extractObjectWithBalance(raw: string): { json: string; balanced: boolean; sawBrace: boolean } {\n const { matches, fallback, sawBrace } = extractObjectsWithKey(raw, PLAN_ENVELOPE_KEY);\n if (matches.length > 0) return { json: matches[0], balanced: true, sawBrace };\n return { ...fallback, sawBrace };\n}\n\n/**\n * Pull the JSON object out of an LLM response. Strips markdown code fences and\n * `<think>` blocks, prefers the object containing a `tasks` key, and tolerates a\n * prose preamble and trailing prose. Returns the fence-stripped text unchanged\n * if no `{` is present so the caller's JSON.parse produces a clear failure.\n */\nexport function extractJsonObject(raw: string): string {\n return extractObjectWithBalance(raw).json;\n}\n\n/** Drop trailing commas before a closing `}` or `]` — a common model quirk. */\nexport function stripTrailingCommas(json: string): string {\n return json.replace(/,(\\s*[}\\]])/g, '$1');\n}\n\n/**\n * Escape raw control characters that appear INSIDE string literals — budget\n * models routinely emit multi-line `prompt` strings with literal newlines,\n * which strict JSON.parse rejects (\"bad control character\"). Characters\n * outside strings (formatting whitespace) are left untouched.\n */\nexport function escapeControlCharsInStrings(json: string): string {\n let out = '';\n let inString = false;\n let escaped = false;\n for (const ch of json) {\n if (inString) {\n if (escaped) { out += ch; escaped = false; continue; }\n if (ch === '\\\\') { out += ch; escaped = true; continue; }\n if (ch === '\"') { out += ch; inString = false; continue; }\n if (ch === '\\n') { out += '\\\\n'; continue; }\n if (ch === '\\r') { out += '\\\\r'; continue; }\n if (ch === '\\t') { out += '\\\\t'; continue; }\n out += ch;\n continue;\n }\n if (ch === '\"') inString = true;\n out += ch;\n }\n return out;\n}\n"],"mappings":"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","names":[]}
@@ -1,329 +0,0 @@
1
- import { q as IsolationTaskStatus, P as PlanIsolation, F as RunnerId, a as DiscoveredModel, T as Task, L as LegacyPlanState, Q as QueuedMessage, a3 as Verdict, J as TaskIsolation, d as IsolationHandoff, k as IsolationMergeResult, z as ResearchStep, B as ResearchStepOutcome } from './Task-BxQkPlXO.js';
2
- import { R as RunnerModeInfo } from './ModeResolver-Dkig8ghQ.js';
3
- import { A as ApprovalKind, e as ApprovalSource } from './ApprovalPolicy-BVhGdECT.js';
4
-
5
- /** A conflict's files as one surface shows them: every one, up to `max`, then how many more. */
6
- declare function capConflictFiles(files: string[], max?: number): string;
7
- /** A task record as ADR-0013 persisted it, for one repository. */
8
- interface Adr0013TaskRecord {
9
- taskId: string;
10
- order: number;
11
- title: string;
12
- branch: string;
13
- worktree: string;
14
- status: IsolationTaskStatus;
15
- linked: string[];
16
- }
17
- /** A run as ADR-0013 persisted it (0.4.23): one repository, its refs on the run itself. */
18
- interface Adr0013IsolationRun {
19
- id: string;
20
- workspaceRoot: string;
21
- baseRef: string;
22
- baseBranch?: string;
23
- integrationBranch: string;
24
- tasks: Record<string, Adr0013TaskRecord>;
25
- }
26
- interface Adr0013PlanIsolation {
27
- run: Adr0013IsolationRun;
28
- resolvers: Record<string, string>;
29
- }
30
- /**
31
- * A persisted plan isolation in today's shape. A run saved in the ADR-0013
32
- * format — recognised by its own `integrationBranch` — becomes a group of one
33
- * at `.`, keeping its branches, so a session saved by 0.4.23 resumes and hands
34
- * off exactly as it would have.
35
- */
36
- declare function migratePlanIsolation(state: PlanIsolation | Adr0013PlanIsolation): PlanIsolation;
37
-
38
- /**
39
- * The catalog a model/task-mode edit is checked against — the same discovered
40
- * models and manifest modes the planner was shown in the per-turn catalog
41
- * block (`PlannerConversation.catalogBlock`), so a refusal here can never name something
42
- * as invalid that the planner was never told about, or vice versa.
43
- */
44
- interface EditCatalog {
45
- modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>;
46
- runnerModes: Partial<Record<RunnerId, RunnerModeInfo[]>>;
47
- /** Raw (unfiltered) allowlist, keyed by runner — the same shape `coerceAssignments` takes. */
48
- perRunnerAllowlist?: Partial<Record<RunnerId, string[]>>;
49
- }
50
-
51
- /**
52
- * Targeted task edits emitted by the planner conversation (the 'task_ops'
53
- * ConversationTurn). Task references accept a task id, a "#<order>" ref, or a
54
- * bare order number — cheap models rarely echo UUIDs correctly.
55
- */
56
- type TaskOp = {
57
- op: 'update';
58
- taskId: string;
59
- changes: Partial<Task>;
60
- } | {
61
- op: 'add';
62
- task: Partial<Task>;
63
- handle?: string;
64
- } | {
65
- op: 'remove';
66
- taskId: string;
67
- } | {
68
- op: 'reorder';
69
- taskIds: string[];
70
- } | {
71
- op: 'merge';
72
- taskIds: string[];
73
- merged: Partial<Task>;
74
- handle?: string;
75
- } | {
76
- op: 'split';
77
- taskId: string;
78
- parts: Partial<Task>[];
79
- handle?: string;
80
- } | {
81
- op: 'rearm';
82
- taskId: string;
83
- changes?: Partial<Task>;
84
- };
85
- declare function textHasTaskOps(text: string): boolean;
86
- /** Parse a `{"taskOps":[...]}` reply. Throws PlanParseError when the JSON is unusable. */
87
- declare function parseTaskOpsJson(text: string): TaskOp[];
88
- interface ApplyTaskOpsResult {
89
- ok: boolean;
90
- tasks: Task[];
91
- errors: string[];
92
- /** Human-readable summary of what changed, for the chat transcript. */
93
- summary: string[];
94
- }
95
- /**
96
- * Pre-flight check for a merge: would collapsing the selected tasks into one
97
- * (rewiring every dependent of any selected task to the survivor) keep the
98
- * dependency graph valid? Rejects locked tasks and merges whose rewiring would
99
- * introduce a cycle. Display order is NOT checked here — neither
100
- * consecutiveness (a UI concern) nor a dependency the merge pulls out of order,
101
- * which {@link applyTaskOps} repairs; refusing on order would make this
102
- * pre-flight stricter than the applier it stands in for.
103
- */
104
- declare function canMergeTasks(tasks: Task[], selectedIds: string[]): {
105
- ok: boolean;
106
- error?: string;
107
- };
108
- /**
109
- * The shape the dependency helpers below read. Structural rather than `Task`,
110
- * because the TUI projects tasks into its own `TaskView`: a surface's
111
- * dependency picker and the API's validation must agree on which dependencies
112
- * are legal, and a signature only core can satisfy would have forced the TUI
113
- * to keep a second copy of the rule.
114
- */
115
- interface TaskRef {
116
- id: string;
117
- order: number;
118
- title: string;
119
- dependencies: string[];
120
- }
121
- /** The tasks listing `taskId` as a dependency — exactly what removing it detaches. */
122
- declare function dependentsOf<T extends Pick<TaskRef, 'id' | 'dependencies'>>(tasks: T[], taskId: string): T[];
123
- /**
124
- * The tasks that may become dependencies of `taskId`: those displayed before it.
125
- *
126
- * Offering only earlier tasks is what keeps a hand-edited graph valid without a
127
- * cycle check — dependencies then only ever point backwards in display order,
128
- * the same invariant `applyTaskOps` enforces. Omit `taskId` for a task that does
129
- * not exist yet: it lands last, so every current task is a candidate.
130
- */
131
- declare function dependencyCandidates<T extends Pick<TaskRef, 'id' | 'order'>>(tasks: T[], taskId?: string): T[];
132
- /** Pre-flight check for a hand-edited dependency list: every id exists and comes earlier. */
133
- declare function canSetDependencies<T extends TaskRef>(tasks: T[], taskId: string, dependencies: string[]): {
134
- ok: boolean;
135
- error?: string;
136
- };
137
- /** Pre-flight check for a split: the task exists and is not locked by execution. */
138
- declare function canSplitTask(tasks: Task[], taskId: string): {
139
- ok: boolean;
140
- error?: string;
141
- };
142
- /**
143
- * Apply task ops to a snapshot of the plan, atomically: either every op
144
- * applies and the result validates (deps resolve, no cycles, running and
145
- * completed tasks untouched), or nothing is returned and `errors` explains
146
- * why. The caller commits `tasks` on ok.
147
- */
148
- declare function applyTaskOps(currentTasks: Task[], ops: TaskOp[], runners: RunnerId[], catalog?: EditCatalog): ApplyTaskOpsResult;
149
-
150
- type SerializedTaskStatus = {
151
- id: string;
152
- status: string;
153
- verdict: {
154
- outcome: 'pass' | 'fail';
155
- reason: string;
156
- checks: Verdict['checks'];
157
- } | null;
158
- /** Advisory silence timestamp from VerdictEngine — not part of task status semantics. */
159
- idleSince?: string | null;
160
- /** Absent unless the plan has an isolation run, so a shared-root plan's updates are unchanged. */
161
- isolation?: TaskIsolation;
162
- };
163
- type SerializedTask = {
164
- id: string;
165
- order: number;
166
- title: string;
167
- type: string;
168
- description: string;
169
- dependencies: string[];
170
- assignedRunner: RunnerId;
171
- assignedModel: Task['assignedModel'] | null;
172
- taskMode: string;
173
- prompt: string | null;
174
- subtasks: SerializedTask[];
175
- userSteps: Task['userSteps'];
176
- thinkingEffort: Task['thinkingEffort'];
177
- autonomy: Task['autonomy'];
178
- sliceType: Task['sliceType'];
179
- userStoriesCovered: Task['userStoriesCovered'];
180
- };
181
- type SerializedPlan = {
182
- tasks: SerializedTask[];
183
- runners: RunnerId[];
184
- generatedAt: string;
185
- conversationHistory?: LegacyPlanState['conversationHistory'];
186
- prdMarkdown?: string;
187
- queuedMessages?: QueuedMessage[];
188
- };
189
- type SessionMessage = {
190
- type: 'plan_generated';
191
- plan: SerializedPlan;
192
- goal: string;
193
- runners: RunnerId[];
194
- } | {
195
- type: 'planner_message';
196
- content: string;
197
- timestamp: string;
198
- } | {
199
- type: 'status_update';
200
- tasks: SerializedTaskStatus[];
201
- } | {
202
- type: 'review_needed';
203
- tasks: SerializedTask[];
204
- } | {
205
- type: 'review_approved';
206
- } | {
207
- type: 'checkpoint';
208
- taskId: string;
209
- taskTitle: string;
210
- summary: string;
211
- } | {
212
- type: 'execution_complete';
213
- summary: {
214
- total: number;
215
- completed: number;
216
- failed: number;
217
- };
218
- } | {
219
- type: 'execution_stopped';
220
- } | {
221
- type: 'queue_ready';
222
- } | {
223
- type: 'task_updated';
224
- taskId: string;
225
- changes: Record<string, unknown>;
226
- } | {
227
- type: 'task_started';
228
- taskId: string;
229
- order: number;
230
- title: string;
231
- runner: RunnerId;
232
- modelId?: string;
233
- } | {
234
- type: 'task_output';
235
- taskId: string;
236
- text: string;
237
- } | {
238
- type: 'isolation_blocked';
239
- reason: 'dirty';
240
- repos?: string[];
241
- message: string;
242
- } | {
243
- type: 'isolation_handoff';
244
- repos: IsolationHandoff['repos'];
245
- landed: IsolationHandoff['landed'];
246
- } | {
247
- type: 'isolation_merge';
248
- result: IsolationMergeResult;
249
- } | {
250
- type: 'plan_thinking';
251
- text: string;
252
- } | {
253
- type: 'planner_liveness';
254
- } | {
255
- type: 'research_step';
256
- tool: string;
257
- toolLabel?: string;
258
- args: string;
259
- subagentId?: string;
260
- toolCallId?: string;
261
- } | {
262
- type: 'plan_token';
263
- token: string;
264
- } | {
265
- type: 'research_step_done';
266
- step: ResearchStep;
267
- subagentId?: string;
268
- } | {
269
- type: 'approval_request';
270
- id: string;
271
- kind: ApprovalKind;
272
- subject: string;
273
- scope: string;
274
- detail?: string;
275
- } | {
276
- type: 'approval_settled';
277
- id: string;
278
- granted: boolean;
279
- } | {
280
- type: 'approval_decided';
281
- kind: ApprovalKind;
282
- subject: string;
283
- scope: string;
284
- detail?: string;
285
- granted: boolean;
286
- source: Exclude<ApprovalSource, 'asked'>;
287
- };
288
- /**
289
- * A line a run wants the user to read — how it isolates, what it shares. Not a
290
- * {@link SessionMessage}: hosts that show notices as toasts already do, and
291
- * widening the union would break every exhaustive switch over it. A host with
292
- * no toast channel (the daemon) hands these to its clients by its own means.
293
- */
294
- type SessionNotice = {
295
- type: 'notice';
296
- level: 'info' | 'warn';
297
- message: string;
298
- };
299
- type SessionBroadcaster = (msg: SessionMessage) => void;
300
- declare function serializeTask(t: Task): SerializedTask;
301
- declare function serializeTaskStatus(t: Task, idleSince?: string | null, isolation?: TaskIsolation | null): SerializedTaskStatus;
302
- declare function serializePlan(plan: LegacyPlanState): SerializedPlan;
303
- declare function executionSummary(tasks: Task[]): {
304
- total: number;
305
- completed: number;
306
- failed: number;
307
- };
308
- declare const CHECKPOINT_TRUNCATE_LENGTH = 120;
309
- /** Truncate a checkpoint summary to a single line of at most CHECKPOINT_TRUNCATE_LENGTH chars. */
310
- declare function truncateCheckpointSummary(summary: string): string;
311
-
312
- /**
313
- * @param toolLabel The agent's own name for the tool, when a harness planner
314
- * produced the call (ADR-0009). It replaces the member name in the summary, so
315
- * the timeline says `Edit` rather than the catch-all `agent_tool` — and says
316
- * `WebFetch` rather than claiming a shell command ran.
317
- */
318
- declare function summarizeToolCall(tool: string, argsJson: string, toolLabel?: string): string;
319
- /**
320
- * Classify a tool outcome from its result text and success flag, so a surface
321
- * can render a refused `rm` or a denied `npm test` distinctly from a
322
- * successful run without re-deriving the refusal signatures per surface. The
323
- * refusal/denial strings come from `commandPolicy.ts`, `BaseFileSystem.ts`,
324
- * and the research-subagent wrapper — keeping the matching here means the
325
- * signatures stay in one place alongside the human summary.
326
- */
327
- declare function classifyOutcome(success: boolean, output: string): ResearchStepOutcome;
328
-
329
- export { type Adr0013IsolationRun as A, CHECKPOINT_TRUNCATE_LENGTH as C, type SerializedPlan as S, type TaskOp as T, type Adr0013PlanIsolation as a, type Adr0013TaskRecord as b, type ApplyTaskOpsResult as c, type SerializedTask as d, type SerializedTaskStatus as e, type SessionBroadcaster as f, type SessionMessage as g, type SessionNotice as h, type TaskRef as i, applyTaskOps as j, canMergeTasks as k, canSetDependencies as l, canSplitTask as m, capConflictFiles as n, classifyOutcome as o, dependencyCandidates as p, dependentsOf as q, executionSummary as r, migratePlanIsolation as s, parseTaskOpsJson as t, serializePlan as u, serializeTask as v, serializeTaskStatus as w, summarizeToolCall as x, textHasTaskOps as y, truncateCheckpointSummary as z };