@ordewell/core 0.4.9 → 0.4.10
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/dist/ModeResolver-Dx5gzNPC.d.ts +44 -0
- package/dist/ModeResolver-F5wnlSz1.d.mts +44 -0
- package/dist/{chunk-SRGQBGI5.mjs → chunk-IO6PEEN6.mjs} +3 -1
- package/dist/{chunk-SRGQBGI5.mjs.map → chunk-IO6PEEN6.mjs.map} +1 -1
- package/dist/{chunk-YXAIQKVE.mjs → chunk-TUGJXD4G.mjs} +2 -2
- package/dist/chunk-YI4ZDZEE.mjs +862 -0
- package/dist/chunk-YI4ZDZEE.mjs.map +1 -0
- package/dist/index.d.mts +170 -17
- package/dist/index.d.ts +170 -17
- package/dist/index.js +1031 -359
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +482 -160
- package/dist/index.mjs.map +1 -1
- package/dist/{parsing-RjN9JbyG.d.ts → parsing-BqbJySeo.d.mts} +5 -43
- package/dist/{parsing-xpozY5ac.d.mts → parsing-X0Gvuz-q.d.ts} +5 -43
- package/dist/parsing.d.mts +2 -1
- package/dist/parsing.d.ts +2 -1
- package/dist/parsing.js.map +1 -1
- package/dist/parsing.mjs +2 -2
- package/dist/{plan-utils-nfHKSbd3.d.mts → plan-utils-B5rVXNVI.d.mts} +27 -5
- package/dist/{plan-utils-Cgc0cCH1.d.ts → plan-utils-DtsfHnn_.d.ts} +27 -5
- package/dist/plan-utils.d.mts +2 -1
- package/dist/plan-utils.d.ts +2 -1
- package/dist/plan-utils.js +1 -9
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +2 -2
- package/package.json +1 -1
- package/dist/chunk-4KIWPW4K.mjs +0 -510
- package/dist/chunk-4KIWPW4K.mjs.map +0 -1
- /package/dist/{chunk-YXAIQKVE.mjs.map → chunk-TUGJXD4G.mjs.map} +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { h as RunnerId } from './Task-1NwmUImI.js';
|
|
2
|
+
|
|
3
|
+
interface RunnerModeInfo {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
description: string;
|
|
7
|
+
autonomous?: boolean;
|
|
8
|
+
safe?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** The slice of RunnerRegistry this module needs — structural, so nothing here depends on the plugin layer. */
|
|
11
|
+
interface ManifestLookup {
|
|
12
|
+
getManifest(runner: RunnerId): {
|
|
13
|
+
modes?: RunnerModeInfo[];
|
|
14
|
+
} | undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A runner's modes are declared only in its manifest, never through a service,
|
|
18
|
+
* so every caller that needs them fuses registry and runner list by hand. This
|
|
19
|
+
* is that fusion, in one place: the planner prompt, the mode pickers on each
|
|
20
|
+
* surface, and the runner-change retarget all read modes the same way.
|
|
21
|
+
*
|
|
22
|
+
* A runner with no modes maps to `[]` rather than being dropped — callers
|
|
23
|
+
* distinguish "declares no modes" from "not asked about".
|
|
24
|
+
*/
|
|
25
|
+
declare function runnerModesFrom(registry: ManifestLookup, runners: RunnerId[]): Record<RunnerId, RunnerModeInfo[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Find the mode ID a runner resolves to under the given toggle state.
|
|
28
|
+
* Tag-based first (autonomous/safe); falls back to positional last non-plan
|
|
29
|
+
* if no tag is present on any mode (degrades gracefully on untagged manifests).
|
|
30
|
+
* Returns undefined if `modes` is empty or undefined.
|
|
31
|
+
*/
|
|
32
|
+
declare function resolveDefaultMode(modes: RunnerModeInfo[] | undefined, autonomousDefault?: boolean): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Build modes compatible with the current toggle. Plan is excluded from the
|
|
35
|
+
* build-mode list (it is never a default for implementation tasks). Modes
|
|
36
|
+
* tagged exclusively for the OPPOSITE toggle are hidden — the LLM cannot see
|
|
37
|
+
* or select them. A dual-tagged mode (both autonomous + safe) is shown under
|
|
38
|
+
* either toggle.
|
|
39
|
+
*/
|
|
40
|
+
declare function filteredBuildModes(modes: RunnerModeInfo[], autonomousDefault: boolean): RunnerModeInfo[];
|
|
41
|
+
declare function buildModeGuide(runnerModes: Record<RunnerId, RunnerModeInfo[]>, autonomousDefault?: boolean): string;
|
|
42
|
+
declare function resolveTaskMode(emittedMode: string | undefined, runnerId: RunnerId, runnerModes: Record<RunnerId, RunnerModeInfo[]> | undefined, autonomousDefault?: boolean): string;
|
|
43
|
+
|
|
44
|
+
export { type ManifestLookup as M, type RunnerModeInfo as R, resolveTaskMode as a, buildModeGuide as b, runnerModesFrom as c, filteredBuildModes as f, resolveDefaultMode as r };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { h as RunnerId } from './Task-1NwmUImI.mjs';
|
|
2
|
+
|
|
3
|
+
interface RunnerModeInfo {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
description: string;
|
|
7
|
+
autonomous?: boolean;
|
|
8
|
+
safe?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** The slice of RunnerRegistry this module needs — structural, so nothing here depends on the plugin layer. */
|
|
11
|
+
interface ManifestLookup {
|
|
12
|
+
getManifest(runner: RunnerId): {
|
|
13
|
+
modes?: RunnerModeInfo[];
|
|
14
|
+
} | undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A runner's modes are declared only in its manifest, never through a service,
|
|
18
|
+
* so every caller that needs them fuses registry and runner list by hand. This
|
|
19
|
+
* is that fusion, in one place: the planner prompt, the mode pickers on each
|
|
20
|
+
* surface, and the runner-change retarget all read modes the same way.
|
|
21
|
+
*
|
|
22
|
+
* A runner with no modes maps to `[]` rather than being dropped — callers
|
|
23
|
+
* distinguish "declares no modes" from "not asked about".
|
|
24
|
+
*/
|
|
25
|
+
declare function runnerModesFrom(registry: ManifestLookup, runners: RunnerId[]): Record<RunnerId, RunnerModeInfo[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Find the mode ID a runner resolves to under the given toggle state.
|
|
28
|
+
* Tag-based first (autonomous/safe); falls back to positional last non-plan
|
|
29
|
+
* if no tag is present on any mode (degrades gracefully on untagged manifests).
|
|
30
|
+
* Returns undefined if `modes` is empty or undefined.
|
|
31
|
+
*/
|
|
32
|
+
declare function resolveDefaultMode(modes: RunnerModeInfo[] | undefined, autonomousDefault?: boolean): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Build modes compatible with the current toggle. Plan is excluded from the
|
|
35
|
+
* build-mode list (it is never a default for implementation tasks). Modes
|
|
36
|
+
* tagged exclusively for the OPPOSITE toggle are hidden — the LLM cannot see
|
|
37
|
+
* or select them. A dual-tagged mode (both autonomous + safe) is shown under
|
|
38
|
+
* either toggle.
|
|
39
|
+
*/
|
|
40
|
+
declare function filteredBuildModes(modes: RunnerModeInfo[], autonomousDefault: boolean): RunnerModeInfo[];
|
|
41
|
+
declare function buildModeGuide(runnerModes: Record<RunnerId, RunnerModeInfo[]>, autonomousDefault?: boolean): string;
|
|
42
|
+
declare function resolveTaskMode(emittedMode: string | undefined, runnerId: RunnerId, runnerModes: Record<RunnerId, RunnerModeInfo[]> | undefined, autonomousDefault?: boolean): string;
|
|
43
|
+
|
|
44
|
+
export { type ManifestLookup as M, type RunnerModeInfo as R, resolveTaskMode as a, buildModeGuide as b, runnerModesFrom as c, filteredBuildModes as f, resolveDefaultMode as r };
|
|
@@ -296,6 +296,7 @@ function warningsText(w) {
|
|
|
296
296
|
// src/services/JsonExtractor.ts
|
|
297
297
|
var PLAN_ENVELOPE_KEY = "tasks";
|
|
298
298
|
var TASK_OPS_ENVELOPE_KEY = "taskOps";
|
|
299
|
+
var TASK_QUERY_ENVELOPE_KEY = "taskQuery";
|
|
299
300
|
var PlanParseError = class extends Error {
|
|
300
301
|
raw;
|
|
301
302
|
/**
|
|
@@ -452,6 +453,7 @@ export {
|
|
|
452
453
|
warningsText,
|
|
453
454
|
PLAN_ENVELOPE_KEY,
|
|
454
455
|
TASK_OPS_ENVELOPE_KEY,
|
|
456
|
+
TASK_QUERY_ENVELOPE_KEY,
|
|
455
457
|
PlanParseError,
|
|
456
458
|
stripModelNoise,
|
|
457
459
|
extractObjectsWithKey,
|
|
@@ -460,4 +462,4 @@ export {
|
|
|
460
462
|
stripTrailingCommas,
|
|
461
463
|
escapeControlCharsInStrings
|
|
462
464
|
};
|
|
463
|
-
//# sourceMappingURL=chunk-
|
|
465
|
+
//# sourceMappingURL=chunk-IO6PEEN6.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/models/Task.ts","../src/services/JsonExtractor.ts"],"sourcesContent":["import { v4 as uuidv4 } from 'uuid';\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 type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted';\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. Absent for ordinary chat turns, so\n * sessions saved before markers existed degrade gracefully.\n */\n kind?: 'plan_generated' | 'system';\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\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 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\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\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
|
+
{"version":3,"sources":["../src/models/Task.ts","../src/services/JsonExtractor.ts"],"sourcesContent":["import { v4 as uuidv4 } from 'uuid';\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 type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted';\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. Absent for ordinary chat turns, so\n * sessions saved before markers existed degrade gracefully.\n */\n kind?: 'plan_generated' | 'system';\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\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 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\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":[]}
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
extractObjectsWithKey,
|
|
7
7
|
stripModelNoise,
|
|
8
8
|
stripTrailingCommas
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-IO6PEEN6.mjs";
|
|
10
10
|
|
|
11
11
|
// src/services/ModeResolver.ts
|
|
12
12
|
function runnerModesFrom(registry, runners) {
|
|
@@ -395,4 +395,4 @@ export {
|
|
|
395
395
|
validatePlanModification,
|
|
396
396
|
parsePartialPlan
|
|
397
397
|
};
|
|
398
|
-
//# sourceMappingURL=chunk-
|
|
398
|
+
//# sourceMappingURL=chunk-TUGJXD4G.mjs.map
|