@buildaharness/aielia 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +832 -0
- package/dist/action-approval-service.d.ts +52 -0
- package/dist/action-snapshot.d.ts +173 -0
- package/dist/action-tools.d.ts +38 -0
- package/dist/agent-loop.d.ts +372 -0
- package/dist/answer-claim.d.ts +51 -0
- package/dist/ask-clarification-service.d.ts +70 -0
- package/dist/ask-mode-flag.d.ts +25 -0
- package/dist/ask-response-format.d.ts +8 -0
- package/dist/assistant-session.d.ts +267 -0
- package/dist/assistant-source.d.ts +14 -0
- package/dist/assistant-types.d.ts +158 -0
- package/dist/assistant.d.ts +419 -0
- package/dist/batch-list-detector.d.ts +15 -0
- package/dist/batch-research-fixtures.d.ts +50 -0
- package/dist/benchmark-injected-failure.d.ts +27 -0
- package/dist/claude-cli-llm-client.d.ts +139 -0
- package/dist/claude-cli-prompt.d.ts +86 -0
- package/dist/cli-config.d.ts +27 -0
- package/dist/cli-session-D0uLdT2A.js +7863 -0
- package/dist/cli-session-D0uLdT2A.js.map +1 -0
- package/dist/cli-session.d.ts +63 -0
- package/dist/cli.d.ts +65 -0
- package/dist/cli.js +1693 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +175 -0
- package/dist/contradiction-checker.d.ts +27 -0
- package/dist/debug-log.d.ts +16 -0
- package/dist/decomposition-classifier.d.ts +38 -0
- package/dist/doctor-checks.d.ts +8 -0
- package/dist/email-smtp.d.ts +19 -0
- package/dist/email.d.ts +60 -0
- package/dist/error-classifier.d.ts +13 -0
- package/dist/execution-mode.d.ts +85 -0
- package/dist/fact-extraction.d.ts +143 -0
- package/dist/failure-mode-matcher.d.ts +32 -0
- package/dist/file-tools-mcp-server.mjs +1027 -0
- package/dist/file-tools.d.ts +179 -0
- package/dist/first-run.d.ts +22 -0
- package/dist/harness-bridge.d.ts +103 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +212 -0
- package/dist/index.js.map +1 -0
- package/dist/ink-test-render.d.ts +24 -0
- package/dist/lexical/patterns/batch-list-markers.json +8 -0
- package/dist/lexical/patterns/batch-list-markers.json.d.ts +11 -0
- package/dist/lexical/patterns/coding-fact-markers.json +8 -0
- package/dist/lexical/patterns/coding-fact-markers.json.d.ts +11 -0
- package/dist/lexical/patterns/enumeration-markers.json +20 -0
- package/dist/lexical/patterns/enumeration-markers.json.d.ts +23 -0
- package/dist/lexical/patterns/fact-markers.json +16 -0
- package/dist/lexical/patterns/fact-markers.json.d.ts +19 -0
- package/dist/lexical/patterns/injection-patterns.json +20 -0
- package/dist/lexical/patterns/injection-patterns.json.d.ts +23 -0
- package/dist/lexical/patterns/plan-mode-markers.json +10 -0
- package/dist/lexical/patterns/plan-mode-markers.json.d.ts +13 -0
- package/dist/lexical/patterns/risk-patterns.json +122 -0
- package/dist/lexical/patterns/risk-patterns.json.d.ts +125 -0
- package/dist/lexical/patterns/task-cancel-markers.json +22 -0
- package/dist/lexical/patterns/task-cancel-markers.json.d.ts +25 -0
- package/dist/lexical/patterns/template-keywords.json +20 -0
- package/dist/lexical/patterns/template-keywords.json.d.ts +23 -0
- package/dist/lexical/patterns/tool-yield-markers.json +22 -0
- package/dist/lexical/patterns/tool-yield-markers.json.d.ts +25 -0
- package/dist/lexical/patterns.d.ts +64 -0
- package/dist/memory-service.d.ts +219 -0
- package/dist/model-pricing.d.ts +19 -0
- package/dist/network-containment.d.ts +21 -0
- package/dist/node-config-store.d.ts +29 -0
- package/dist/node-display-names.d.ts +26 -0
- package/dist/node-fs-backend.d.ts +8 -0
- package/dist/non-interactive-mode.d.ts +22 -0
- package/dist/one-loop-flag.d.ts +38 -0
- package/dist/plan-approval-service.d.ts +62 -0
- package/dist/plan-builder.d.ts +32 -0
- package/dist/plan-drafting-service.d.ts +87 -0
- package/dist/plan-drafting.d.ts +58 -0
- package/dist/plan-mode-flag.d.ts +34 -0
- package/dist/plan-service.d.ts +61 -0
- package/dist/plan-sketch-service.d.ts +24 -0
- package/dist/plan-sketch.d.ts +20 -0
- package/dist/plan-store.d.ts +232 -0
- package/dist/plan-templates/data/content_creation.json.d.ts +75 -0
- package/dist/plan-templates/data/decision_making.json.d.ts +66 -0
- package/dist/plan-templates/data/problem_solving.json.d.ts +75 -0
- package/dist/plan-templates/data/process_improvement.json.d.ts +57 -0
- package/dist/plan-templates/data/project_planning.json.d.ts +66 -0
- package/dist/plan-templates/data/research_analysis.json.d.ts +66 -0
- package/dist/plan-templates/data/trip_planning.json.d.ts +66 -0
- package/dist/plan-templates/index.d.ts +33 -0
- package/dist/plan-verification.d.ts +31 -0
- package/dist/reminder-tools.d.ts +13 -0
- package/dist/response-service.d.ts +73 -0
- package/dist/review-checker.d.ts +22 -0
- package/dist/risk-classifier.d.ts +7 -0
- package/dist/scripted-llm-client.d.ts +37 -0
- package/dist/semantic-criterion-coverage.d.ts +30 -0
- package/dist/shell-executor.d.ts +14 -0
- package/dist/shell-tools.d.ts +84 -0
- package/dist/spend-cap.d.ts +41 -0
- package/dist/spike.d.ts +1 -0
- package/dist/spike.js +18 -0
- package/dist/spike.js.map +1 -0
- package/dist/supervisor-decider.d.ts +3 -0
- package/dist/system-prompt.d.ts +8 -0
- package/dist/task-mapping.d.ts +42 -0
- package/dist/tool-control-plane.d.ts +67 -0
- package/dist/tool-policy.d.ts +45 -0
- package/dist/tool-step.d.ts +23 -0
- package/dist/tool-yield-classifier.d.ts +7 -0
- package/dist/trace-events.d.ts +126 -0
- package/dist/transcript-compaction.d.ts +13 -0
- package/dist/trust-tagging.d.ts +38 -0
- package/dist/tui-app-DxLPSGh2.js +481 -0
- package/dist/tui-app-DxLPSGh2.js.map +1 -0
- package/dist/tui-app.d.ts +125 -0
- package/dist/tui-input.d.ts +51 -0
- package/dist/tui-mode-flag.d.ts +36 -0
- package/dist/tui-output-capture.d.ts +30 -0
- package/dist/tui-spike.d.ts +1 -0
- package/dist/turn-intent-classifier.d.ts +96 -0
- package/dist/turn-interpreter.d.ts +71 -0
- package/dist/turn-policy.d.ts +45 -0
- package/dist/web-fetch-core.d.ts +65 -0
- package/dist/web-search-provider.d.ts +12 -0
- package/dist/web-tools.d.ts +26 -0
- package/package.json +69 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ILLMClient, TokenUsage, MemoryAdapter } from '@buildaharness/runtime';
|
|
2
|
+
import { AskResponse } from '@buildaharness/harness';
|
|
3
|
+
import { AssistantSession } from './assistant-session.js';
|
|
4
|
+
import { PlanService } from './plan-service.js';
|
|
5
|
+
import { PlanApprovalService } from './plan-approval-service.js';
|
|
6
|
+
import { AssistantTurnResult } from './assistant-types.js';
|
|
7
|
+
import { TraceEvent } from './trace-events.js';
|
|
8
|
+
import { AgentLoop } from './agent-loop.js';
|
|
9
|
+
/**
|
|
10
|
+
* `{ fallThrough: true }` only ever comes from a *fresh, auto-triggered* entry (`seed` set —
|
|
11
|
+
* assistant.ts's P3 auto-trigger, never PersonalAssistant.enterPlanMode's manual/test entry point)
|
|
12
|
+
* whose very first drafting call failed (malformed/insufficient LLM output, or a thrown error —
|
|
13
|
+
* draftPlanRevision's own try/catch already normalizes both to `null`) — this phase's Validation
|
|
14
|
+
* requires falling back to *not entering plan mode at all* rather than staging a broken draft or
|
|
15
|
+
* getting the user stuck in a "couldn't update the draft" loop they never asked to enter (same
|
|
16
|
+
* "null means fall through" convention `buildPlanFromTemplate` already used). Mirrors
|
|
17
|
+
* `PlanApprovalOutcome`'s exact shape/discipline. A manual `enterPlanMode` entry with no `seed`
|
|
18
|
+
* keeps today's pre-P3 behavior on a failed first draft: stay in drafting, ask the user to
|
|
19
|
+
* rephrase — that path is an explicit, already-active choice to draft, not a guess the system
|
|
20
|
+
* made on the user's behalf.
|
|
21
|
+
*/
|
|
22
|
+
export type PlanDraftOutcome = {
|
|
23
|
+
fallThrough: true;
|
|
24
|
+
} | AssistantTurnResult;
|
|
25
|
+
/**
|
|
26
|
+
* P3 of plans/ask_question_and_plan_mode_plan.html — how `assistant.ts`'s auto-trigger tells
|
|
27
|
+
* `draftTurn` what to seed a *fresh* draft with (ignored on every later revision of an
|
|
28
|
+
* already-drafting plan). `templateName` set means a template matched — the draft starts from
|
|
29
|
+
* that template's own skeleton (today's personalization behavior, reused, not discarded — see
|
|
30
|
+
* `buildPlanFromTemplate`'s doc comment — except the drafting call itself, not a separate
|
|
31
|
+
* personalization call, does the adapting, so it's free to add/remove/split/reorder relative to
|
|
32
|
+
* the skeleton rather than preserving it 1:1). `grounded: true` (only meaningful when
|
|
33
|
+
* `templateName` is null) means ground the from-scratch draft in real repo state via a bounded
|
|
34
|
+
* read_file/list_directory walk before drafting.
|
|
35
|
+
*/
|
|
36
|
+
export interface PlanDraftSeed {
|
|
37
|
+
templateName: string | null;
|
|
38
|
+
grounded: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Owns plan mode's P1 exclusive drafting loop: while `AssistantSession.getPlanModeState(sessionId)`
|
|
42
|
+
* is `active`, `assistant.ts`'s runTurn routes every message here instead of TurnInterpreter's
|
|
43
|
+
* normal classify/tool-loop pipeline (INV-30 — no tool other than this call itself executes for
|
|
44
|
+
* that session while drafting is active). Two designed exits: an explicit cancel/abort phrase
|
|
45
|
+
* (handled entirely here), and P2's approval response once a draft is staged for
|
|
46
|
+
* `awaiting_approval` (handed off to `PlanApprovalService.stageAndRespond` below the moment a
|
|
47
|
+
* revision's `readyForApproval` is true — resolved via `turn(message, { planApprovalId,
|
|
48
|
+
* planDecision })`, checked earlier in `assistant.ts`'s runTurn than this drafting path at all).
|
|
49
|
+
*
|
|
50
|
+
* The drafting call itself (plan-drafting.ts's draftPlanRevision) has no tool access of its own —
|
|
51
|
+
* P3 grounds a from-scratch draft by running a separate, bounded read_file/list_directory
|
|
52
|
+
* investigation walk first (see the `seed?.grounded` branch in `draftTurn` below) and feeding its
|
|
53
|
+
* findings in as extra context, rather than giving the drafting call itself live tool-calling.
|
|
54
|
+
* P3 also wired the automatic judgment-based trigger this class was previously waiting on:
|
|
55
|
+
* `assistant.ts`'s runTurn now calls `session.enterPlanMode` and routes into `draftTurn` (with a
|
|
56
|
+
* `PlanDraftSeed`) the moment `classifyTurnIntent` reports `matchedPlanTemplate !== null ||
|
|
57
|
+
* needsMultiStepPlan` and no plan is already active — `PersonalAssistant.enterPlanMode` remains
|
|
58
|
+
* available as a direct entry point too (tests, a future `/plan` command), unchanged.
|
|
59
|
+
*/
|
|
60
|
+
export declare class PlanDraftingService {
|
|
61
|
+
private readonly planService;
|
|
62
|
+
private readonly session;
|
|
63
|
+
private readonly llmClient;
|
|
64
|
+
private readonly model;
|
|
65
|
+
private readonly planApproval;
|
|
66
|
+
private readonly onTrace;
|
|
67
|
+
private readonly agentLoop?;
|
|
68
|
+
private readonly memory?;
|
|
69
|
+
constructor(planService: PlanService, session: AssistantSession, llmClient: ILLMClient, model: () => string | undefined, planApproval: PlanApprovalService, onTrace: ((event: TraceEvent) => void) | undefined, agentLoop?: Pick<AgentLoop, "runSupervisorInvestigation"> | undefined, memory?: MemoryAdapter | undefined);
|
|
70
|
+
private planAskPendingKey;
|
|
71
|
+
/** Peeked by `assistant.ts`'s runTurn before it decides whether a `pendingClarificationId` belongs to this nested-ask side channel (P8) or AskClarificationService's harness-resume path (Q2) — the two staging stores are otherwise independent, so an opaque ID has no other way to say which one it came from. */
|
|
72
|
+
isPendingAsk(id: string): Promise<boolean>;
|
|
73
|
+
/**
|
|
74
|
+
* Resolves a nested plan-drafting ask by ID (P8) — the drafting counterpart to
|
|
75
|
+
* AskClarificationService.resolvePendingClarification, but folds the answer into the *next
|
|
76
|
+
* drafting revision call* instead of resuming a paused harness run (there is none mid-draft).
|
|
77
|
+
* Fail-closed (Protected Invariants) exactly like Q2: no answer, or one that doesn't validate
|
|
78
|
+
* against exactly the staged questions (INV-28), leaves the turn in `needs_clarification`
|
|
79
|
+
* rather than silently proceeding or discarding the batch.
|
|
80
|
+
*/
|
|
81
|
+
resolvePendingAsk(sessionId: string, transcriptKey: string, pendingAskId: string, response: AskResponse | undefined, onUsage: (usage: TokenUsage) => void): Promise<AssistantTurnResult>;
|
|
82
|
+
draftTurn(sessionId: string, transcriptKey: string, userMessage: string, onUsage: (usage: TokenUsage) => void, seed?: PlanDraftSeed): Promise<PlanDraftOutcome>;
|
|
83
|
+
private stillAwaitingApproval;
|
|
84
|
+
/** Stages a single nested question (P8) and returns the `needs_clarification` result — mirrors AskClarificationService.stageAndRespond's "append the user's message, return no reply yet" shape, but into `plan-ask-pending:` instead of `ask-pending:`, and resolved by `resolvePendingAsk` above instead of a harness resume. */
|
|
85
|
+
private stageNestedAsk;
|
|
86
|
+
private cancelDrafting;
|
|
87
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ILLMClient, TokenUsage } from '@buildaharness/runtime';
|
|
2
|
+
import { AskQuestion } from '@buildaharness/harness';
|
|
3
|
+
import { DecomposedTaskSpec } from './decomposition-classifier.js';
|
|
4
|
+
import { PlanTaskRecord } from './plan-store.js';
|
|
5
|
+
/**
|
|
6
|
+
* P1 of plans/ask_question_and_plan_mode_plan.html — the "plan-drafting revision call" P1's
|
|
7
|
+
* routing (assistant.ts's runTurn, PlanDraftingService) sends every message to while
|
|
8
|
+
* `planMode.active`. Deliberately narrower than P3's eventual version: no tools at all (P3 adds
|
|
9
|
+
* read_file/list_directory grounding for a from-scratch draft — see Section & non-goals),
|
|
10
|
+
* only revises the running draft from conversation text. Kept in its own module, same reasoning
|
|
11
|
+
* plan-builder.ts documents for buildPlanFromTemplate: a single-purpose LLM call, unit-testable
|
|
12
|
+
* without the rest of the drafting plumbing.
|
|
13
|
+
*/
|
|
14
|
+
export interface PlanDraftTurn {
|
|
15
|
+
/** A short, human-readable summary of the current draft, shown back to the user as the turn's reply. */
|
|
16
|
+
reply: string;
|
|
17
|
+
tasks: DecomposedTaskSpec[];
|
|
18
|
+
successCriteria: string;
|
|
19
|
+
rationale: string;
|
|
20
|
+
/**
|
|
21
|
+
* P2 of plans/ask_question_and_plan_mode_plan.html — true only once the model judges the user
|
|
22
|
+
* has clearly signaled they're satisfied with the current draft and want to proceed, at which
|
|
23
|
+
* point PlanDraftingService hands off to PlanApprovalService instead of returning another plain
|
|
24
|
+
* drafting reply. Defaults false on any malformed/missing value (see parsing below) — a
|
|
25
|
+
* misparsed response must never accidentally fast-track a plan into the approval gate.
|
|
26
|
+
*/
|
|
27
|
+
readyForApproval: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* P8 of plans/ask_question_and_plan_mode_plan.html — set only when this revision hit a genuine
|
|
30
|
+
* ambiguity worth pausing on instead of guessing (an ambiguous scope boundary, or a
|
|
31
|
+
* MEDIUM/HIGH-risk branch point with more than one viable approach) — e.g. "which of these two
|
|
32
|
+
* frameworks should the plan target?" A drafting call that sets this always also leaves
|
|
33
|
+
* `readyForApproval` false; `PlanDraftingService.draftTurn` stages it as a nested
|
|
34
|
+
* `needs_clarification` exchange (reusing Q0's `AskQuestion` shape and Q2/Q5/Q6's existing
|
|
35
|
+
* rendering) instead of treating `reply`/`tasks` as an ordinary revision, and the user's answer
|
|
36
|
+
* folds into the *next* drafting call as its `userMessage` rather than resuming a harness run
|
|
37
|
+
* (there is no harness run mid-draft to resume — see plan-drafting-service.ts's doc comment).
|
|
38
|
+
* Absent on every ordinary revision, same "absent when unused" convention the rest of this
|
|
39
|
+
* module follows.
|
|
40
|
+
*/
|
|
41
|
+
question?: AskQuestion;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Spends one real LLM call revising the draft given `userMessage` — same "malformed/incomplete
|
|
45
|
+
* JSON is the expected failure mode, not the edge case" fallback buildPlanFromTemplate uses: any
|
|
46
|
+
* parse failure, or a response with zero usable tasks, returns null (caller keeps the draft
|
|
47
|
+
* unchanged and reports a generic "couldn't update the draft" reply rather than staging garbage).
|
|
48
|
+
*/
|
|
49
|
+
export declare function draftPlanRevision(llmClient: ILLMClient, userMessage: string, currentTasks: PlanTaskRecord[], currentSuccessCriteria: string, currentRationale: string, model?: string, onUsage?: (usage: TokenUsage) => void,
|
|
50
|
+
/**
|
|
51
|
+
* P3 of plans/ask_question_and_plan_mode_plan.html — grounding text for a from-scratch (no
|
|
52
|
+
* template match) draft, gathered from a bounded read_file/list_directory investigation walk
|
|
53
|
+
* (see AgentLoop.runSupervisorInvestigation, reused rather than building a new search engine —
|
|
54
|
+
* Scope & non-goals) so a code-shaped request's draft can reference real repo state instead of
|
|
55
|
+
* guessing. Absent for every other call (a template-seeded draft, or any revision after the
|
|
56
|
+
* first) — same "absent when unused" convention the rest of this module follows.
|
|
57
|
+
*/
|
|
58
|
+
groundingContext?: string): Promise<PlanDraftTurn | null>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure logic backing PersonalAssistant's ASSISTANT_PLAN_MODE handling — split out so it's
|
|
3
|
+
* unit-testable in isolation, mirroring one-loop-flag.ts/ask-mode-flag.ts's exact shape
|
|
4
|
+
* (module-level default, an injectable resolver so tests never touch real process.env).
|
|
5
|
+
*
|
|
6
|
+
* P11 of plans/ask_question_and_plan_mode_plan.html — the rollout flag gating P1-P8's whole
|
|
7
|
+
* plan-mode behavior change (exclusive drafting, mandatory approval, the generalized trigger,
|
|
8
|
+
* auto-advance, the markdown/delegate/trust-mode extensions) behind one switch. 'legacy' (the
|
|
9
|
+
* default, for the whole rollout window, per `DEFAULT_PLAN_MODE`): P3's judgment-based
|
|
10
|
+
* auto-trigger (assistant.ts's `runTurn`, the one production call site that ever calls
|
|
11
|
+
* `PersonalAssistant.enterPlanMode`) never fires, so `planMode.active` never becomes true for
|
|
12
|
+
* real user traffic and the entire P1-P8/P10 apparatus downstream of it stays dormant by
|
|
13
|
+
* construction — same "inert until its one entry point is reached" shape P1 itself already
|
|
14
|
+
* documents for the pre-P3 window, and the same discipline the Trajectory Supervisor's flag
|
|
15
|
+
* uses (a single call-site gate rather than scattering flag checks through every downstream
|
|
16
|
+
* consumer). Explicit/manual entry (`enterPlanMode` called directly by a test, or a future
|
|
17
|
+
* `/plan enter`-style CLI verb) is deliberately left ungated by this flag, since it has no
|
|
18
|
+
* production caller today independent of P3 (see `enterPlanMode`'s own doc comment) — there is
|
|
19
|
+
* nothing for 'legacy' to preserve byte-identically there. 'gated' lets P3's auto-trigger run.
|
|
20
|
+
*/
|
|
21
|
+
export type PlanRolloutMode = 'gated' | 'legacy';
|
|
22
|
+
export declare const DEFAULT_PLAN_MODE: PlanRolloutMode;
|
|
23
|
+
/**
|
|
24
|
+
* Value-level resolver, shared by every surface: `resolvePlanMode` (CLI, reads
|
|
25
|
+
* `process.env.ASSISTANT_PLAN_MODE`) and chat-ui's `envOverridesFromImportMetaEnv` (browser
|
|
26
|
+
* build, reads Vite's `import.meta.env.VITE_ASSISTANT_PLAN_MODE`). An unset or empty value
|
|
27
|
+
* falls back to the default silently; an unrecognized non-empty value falls back with a
|
|
28
|
+
* startup warning naming `varName` — same typo-tolerance-with-a-warning convention
|
|
29
|
+
* normalizeOneLoopMode/normalizeAskMode use, since a silently-misread flag here is
|
|
30
|
+
* safety-relevant (it decides whether real turns can auto-enter a durable, approval-gated
|
|
31
|
+
* planning loop at all).
|
|
32
|
+
*/
|
|
33
|
+
export declare function normalizePlanMode(raw: string | undefined, varName?: string): PlanRolloutMode;
|
|
34
|
+
export declare function resolvePlanMode(env: NodeJS.ProcessEnv): PlanRolloutMode;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { MemoryAdapter } from '@buildaharness/runtime';
|
|
2
|
+
import { TaskStatus } from '@buildaharness/harness';
|
|
3
|
+
import { Plan } from './plan-builder.js';
|
|
4
|
+
import { nextPendingTask, PlanRecord, PlanPosition, TaskCancelMatch, PlanFsPersistence } from './plan-store.js';
|
|
5
|
+
/**
|
|
6
|
+
* Thin constructor-scoped facade over `memory` for durable-plan bookkeeping — plan-store.ts (and
|
|
7
|
+
* plan-builder.ts/plan-templates/, untouched by this split) already hold the real logic, so this
|
|
8
|
+
* module exists only so TurnInterpreter, HarnessBridge (mid-run `onCheckpoint`), and
|
|
9
|
+
* ResponseService (post-run planStatus assembly) share one collaborator instead of each importing
|
|
10
|
+
* plan-store.ts's free functions directly and threading `memory` themselves.
|
|
11
|
+
*
|
|
12
|
+
* `fsPersistence` (plan mode's P5) is the same `{backend, workspaceRoot}` pair
|
|
13
|
+
* AssistantSession.undoWorkspace() resolves for write_file/run_shell_command — `undefined` on a
|
|
14
|
+
* surface with no real filesystem (e.g. a browser tab), in which case every method below behaves
|
|
15
|
+
* exactly as it did before P5, Dexie-only.
|
|
16
|
+
*/
|
|
17
|
+
export declare class PlanService {
|
|
18
|
+
private readonly memory;
|
|
19
|
+
private readonly fsPersistence?;
|
|
20
|
+
constructor(memory: MemoryAdapter, fsPersistence?: PlanFsPersistence | undefined);
|
|
21
|
+
loadActivePlan(sessionId: string): Promise<PlanRecord | null>;
|
|
22
|
+
/** Unlike loadActivePlan, returns the stored record regardless of `mode` — used by plan mode's P1 drafting loop, which needs to resume a `mode: 'drafting'` record loadActivePlan would never return. */
|
|
23
|
+
loadPlanRecord(sessionId: string): Promise<PlanRecord | null>;
|
|
24
|
+
createDraftPlanRecord(templateName: string | null): PlanRecord;
|
|
25
|
+
matchTaskCancelAttempt(message: string, plan: PlanRecord): TaskCancelMatch | null;
|
|
26
|
+
cancelPlanTask(sessionId: string, plan: PlanRecord, taskId: string): Promise<PlanRecord>;
|
|
27
|
+
editPlanTask(sessionId: string, plan: PlanRecord, taskId: string, newDescription: string): Promise<PlanRecord>;
|
|
28
|
+
abandonPlan(sessionId: string, plan: PlanRecord): Promise<void>;
|
|
29
|
+
stagePlanForApproval(sessionId: string, plan: PlanRecord): Promise<PlanRecord>;
|
|
30
|
+
activatePlanRecord(sessionId: string, plan: PlanRecord): Promise<PlanRecord>;
|
|
31
|
+
savePlan(sessionId: string, plan: PlanRecord): Promise<void>;
|
|
32
|
+
createPlanRecord(plan: Plan): PlanRecord;
|
|
33
|
+
updatePlanFromRun(plan: PlanRecord, taskGraphTasks: {
|
|
34
|
+
id: string;
|
|
35
|
+
status: TaskStatus;
|
|
36
|
+
}[]): PlanRecord;
|
|
37
|
+
planCompletionPct(plan: PlanRecord): number;
|
|
38
|
+
computePlanPosition(plan: PlanRecord, taskGraphTasks: {
|
|
39
|
+
id: string;
|
|
40
|
+
status: TaskStatus;
|
|
41
|
+
}[]): PlanPosition | null;
|
|
42
|
+
nextPendingTask(plan: PlanRecord): ReturnType<typeof nextPendingTask>;
|
|
43
|
+
/** Persists `plan`'s current task statuses and returns the AssistantTurnResult.planStatus shape both the paused and success branches of ResponseService build identically. */
|
|
44
|
+
saveAndSummarize(sessionId: string, plan: PlanRecord, taskGraphTasks: {
|
|
45
|
+
id: string;
|
|
46
|
+
status: TaskStatus;
|
|
47
|
+
}[]): Promise<{
|
|
48
|
+
plan: PlanRecord;
|
|
49
|
+
completionPct: number;
|
|
50
|
+
planStatus: {
|
|
51
|
+
templateName: string | null;
|
|
52
|
+
successCriteria: string;
|
|
53
|
+
completionPct: number;
|
|
54
|
+
tasks: {
|
|
55
|
+
id: string;
|
|
56
|
+
description: string;
|
|
57
|
+
status: string;
|
|
58
|
+
}[];
|
|
59
|
+
};
|
|
60
|
+
}>;
|
|
61
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ILLMClient, TokenUsage } from '@buildaharness/runtime';
|
|
2
|
+
import { AssistantTurnResult } from './assistant-types.js';
|
|
3
|
+
import { TraceEvent } from './trace-events.js';
|
|
4
|
+
import { AgentLoop } from './agent-loop.js';
|
|
5
|
+
/**
|
|
6
|
+
* P9 of plans/ask_question_and_plan_mode_plan.html — the lightweight plan-sketch delegate: one
|
|
7
|
+
* bounded LLM call (grounded, best-effort, via the same read_file/list_directory investigation
|
|
8
|
+
* walk P3's from-scratch drafting reuses — AgentLoop.runSupervisorInvestigation, not a new search
|
|
9
|
+
* engine) that returns a plain proposed task list in the chat reply itself. Deliberately NOT
|
|
10
|
+
* PlanDraftingService: it never touches PlanService/PlanRecord, never calls
|
|
11
|
+
* AssistantSession.enterPlanMode, and stages nothing for PlanApprovalService to see (INV-35) —
|
|
12
|
+
* advice for one turn, not a commitment to execute. Kept in its own module/class, same
|
|
13
|
+
* single-purpose-collaborator discipline every other plan-mode service in this package follows,
|
|
14
|
+
* so a caller that only wants the free-form sketch never pulls in the stateful drafting/approval
|
|
15
|
+
* machinery at all.
|
|
16
|
+
*/
|
|
17
|
+
export declare class PlanSketchService {
|
|
18
|
+
private readonly llmClient;
|
|
19
|
+
private readonly model;
|
|
20
|
+
private readonly onTrace;
|
|
21
|
+
private readonly agentLoop?;
|
|
22
|
+
constructor(llmClient: ILLMClient, model: () => string | undefined, onTrace: ((event: TraceEvent) => void) | undefined, agentLoop?: Pick<AgentLoop, "runSupervisorInvestigation"> | undefined);
|
|
23
|
+
sketch(request: string, onUsage: (usage: TokenUsage) => void): Promise<AssistantTurnResult>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ILLMClient, TokenUsage } from '@buildaharness/runtime';
|
|
2
|
+
/**
|
|
3
|
+
* P9 of plans/ask_question_and_plan_mode_plan.html — the lightweight plan-sketch delegate's one
|
|
4
|
+
* bounded LLM call. Deliberately the cheapest possible shape: a single plain-text reply the
|
|
5
|
+
* caller shows directly in the chat, not a structured task list staged anywhere — there is no
|
|
6
|
+
* PlanRecord, no PlanTaskRecord, nothing for P2's approval gate to see (INV-35). Distinct from
|
|
7
|
+
* plan-drafting.ts's draftPlanRevision, which is the first call in an iterative, stateful,
|
|
8
|
+
* eventually-approved drafting loop; this is one-shot advice, thrown away the moment the reply
|
|
9
|
+
* is shown — there is no "revise the sketch" follow-up call, no running draft to revise.
|
|
10
|
+
*/
|
|
11
|
+
export interface PlanSketchResult {
|
|
12
|
+
reply: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Spends one real LLM call sketching a plan for `request` — same "malformed/incomplete output is
|
|
16
|
+
* the expected failure mode, not the edge case" fallback plan-drafting.ts's draftPlanRevision
|
|
17
|
+
* uses: any thrown error, or an empty reply, returns null so the caller can show a generic
|
|
18
|
+
* "couldn't sketch that" message instead of an empty bubble.
|
|
19
|
+
*/
|
|
20
|
+
export declare function sketchPlan(llmClient: ILLMClient, request: string, groundingContext: string | undefined, model: string | undefined, onUsage: ((usage: TokenUsage) => void) | undefined): Promise<PlanSketchResult | null>;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { FsBackend, MemoryAdapter } from '@buildaharness/runtime';
|
|
2
|
+
import { TaskStatus } from '@buildaharness/harness';
|
|
3
|
+
import { Plan } from './plan-builder.js';
|
|
4
|
+
export interface PlanTaskRecord {
|
|
5
|
+
id: string;
|
|
6
|
+
description: string;
|
|
7
|
+
depends_on: string[];
|
|
8
|
+
status: TaskStatus;
|
|
9
|
+
/**
|
|
10
|
+
* Per-task risk, attached at plan-creation time (see plan-builder.ts's buildPlanFromTemplate)
|
|
11
|
+
* instead of re-derived from the description text on every resume. Optional only for backward
|
|
12
|
+
* compatibility with a plan persisted before this field existed — assistant.ts's
|
|
13
|
+
* toHarnessTasks falls back to the lexical planTaskRiskLevel/classifyRisk for those.
|
|
14
|
+
*/
|
|
15
|
+
riskLevel?: 'LOW' | 'MEDIUM' | 'HIGH';
|
|
16
|
+
/**
|
|
17
|
+
* True once the user explicitly cancelled this specific task (see matchTaskCancelAttempt/
|
|
18
|
+
* cancelPlanTask) — distinct from `status`, which gets set to 'COMPLETE' alongside this so the
|
|
19
|
+
* harness's task-graph selection and dependent-unblocking treat it as resolved the same way a
|
|
20
|
+
* genuinely finished task would be (TaskStatus, from @buildaharness/harness, has no CANCELLED
|
|
21
|
+
* value of its own — extending that union is a cross-package change out of scope here).
|
|
22
|
+
* formatPlanProgress/planCompletionPct read this flag to show/count it accurately instead of
|
|
23
|
+
* claiming the user's own work actually got done.
|
|
24
|
+
*/
|
|
25
|
+
cancelled?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Superset of the old 3-value `status`: 'drafting' and 'awaiting_approval' are new (plan mode's
|
|
29
|
+
* P1/P2 — not wired to any producer yet, this phase is schema-only), 'active'/'done'/'abandoned'
|
|
30
|
+
* are the pre-existing values, unchanged in meaning.
|
|
31
|
+
*/
|
|
32
|
+
export type PlanMode = 'drafting' | 'awaiting_approval' | 'active' | 'done' | 'abandoned';
|
|
33
|
+
export interface PlanRecord {
|
|
34
|
+
/** null for a fully custom-drafted plan (P3) that isn't seeded from one of the named templates. */
|
|
35
|
+
templateName: string | null;
|
|
36
|
+
successCriteria: string;
|
|
37
|
+
/** Why this approach, not just the success criteria — see Plan.rationale. */
|
|
38
|
+
rationale: string;
|
|
39
|
+
tasks: PlanTaskRecord[];
|
|
40
|
+
mode: PlanMode;
|
|
41
|
+
/** Populated by plan mode's P6 self-verification pass. */
|
|
42
|
+
reviewNotes?: string[];
|
|
43
|
+
verifiedAt?: string;
|
|
44
|
+
/** Whether the harness should auto-advance through this plan's tasks without a per-step pacing pause (plan mode's P4). */
|
|
45
|
+
executingOnPlan: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Plan mode's P10 "trust this approved plan" opt-in — absent/false (the default, "absent when
|
|
48
|
+
* unused" per this codebase's convention for additive fields) means every write/shell/email
|
|
49
|
+
* action this plan's execution proposes stays individually gated exactly as before this phase.
|
|
50
|
+
* `true` only once the user chose `'approve_trusted'` at the P2 approval screen (never the
|
|
51
|
+
* default choice there — see PlanApprovalService) — while true, an action proposed during
|
|
52
|
+
* execution of whichever task is this plan's currently RUNNING one is auto-applied instead of
|
|
53
|
+
* staged (INV-36; see assistant.ts's buildToolLoopPauseResult). Per-plan, not global or
|
|
54
|
+
* per-session: a fresh plan, or this same plan reloaded after `abandonPlan`, starts back at
|
|
55
|
+
* false.
|
|
56
|
+
*/
|
|
57
|
+
trustApprovedSteps?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Set only while `mode === 'awaiting_approval'` (plan mode's P2) — correlates a
|
|
60
|
+
* `turn(message, { planApprovalId, planDecision })` resolution to exactly this staged snapshot,
|
|
61
|
+
* same "resolved by ID, never re-derived" discipline pendingActionId/pendingClarificationId
|
|
62
|
+
* already follow. Cleared (undefined) once the plan is activated or the draft is abandoned.
|
|
63
|
+
*/
|
|
64
|
+
planApprovalId?: string;
|
|
65
|
+
createdAt: string;
|
|
66
|
+
updatedAt: string;
|
|
67
|
+
}
|
|
68
|
+
/** Pre-P0 persisted shape: `status` instead of `mode`, no `rationale`/`executingOnPlan`. */
|
|
69
|
+
interface LegacyPlanRecordShape {
|
|
70
|
+
templateName: string;
|
|
71
|
+
successCriteria: string;
|
|
72
|
+
tasks: PlanTaskRecord[];
|
|
73
|
+
status: 'active' | 'done' | 'abandoned';
|
|
74
|
+
createdAt: string;
|
|
75
|
+
updatedAt: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Maps a persisted record — old 3-value `status` shape or the current 5-value `mode` shape — to
|
|
79
|
+
* the current PlanRecord shape, same "default pre-existing records without demoting a real value"
|
|
80
|
+
* convention fact-extraction.ts's migrateFact already uses elsewhere in this package. A migrated
|
|
81
|
+
* `active` plan gets `executingOnPlan: true` so an in-flight plan from before this change keeps
|
|
82
|
+
* progressing rather than silently stalling.
|
|
83
|
+
*/
|
|
84
|
+
export declare function migratePlanRecord(record: PlanRecord | LegacyPlanRecordShape): PlanRecord;
|
|
85
|
+
/**
|
|
86
|
+
* Plan mode's P5 file-backed persistence: when a real filesystem is available (CLI/desktop —
|
|
87
|
+
* the same `{backend, workspaceRoot}` pair AssistantSession.undoWorkspace() already resolves for
|
|
88
|
+
* write_file/run_shell_command), every plan write is mirrored to
|
|
89
|
+
* `<workspaceRoot>/.buildaharness/plans/<sessionId>.plan.json` (plus a generated `.plan.md`
|
|
90
|
+
* view), and every load prefers that file over the Dexie/IndexedDB State-tier record so a
|
|
91
|
+
* hand-edit to the JSON between turns is picked up. On a surface with no filesystem (a browser
|
|
92
|
+
* tab), this is `undefined` throughout and Dexie alone remains the source of truth, unchanged
|
|
93
|
+
* from before P5.
|
|
94
|
+
*/
|
|
95
|
+
export interface PlanFsPersistence {
|
|
96
|
+
backend: FsBackend;
|
|
97
|
+
workspaceRoot: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Reads `sessionId`'s stored plan record regardless of `mode` — unlike `loadActivePlan`, which
|
|
101
|
+
* filters to `mode === 'active'` only. Plan mode's P1 drafting path needs to resume a
|
|
102
|
+
* `mode: 'drafting'` record across turns (and P2 needs `awaiting_approval` the same way), neither
|
|
103
|
+
* of which `loadActivePlan` would ever return.
|
|
104
|
+
*
|
|
105
|
+
* When `fsPersistence` is configured and its plan JSON file exists, that file wins over the
|
|
106
|
+
* Dexie/State-tier record (P5 — the file is the editable-and-reloadable artifact, so a hand-edit
|
|
107
|
+
* between turns takes effect on the very next load) and is written back into `memory` to
|
|
108
|
+
* reconcile the two (INV-33: after any interrupted dual write, the next load's result is
|
|
109
|
+
* self-consistent and Dexie catches back up to it, rather than the two staying silently split).
|
|
110
|
+
*/
|
|
111
|
+
export declare function loadPlanRecord(memory: MemoryAdapter, sessionId: string, fsPersistence?: PlanFsPersistence): Promise<PlanRecord | null>;
|
|
112
|
+
/** Returns null when no plan exists for this session, or the stored plan is already done/abandoned — a finished plan never auto-resumes. */
|
|
113
|
+
export declare function loadActivePlan(memory: MemoryAdapter, sessionId: string, fsPersistence?: PlanFsPersistence): Promise<PlanRecord | null>;
|
|
114
|
+
/**
|
|
115
|
+
* Builds an immediately-`active` PlanRecord with no drafting/approval step at all. Before P3 of
|
|
116
|
+
* plans/ask_question_and_plan_mode_plan.html, TurnInterpreter.resolveTasks called this the instant
|
|
117
|
+
* classifyTurnIntent matched a template; P3 retired that call site — a template match (or the
|
|
118
|
+
* newer general needsMultiStepPlan judgment) now always routes through plan mode's exclusive
|
|
119
|
+
* drafting+approval loop instead (see assistant.ts's auto-trigger and
|
|
120
|
+
* PlanDraftingService.draftTurn's template-seeding). This function itself is unchanged and still
|
|
121
|
+
* used directly by tests/callers that want to seed an already-active plan without going through
|
|
122
|
+
* drafting. The ONLY other place `mode: 'active'` is ever set is `activatePlanRecord` below,
|
|
123
|
+
* reached solely via a drafted plan's explicit approve/approve-with-edits (INV-31) — this function
|
|
124
|
+
* remains the one intentional exception to "exactly one place", now purely a low-level primitive
|
|
125
|
+
* rather than something classification wires up automatically.
|
|
126
|
+
*/
|
|
127
|
+
export declare function createPlanRecord(plan: Plan): PlanRecord;
|
|
128
|
+
/**
|
|
129
|
+
* The funnel every plan mutation in this module writes through. When `fsPersistence` is
|
|
130
|
+
* configured, the fs file is written first and Dexie second (P5, INV-33): if the process dies
|
|
131
|
+
* between the two, the fs file — which `loadPlanRecord` always prefers when present — already
|
|
132
|
+
* reflects either the old state (rename never happened) or the new one (rename completed),
|
|
133
|
+
* never a half-written file, so the next load resolves to one consistent version of both instead
|
|
134
|
+
* of a silently split pair.
|
|
135
|
+
*/
|
|
136
|
+
export declare function savePlan(memory: MemoryAdapter, sessionId: string, plan: PlanRecord, fsPersistence?: PlanFsPersistence): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* A fresh, empty `mode: 'drafting'` record — the seed for plan mode's P1 drafting loop, started
|
|
139
|
+
* either from nothing (a from-scratch draft, P3) or already carrying a `templateName` seed.
|
|
140
|
+
* Unlike `createPlanRecord`, `executingOnPlan` starts false (P4's auto-advance only ever applies
|
|
141
|
+
* once P2 approval flips the record to `active`) and `mode` starts `'drafting'`, not `'active'`.
|
|
142
|
+
*/
|
|
143
|
+
export declare function createDraftPlanRecord(templateName: string | null): PlanRecord;
|
|
144
|
+
export declare function abandonPlan(memory: MemoryAdapter, sessionId: string, plan: PlanRecord, fsPersistence?: PlanFsPersistence): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Stages a drafted plan for plan mode's P2 mandatory whole-plan approval gate: `mode` moves to
|
|
147
|
+
* `'awaiting_approval'` and a fresh `planApprovalId` is minted so the eventual
|
|
148
|
+
* `turn(message, { planApprovalId, planDecision })` resolves exactly this snapshot, never a
|
|
149
|
+
* second, re-derived one (see PlanRecord.planApprovalId's doc comment).
|
|
150
|
+
*/
|
|
151
|
+
export declare function stagePlanForApproval(memory: MemoryAdapter, sessionId: string, plan: PlanRecord, fsPersistence?: PlanFsPersistence): Promise<PlanRecord>;
|
|
152
|
+
/**
|
|
153
|
+
* The sole place a *drafted* plan reaches `mode: 'active'` (INV-31) — called only from
|
|
154
|
+
* PlanApprovalService's approve/approve-with-edits branch, once any edits have already landed on
|
|
155
|
+
* `plan`. `createPlanRecord` above sets `mode: 'active'` too, but that's the pre-existing,
|
|
156
|
+
* unrelated template-instant-match path — not a second way for a *drafted* plan to skip approval.
|
|
157
|
+
* `planApprovalId` is cleared since the staged snapshot it correlated to no longer exists once
|
|
158
|
+
* activated.
|
|
159
|
+
*/
|
|
160
|
+
export declare function activatePlanRecord(memory: MemoryAdapter, sessionId: string, plan: PlanRecord, fsPersistence?: PlanFsPersistence): Promise<PlanRecord>;
|
|
161
|
+
export interface TaskCancelMatch {
|
|
162
|
+
taskId: string;
|
|
163
|
+
taskDescription: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Detects a request to cancel/skip ONE task within an active plan — distinct from
|
|
167
|
+
* classifyTurnIntent's isAbandonRequest judgment, which is about ending the WHOLE plan (see
|
|
168
|
+
* conv59/conv70's h9 finding: "cancel the daily-budget task" isn't asking to abandon a trip-planning
|
|
169
|
+
* plan entirely, just to drop one of its steps, and there was no feature to route that to at
|
|
170
|
+
* all). Matches a cancel-shaped verb (cancel/skip/drop/remove), an explicit reference to the plan
|
|
171
|
+
* or one of its tasks/steps (TASK_REFERENCE_MARKER), together with a distinctive word (4+ letters,
|
|
172
|
+
* not a common stopword) shared with one of the plan's own not-yet-complete task
|
|
173
|
+
* descriptions/ids — deliberately conservative: a bare "cancel" with no recognizable task
|
|
174
|
+
* reference (e.g. "cancel my gym membership", unrelated to anything in this plan, or "cancel my
|
|
175
|
+
* travel insurance policy with my current provider", a genuine external request that merely
|
|
176
|
+
* happens to share a word with a task description) returns null and falls through to the ordinary
|
|
177
|
+
* message-level risk gate, same as today. Returns the first matching task in plan order, or null.
|
|
178
|
+
*/
|
|
179
|
+
export declare function matchTaskCancelAttempt(message: string, plan: PlanRecord): TaskCancelMatch | null;
|
|
180
|
+
/**
|
|
181
|
+
* Cancels one task within an active plan — internal bookkeeping only (see PlanTaskRecord.cancelled
|
|
182
|
+
* for why status becomes 'COMPLETE' alongside the cancelled flag). Unlike abandonPlan, the plan
|
|
183
|
+
* itself stays 'active' so the remaining tasks continue normally.
|
|
184
|
+
*/
|
|
185
|
+
export declare function cancelPlanTask(memory: MemoryAdapter, sessionId: string, plan: PlanRecord, taskId: string, fsPersistence?: PlanFsPersistence): Promise<PlanRecord>;
|
|
186
|
+
/**
|
|
187
|
+
* Changes one task's description without marking it complete — a lighter sibling of
|
|
188
|
+
* cancelPlanTask, for plan mode's P2 approve-with-edits path (a user can want a task reworded
|
|
189
|
+
* without dropping it entirely, which cancelPlanTask's "mark COMPLETE + cancelled" would do).
|
|
190
|
+
*/
|
|
191
|
+
export declare function editPlanTask(memory: MemoryAdapter, sessionId: string, plan: PlanRecord, taskId: string, newDescription: string, fsPersistence?: PlanFsPersistence): Promise<PlanRecord>;
|
|
192
|
+
/**
|
|
193
|
+
* Maps the harness's resulting task statuses back onto the plan's own task list —
|
|
194
|
+
* mirrors what adapter/harness/plan_store.py's task_graph_to_plan does for the
|
|
195
|
+
* Python planner, just keyed to a chat session instead of a snapshot file. Marks
|
|
196
|
+
* the plan 'done' once every task is COMPLETE, so loadActivePlan stops resuming it.
|
|
197
|
+
* Also flips `executingOnPlan` back to false in that same allComplete branch (plan
|
|
198
|
+
* mode's P4, INV-32) — one of exactly two places that happens, the other being
|
|
199
|
+
* `abandonPlan`'s explicit user-abort path; every other event (a task failing, a
|
|
200
|
+
* needs_approval/needs_clarification interrupt, a harness error) leaves it untouched.
|
|
201
|
+
*/
|
|
202
|
+
export declare function updatePlanFromRun(plan: PlanRecord, taskGraphTasks: {
|
|
203
|
+
id: string;
|
|
204
|
+
status: TaskStatus;
|
|
205
|
+
}[]): PlanRecord;
|
|
206
|
+
/** Cancelled tasks are excluded from both sides of the ratio — they're neither remaining work nor
|
|
207
|
+
* something the user actually did, so counting them as "complete" would overstate real progress.
|
|
208
|
+
* A plan with no tasks at all is 0% (unchanged); a plan whose remaining tasks were all cancelled
|
|
209
|
+
* is 100% (nothing left to do) — two different situations, not the same "empty" case. */
|
|
210
|
+
export declare function planCompletionPct(plan: PlanRecord): number;
|
|
211
|
+
/**
|
|
212
|
+
* Live, mid-run position within a durable plan — see the harness layer activation plan's
|
|
213
|
+
* Phase 3.2. `stepIndex` is 1-based: the task currently RUNNING, or (once nothing is running)
|
|
214
|
+
* the last COMPLETE task, or the first task before anything has started.
|
|
215
|
+
*/
|
|
216
|
+
export interface PlanPosition {
|
|
217
|
+
templateName: string | null;
|
|
218
|
+
stepIndex: number;
|
|
219
|
+
stepCount: number;
|
|
220
|
+
currentTaskDescription: string;
|
|
221
|
+
completionPct: number;
|
|
222
|
+
}
|
|
223
|
+
/** Computes live plan position from a live (possibly mid-run) task-status list — `plan.tasks`' own order is authoritative; `taskGraphTasks` only supplies current status per id. */
|
|
224
|
+
export declare function computePlanPosition(plan: PlanRecord, taskGraphTasks: {
|
|
225
|
+
id: string;
|
|
226
|
+
status: TaskStatus;
|
|
227
|
+
}[]): PlanPosition | null;
|
|
228
|
+
/** The next not-yet-COMPLETE task in plan order — used to phrase a pause/resume prompt ("Ready to continue with: <description>?"). Returns null once every task is COMPLETE. */
|
|
229
|
+
export declare function nextPendingTask(plan: PlanRecord): PlanTaskRecord | null;
|
|
230
|
+
/** Human-readable plan status — mirrors agents/planner/utils.py's format_plan_progress, for callers (the CLI) that want text instead of the structured planStatus field. */
|
|
231
|
+
export declare function formatPlanProgress(plan: PlanRecord): string;
|
|
232
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
"name": "content_creation",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"success_criteria": "A polished, peer-reviewed piece of content is finalised and ready for publication or delivery.",
|
|
5
|
+
"tags": ["content", "writing", "communication"],
|
|
6
|
+
"tasks": [
|
|
7
|
+
{
|
|
8
|
+
"id": "brief",
|
|
9
|
+
"title": "Define brief",
|
|
10
|
+
"description": "Clarify the content goal, target audience, format, tone, length, and success metrics.",
|
|
11
|
+
"depends_on": [],
|
|
12
|
+
"risk_level": "LOW",
|
|
13
|
+
"abstraction_level": 0,
|
|
14
|
+
"parallel_write_domains": []
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "outline",
|
|
18
|
+
"title": "Create outline",
|
|
19
|
+
"description": "Structure the content: define sections, key messages per section, and the logical flow.",
|
|
20
|
+
"depends_on": ["brief"],
|
|
21
|
+
"risk_level": "LOW",
|
|
22
|
+
"abstraction_level": 1,
|
|
23
|
+
"parallel_write_domains": []
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "draft",
|
|
27
|
+
"title": "Write draft",
|
|
28
|
+
"description": "Produce the first full draft following the outline. Focus on completeness over perfection.",
|
|
29
|
+
"depends_on": ["outline"],
|
|
30
|
+
"risk_level": "MEDIUM",
|
|
31
|
+
"abstraction_level": 1,
|
|
32
|
+
"parallel_write_domains": ["draft"]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "gather_references",
|
|
36
|
+
"title": "Gather references",
|
|
37
|
+
"description": "Collect supporting sources, data, quotes, and visuals that will strengthen the content.",
|
|
38
|
+
"depends_on": ["outline"],
|
|
39
|
+
"risk_level": "LOW",
|
|
40
|
+
"abstraction_level": 1,
|
|
41
|
+
"parallel_write_domains": ["references"]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "peer_review",
|
|
45
|
+
"title": "Peer review",
|
|
46
|
+
"description": "Have at least one reviewer assess the draft for accuracy, clarity, tone, and completeness.",
|
|
47
|
+
"depends_on": ["draft", "gather_references"],
|
|
48
|
+
"risk_level": "LOW",
|
|
49
|
+
"abstraction_level": 0,
|
|
50
|
+
"parallel_write_domains": []
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "revise",
|
|
54
|
+
"title": "Revise",
|
|
55
|
+
"description": "Incorporate review feedback. Address all critical comments before finalisation.",
|
|
56
|
+
"depends_on": ["peer_review"],
|
|
57
|
+
"risk_level": "LOW",
|
|
58
|
+
"abstraction_level": 1,
|
|
59
|
+
"parallel_write_domains": []
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "finalise",
|
|
63
|
+
"title": "Finalise",
|
|
64
|
+
"description": "Apply final formatting, proofread, and approve for publication or delivery.",
|
|
65
|
+
"depends_on": ["revise"],
|
|
66
|
+
"risk_level": "LOW",
|
|
67
|
+
"abstraction_level": 0,
|
|
68
|
+
"parallel_write_domains": []
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"metadata": { "author": "buildaharness", "created": "2026-06-22" }
|
|
72
|
+
}
|
|
73
|
+
;
|
|
74
|
+
|
|
75
|
+
export default _default;
|