@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,51 @@
|
|
|
1
|
+
import { Evidence, VerificationResult } from '@buildaharness/harness';
|
|
2
|
+
/**
|
|
3
|
+
* Where the evidence backing a reply actually came from — 'tool_evidence' when at least one
|
|
4
|
+
* real observation (a gather_evidence call, a real read_file/web_search result, ...) was
|
|
5
|
+
* recorded this turn, 'model_reasoning' when the harness ran but produced no such observation
|
|
6
|
+
* (a plain-reasoning reply, still gated through the full loop).
|
|
7
|
+
*/
|
|
8
|
+
export type AnswerClaimSourceType = 'tool_evidence' | 'model_reasoning';
|
|
9
|
+
/**
|
|
10
|
+
* The four branches this plan's Validation section names explicitly — each backed by a real,
|
|
11
|
+
* mechanically-derived signal (see buildAnswerClaim), never guessed from the reply text itself:
|
|
12
|
+
* - 'verified': the verification layer found no critical failure and at least one layer
|
|
13
|
+
* actually PASSed (not just SKIPPED) against real evidence.
|
|
14
|
+
* - 'unverified_attempted': evidence was gathered, but no layer could independently confirm it
|
|
15
|
+
* (all SKIPPED/no tool to check with) — an honest "couldn't verify," not a fake PASS.
|
|
16
|
+
* - 'contradicted': the Contradiction layer flagged a conflict with an existing belief this turn.
|
|
17
|
+
* - 'no_evidence': no real observation was gathered at all — a plain-reasoning answer.
|
|
18
|
+
*/
|
|
19
|
+
export type AnswerClaimVerificationStatus = 'verified' | 'unverified_attempted' | 'contradicted' | 'no_evidence';
|
|
20
|
+
/**
|
|
21
|
+
* Phase 6 of the harness/assistant remediation plan: a first-class epistemic-honesty signal
|
|
22
|
+
* attached to a reply that went through the harness loop, distinguishing "this is true" from
|
|
23
|
+
* "I found X but couldn't independently verify it." Built entirely from real, already-computed
|
|
24
|
+
* harness signal (Phase 2/3's now-real verification layers, the EvidenceStore's real
|
|
25
|
+
* observations, the Contradiction layer's own finding) — never a new LLM judgment call.
|
|
26
|
+
*/
|
|
27
|
+
export interface AnswerClaim {
|
|
28
|
+
evidence: Evidence[];
|
|
29
|
+
/** min(verificationHealth.strength, verificationHealth.feasibility) — the same real number chat-ui's "Why?" panel already buckets into plain language. */
|
|
30
|
+
confidence: number;
|
|
31
|
+
/** ISO timestamp of the least-fresh evidence backing this claim, or null when there's no evidence at all. */
|
|
32
|
+
freshness: string | null;
|
|
33
|
+
source_type: AnswerClaimSourceType;
|
|
34
|
+
verification_status: AnswerClaimVerificationStatus;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Builds an AnswerClaim from one turn's real harness output. `verification` is the last
|
|
38
|
+
* VerificationResult HarnessRuntime's onVerification hook reported this turn (null when the
|
|
39
|
+
* turn never reached verify() at all — e.g. the harness was skipped by the triviality fast
|
|
40
|
+
* path, in which case the caller should not attach an AnswerClaim at all rather than call this
|
|
41
|
+
* with a fabricated null).
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildAnswerClaim(input: {
|
|
44
|
+
evidence: Evidence[];
|
|
45
|
+
verification: VerificationResult | null;
|
|
46
|
+
contradicted: boolean;
|
|
47
|
+
verificationHealth: {
|
|
48
|
+
strength: number;
|
|
49
|
+
feasibility: number;
|
|
50
|
+
};
|
|
51
|
+
}): AnswerClaim;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { AskQuestion, AskResponse } from '@buildaharness/harness';
|
|
2
|
+
import { MemoryAdapter } from '@buildaharness/runtime';
|
|
3
|
+
import { AssistantSession } from './assistant-session.js';
|
|
4
|
+
import { HarnessBridge } from './harness-bridge.js';
|
|
5
|
+
import { ResponseService } from './response-service.js';
|
|
6
|
+
import { PlanRecord } from './plan-store.js';
|
|
7
|
+
import { TurnIntentClassification } from './turn-intent-classifier.js';
|
|
8
|
+
import { UserFact } from './fact-extraction.js';
|
|
9
|
+
import { AssistantTurnResult } from './assistant-types.js';
|
|
10
|
+
import { TraceEvent } from './trace-events.js';
|
|
11
|
+
/**
|
|
12
|
+
* Everything AskClarificationService.resolvePendingClarification needs to re-drive the harness
|
|
13
|
+
* run once the user answers — persisted alongside the staged questions themselves (mirrors
|
|
14
|
+
* AgentLoop's BatchPendingState, staged the same `memory.set('ask-pending:${id}', ...)` way, see
|
|
15
|
+
* ActionApprovalService's own "resolved by ID, never re-derived from a second LLM call"
|
|
16
|
+
* discipline this mirrors, T4 of the file-tools plan).
|
|
17
|
+
*/
|
|
18
|
+
export interface AskClarificationPendingState {
|
|
19
|
+
sessionId: string;
|
|
20
|
+
questions: AskQuestion[];
|
|
21
|
+
classification: TurnIntentClassification;
|
|
22
|
+
activePlan: PlanRecord | null;
|
|
23
|
+
facts: UserFact[];
|
|
24
|
+
draftReply: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Owns the ask-question "staged clarification, resolved by ID" pattern — Q2 of
|
|
28
|
+
* plans/ask_question_and_plan_mode_plan.html, structurally mirroring ActionApprovalService (T4 of
|
|
29
|
+
* the file-tools plan: what gets resolved is exactly what was staged, never re-derived from a
|
|
30
|
+
* second LLM call). Unlike a write/shell action, resolving a clarification means resuming a
|
|
31
|
+
* *harness run* — the checkpoint HarnessBridge would otherwise have deleted on any thrown
|
|
32
|
+
* EscalationHalt (see HarnessBridge.run()'s askModeEnabled/preserveForClarification handling) is
|
|
33
|
+
* kept alive specifically so this service can hand it back to HarnessBridge with the user's
|
|
34
|
+
* answer folded in via a one-shot UpdateChannel, instead of losing the run's progress and
|
|
35
|
+
* starting over.
|
|
36
|
+
*/
|
|
37
|
+
export declare class AskClarificationService {
|
|
38
|
+
private readonly memory;
|
|
39
|
+
private readonly session;
|
|
40
|
+
private readonly harnessBridge;
|
|
41
|
+
private readonly responseService;
|
|
42
|
+
private readonly onTrace;
|
|
43
|
+
constructor(memory: MemoryAdapter, session: AssistantSession, harnessBridge: HarnessBridge, responseService: ResponseService, onTrace: ((event: TraceEvent) => void) | undefined);
|
|
44
|
+
private pendingKey;
|
|
45
|
+
/**
|
|
46
|
+
* Stages a batch of questions and returns the `needs_clarification` AssistantTurnResult — the
|
|
47
|
+
* ResponseService.buildEscalatedResult counterpart for a structured-question escalation
|
|
48
|
+
* (called instead of it, from assistant.ts's EscalationHalt catch, when
|
|
49
|
+
* `err.blocker.questions` is populated and the effective askMode is enabled).
|
|
50
|
+
*/
|
|
51
|
+
stageAndRespond(params: {
|
|
52
|
+
sessionId: string;
|
|
53
|
+
transcriptKey: string;
|
|
54
|
+
userMessage: string;
|
|
55
|
+
questions: AskQuestion[];
|
|
56
|
+
classification: TurnIntentClassification;
|
|
57
|
+
activePlan: PlanRecord | null;
|
|
58
|
+
facts: UserFact[];
|
|
59
|
+
draftReply: string;
|
|
60
|
+
}): Promise<AssistantTurnResult>;
|
|
61
|
+
/**
|
|
62
|
+
* Resolves a staged clarification by ID — validates the response against exactly the
|
|
63
|
+
* questions that were staged (INV-28: a malformed/incomplete payload is rejected, never
|
|
64
|
+
* silently coerced), folds the answer into the paused harness run via a one-shot
|
|
65
|
+
* UpdateChannel, and resumes it. A follow-up escalation (INV-37's deferred batch two) routes
|
|
66
|
+
* back through `stageAndRespond` exactly like the first, via the same catch this resume goes
|
|
67
|
+
* through.
|
|
68
|
+
*/
|
|
69
|
+
resolvePendingClarification(sessionId: string, transcriptKey: string, pendingClarificationId: string, response: AskResponse | undefined, askModeEnabled: boolean): Promise<AssistantTurnResult>;
|
|
70
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure logic backing PersonalAssistant's ASSISTANT_ASK_MODE handling — split out so it's
|
|
3
|
+
* unit-testable in isolation, mirroring one-loop-flag.ts's exact shape (module-level default,
|
|
4
|
+
* an injectable resolver so tests never touch real process.env).
|
|
5
|
+
*
|
|
6
|
+
* Q2 of plans/ask_question_and_plan_mode_plan.html: the "global flag" control point (tier 1 of
|
|
7
|
+
* Q1's three-tier INV-29 resolution) for the batched-questions ask-question mechanism, resolved
|
|
8
|
+
* at this package's boundary and handed to `@buildaharness/harness`'s `resolveAskMode()` as
|
|
9
|
+
* `globalEnabled`. 'disabled' (the default, for the whole rollout window, per Q1's
|
|
10
|
+
* `DEFAULT_ASK_MODE`) means every escalation stays on today's plain `escalated`/`reply: null`
|
|
11
|
+
* path — AskClarificationService is never reached. 'enabled' lets a populated
|
|
12
|
+
* `EscalationHalt.blocker.questions` promote to `needs_clarification` instead.
|
|
13
|
+
*/
|
|
14
|
+
export type AskMode = 'enabled' | 'disabled';
|
|
15
|
+
export declare const DEFAULT_ASK_MODE: AskMode;
|
|
16
|
+
/**
|
|
17
|
+
* Value-level resolver, shared by every surface: `resolveAskMode` (CLI, reads
|
|
18
|
+
* `process.env.ASSISTANT_ASK_MODE`) and chat-ui's `envOverridesFromImportMetaEnv` (browser
|
|
19
|
+
* build, reads Vite's `import.meta.env.VITE_ASSISTANT_ASK_MODE`). An unset or empty value falls
|
|
20
|
+
* back to the default silently; an unrecognized non-empty value falls back with a startup
|
|
21
|
+
* warning naming `varName` — same typo-tolerance-with-a-warning convention
|
|
22
|
+
* `normalizeOneLoopMode` uses, since a silently-misread flag here is safety-relevant.
|
|
23
|
+
*/
|
|
24
|
+
export declare function normalizeAskMode(raw: string | undefined, varName?: string): AskMode;
|
|
25
|
+
export declare function resolveAskMode(env: NodeJS.ProcessEnv): AskMode;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AskQuestion, AskResponse } from '@buildaharness/harness';
|
|
2
|
+
/**
|
|
3
|
+
* Renders a full AskResponse as one transcript-ready message — shared by
|
|
4
|
+
* AskClarificationService (Q2, resuming a paused harness run) and PlanDraftingService's nested-ask
|
|
5
|
+
* resolution (P8, continuing a plan draft), both of which fold the user's answer into a plain-text
|
|
6
|
+
* follow-up message for their own "next call" rather than duplicating this formatting logic.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatAskResponse(questions: AskQuestion[], response: AskResponse): string;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { CheckpointStore, LayerActivityEvent, ExternalContradictionInput } from '@buildaharness/harness';
|
|
2
|
+
import { MemoryAdapter, ChatMessage, TokenUsage, FsBackend } from '@buildaharness/runtime';
|
|
3
|
+
import { FileToolsContext } from './file-tools.js';
|
|
4
|
+
import { UndoLogEntry } from './action-snapshot.js';
|
|
5
|
+
import { ShellToolsContext } from './shell-tools.js';
|
|
6
|
+
import { ActionToolsContext } from './action-tools.js';
|
|
7
|
+
import { SpendCapConfig, SpendState } from './spend-cap.js';
|
|
8
|
+
/**
|
|
9
|
+
* P1 of plans/ask_question_and_plan_mode_plan.html — session/turn-routing switch, distinct from a
|
|
10
|
+
* tool-permission list. While `active`, `assistant.ts`'s turn entry point routes every incoming
|
|
11
|
+
* message for this session to `PlanDraftingService` instead of `TurnInterpreter`'s normal
|
|
12
|
+
* classify/tool-loop pipeline (see PlanDraftingService's own doc comment for the two designed
|
|
13
|
+
* exits). `draftId` is an opaque per-entry token (not a lookup key into anything today — the
|
|
14
|
+
* drafting `PlanRecord` itself still lives at plan-store.ts's single `plan:${sessionId}` slot,
|
|
15
|
+
* same as an ordinary plan) reserved for P2's approval flow to correlate "the draft that was
|
|
16
|
+
* staged" against "the draft the user is approving", the same "resolved by exactly what was
|
|
17
|
+
* staged, never re-derived" discipline ActionApprovalService/AskClarificationService already
|
|
18
|
+
* follow.
|
|
19
|
+
*/
|
|
20
|
+
export interface PlanModeState {
|
|
21
|
+
active: boolean;
|
|
22
|
+
draftId: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const RESUME_ATTEMPT_CAP = 2;
|
|
25
|
+
export declare const resumeAttemptsKey: (sessionId: string) => string;
|
|
26
|
+
/**
|
|
27
|
+
* One searchable, individually-addressable transcript entry — key `transcript-msg:<sessionId>:<n>`
|
|
28
|
+
* (see messageIndexKey). Written alongside every `transcript:<sessionId>` append by
|
|
29
|
+
* appendTranscriptMessage so scoreEntries() (via MemoryAdapter.search()) can resolve a hit to this
|
|
30
|
+
* one exchange instead of the whole session array. Derived, not authoritative: the per-session
|
|
31
|
+
* `transcript:<sessionId>` array remains the one source of truth for conversation replay,
|
|
32
|
+
* compaction, and /export — losing an index entry (a failed write, or a pre-backfill install) only
|
|
33
|
+
* ever degrades search recall, never conversation behavior.
|
|
34
|
+
*/
|
|
35
|
+
export interface IndexedMessage {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
role: 'user' | 'assistant';
|
|
38
|
+
content: string;
|
|
39
|
+
at: string;
|
|
40
|
+
}
|
|
41
|
+
/** One ranked `/search` result — an `IndexedMessage` plus the graduated relevance score `scoreEntries()` gave it. */
|
|
42
|
+
export interface TranscriptSearchHit {
|
|
43
|
+
sessionId: string;
|
|
44
|
+
role: 'user' | 'assistant';
|
|
45
|
+
content: string;
|
|
46
|
+
at: string;
|
|
47
|
+
score: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Owns session/transcript/checkpoint/spend/undo bookkeeping — the "everyday persistence" half of
|
|
51
|
+
* PersonalAssistant, split out in Phase 4d of the architecture remediation plan. `fileTools`/
|
|
52
|
+
* `shellTools` are the same shared option objects AgentLoop also holds (needed here for the
|
|
53
|
+
* shell-result-cache clear in clearSession, and the workspace lookup in undo/sweep) — not a
|
|
54
|
+
* duplication, just two collaborators independently needing the same read-only context.
|
|
55
|
+
*/
|
|
56
|
+
export declare class AssistantSession {
|
|
57
|
+
private readonly memory;
|
|
58
|
+
private readonly checkpointStore;
|
|
59
|
+
private readonly spendCap;
|
|
60
|
+
private readonly model;
|
|
61
|
+
private readonly fileTools;
|
|
62
|
+
private readonly shellTools;
|
|
63
|
+
private readonly actionTools;
|
|
64
|
+
private readonly notifiedContradictions;
|
|
65
|
+
constructor(memory: MemoryAdapter, checkpointStore: CheckpointStore, spendCap: SpendCapConfig | undefined, model: () => string | undefined, fileTools: FileToolsContext | undefined, shellTools: ShellToolsContext | undefined, actionTools: ActionToolsContext | undefined);
|
|
66
|
+
private static notifiedContradictionsKey;
|
|
67
|
+
getNotifiedContradictions(sessionId: string): Promise<Set<string>>;
|
|
68
|
+
recordNotifiedContradiction(sessionId: string, seen: Set<string>, value: string): Promise<void>;
|
|
69
|
+
/** findContradictionNotice's own text, deduped once per session — see notifiedContradictions'
|
|
70
|
+
* doc comment and findContradictionNotice's for why this exists: without it, the always-on
|
|
71
|
+
* lexical Contradiction layer re-fires the identical notice on every subsequent non-trivial
|
|
72
|
+
* turn, since the WorldModel it runs against is rebuilt fresh (re-seeded from all known facts)
|
|
73
|
+
* each turn with no memory of its own that this exact conflict was already surfaced. */
|
|
74
|
+
dedupedContradictionNotice(sessionId: string, layerActivity: LayerActivityEvent[], extraContradictions?: ExternalContradictionInput[]): Promise<string | undefined>;
|
|
75
|
+
/** Persisted alongside transcript/facts/plan (this.memory) — survives a process restart, same as everything else keyed by sessionId, so the ceiling is genuinely cross-session, not just cross-turn within one process lifetime. */
|
|
76
|
+
getSpendState(sessionId: string): Promise<SpendState>;
|
|
77
|
+
/**
|
|
78
|
+
* Pre-turn spend-cap check — a no-op (`{ allowed: true }`) whenever no cap is configured. Only
|
|
79
|
+
* ever checked before a turn starts, never mid-turn (see spend-cap.ts's checkSpendCap doc
|
|
80
|
+
* comment) — `turn()` skips this entirely for a pendingActionId continuation, since that's a
|
|
81
|
+
* resumption of a turn that already passed this check when it first started.
|
|
82
|
+
*/
|
|
83
|
+
checkSpendCapForTurn(sessionId: string): Promise<{
|
|
84
|
+
allowed: boolean;
|
|
85
|
+
reason?: string;
|
|
86
|
+
}>;
|
|
87
|
+
/**
|
|
88
|
+
* Called once per successfully completed ('ok') turn — counts turns, not raw internal LLM
|
|
89
|
+
* calls (see SpendCapConfig's doc comment for why). Estimates cost the same way cli.ts's
|
|
90
|
+
* withCostEstimate does for a backend that doesn't report a real costUsd, so the cap enforces
|
|
91
|
+
* against the same number /cost displays, not a second cost model.
|
|
92
|
+
*
|
|
93
|
+
* Always records, even when no cap is currently configured — the ledger must reflect true
|
|
94
|
+
* cumulative spend regardless of whether a cap happens to exist yet, or a session that chats
|
|
95
|
+
* for a while uncapped and only later runs `/config set sessionCostLimitUsd ...` would have
|
|
96
|
+
* every turn before that point silently excluded from the cumulative total checkSpendCap
|
|
97
|
+
* enforces against, understating real spend. The enforcement gate itself (checkSpendCapForTurn)
|
|
98
|
+
* already correctly no-ops whenever no cap is configured, so this never enforces a cap that
|
|
99
|
+
* isn't set.
|
|
100
|
+
*/
|
|
101
|
+
recordSpend(sessionId: string, usage: TokenUsage | undefined): Promise<void>;
|
|
102
|
+
/** Read-only — null means plan mode has never been entered (or was already exited/cancelled) for this session. */
|
|
103
|
+
getPlanModeState(sessionId: string): Promise<PlanModeState | null>;
|
|
104
|
+
/**
|
|
105
|
+
* The one place `planMode.active` ever flips true — a fresh `draftId` per entry, even if a
|
|
106
|
+
* prior drafting session was cancelled without ever reaching approval, so a stale draftId can
|
|
107
|
+
* never be mistaken for the current one. Idempotent to call again while already active (returns
|
|
108
|
+
* a new state, same as a fresh entry) — callers that only ever call this once per drafting
|
|
109
|
+
* session (P3's future auto-trigger) never observe that.
|
|
110
|
+
*/
|
|
111
|
+
enterPlanMode(sessionId: string): Promise<PlanModeState>;
|
|
112
|
+
/** Clears `planMode.active` — the only two designed callers are an explicit cancel phrase (PlanDraftingService) and P2's future approval resolution. */
|
|
113
|
+
exitPlanMode(sessionId: string): Promise<void>;
|
|
114
|
+
/** The session's conversation transcript, oldest first — same array `turn()` reads/appends to. Used by `/export`. */
|
|
115
|
+
getTranscript(sessionId: string): Promise<ChatMessage[]>;
|
|
116
|
+
/**
|
|
117
|
+
* Reads `sessionId`'s transcript and runs it through transcript-compaction.ts's compactTranscript,
|
|
118
|
+
* persisting the compacted array back when compaction actually collapsed anything — the first
|
|
119
|
+
* thing `runTurn()` did with the raw transcript before this split. Kept on AssistantSession
|
|
120
|
+
* (rather than the sequencer reading `memory` directly) so this remains the one place that reads
|
|
121
|
+
* `transcript:${sessionId}` for a live turn.
|
|
122
|
+
*/
|
|
123
|
+
loadAndCompactTranscript(sessionId: string): Promise<ChatMessage[]>;
|
|
124
|
+
/**
|
|
125
|
+
* Appends `message` to `transcriptKey`'s array — every site that used to call
|
|
126
|
+
* `this.memory.set(transcriptKey, message, 'append')` directly now goes through here instead, so
|
|
127
|
+
* a per-message search index entry (transcript-msg:<sessionId>:<n> — see messageIndexKey) is
|
|
128
|
+
* always written alongside it, with no call site able to forget. The index write is best-effort:
|
|
129
|
+
* caught and logged, never thrown — a search-indexing problem must never be able to break an
|
|
130
|
+
* ordinary turn or lose the transcript message itself.
|
|
131
|
+
*/
|
|
132
|
+
appendTranscriptMessage(sessionId: string, transcriptKey: string, message: {
|
|
133
|
+
role: 'user' | 'assistant';
|
|
134
|
+
content: string;
|
|
135
|
+
}): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* One-off, idempotent backfill for installs that already had transcript history before the
|
|
138
|
+
* message index existed: scans every `transcript:*` session currently in `this.memory` and
|
|
139
|
+
* indexes whichever messages don't already have a `transcript-msg:` entry, so pre-existing
|
|
140
|
+
* conversations become searchable too, not just messages sent after this shipped. Guarded by
|
|
141
|
+
* MESSAGE_INDEX_BACKFILL_VERSION_KEY so it only does real work once per install (and once more
|
|
142
|
+
* per future version bump); a second call is a cheap no-op. Only covers what's still present in
|
|
143
|
+
* the live (possibly already-compacted) transcript array — a session already compacted before
|
|
144
|
+
* backfill ran has already lost its older messages the same way /search would, a known,
|
|
145
|
+
* documented limitation rather than a bug (see README).
|
|
146
|
+
*
|
|
147
|
+
* Run fire-and-forget from PersonalAssistant's constructor, never awaited by a turn — a large
|
|
148
|
+
* pre-existing history must not delay the first prompt/render.
|
|
149
|
+
*/
|
|
150
|
+
backfillMessageIndex(): Promise<void>;
|
|
151
|
+
/**
|
|
152
|
+
* Prunes stale `.pending-actions/` records on startup. A leftover record is harmless (never
|
|
153
|
+
* applied without a matching id) but unbounded, so this keeps the directory from growing
|
|
154
|
+
* forever across crashed/abandoned turns.
|
|
155
|
+
*
|
|
156
|
+
* Safety-first, not per-record: `stagePendingAction`'s records carry no `sessionId` (the id is a
|
|
157
|
+
* random UUID, unrelated to any session — see file-tools.ts), so there is no direct way to tie
|
|
158
|
+
* one staged record to one session's checkpoint. Rather than sweep blind, this skips the sweep
|
|
159
|
+
* entirely for this startup if ANY known session still has a checkpoint eligible for resume —
|
|
160
|
+
* the coarser, but always-safe, version of "never sweep a record that could still be
|
|
161
|
+
* legitimately resumed". Known sessions are discovered the same way backfillMessageIndex
|
|
162
|
+
* already does (transcript: key prefixes), so no new bookkeeping is added.
|
|
163
|
+
*/
|
|
164
|
+
sweepAbandonedPendingActionsOnStartup(): Promise<void>;
|
|
165
|
+
/**
|
|
166
|
+
* Records a message-level risk-gate decline (the `needs_approval` branch with no
|
|
167
|
+
* `pendingActionId`) as a resolved, paired exchange, once the caller (cli.ts) knows the final
|
|
168
|
+
* answer was "no". Unlike the eager-append this deliberately avoids inside runTurn itself (the
|
|
169
|
+
* outcome isn't known yet at that point), this is safe: both the user message and a "declined"
|
|
170
|
+
* reply are appended together, atomically, only after the decline is already final — there is
|
|
171
|
+
* never a dangling, un-replied-to turn a later tool-enabled call could mistake for a live
|
|
172
|
+
* request.
|
|
173
|
+
*
|
|
174
|
+
* Without this, a message-level decline (unlike a tool-call-level one, which resolvePendingAction
|
|
175
|
+
* already persists) left zero trace at all: a later "did that unsubscribe actually happen?"
|
|
176
|
+
* question found nothing in the transcript and confidently denied the request was ever made,
|
|
177
|
+
* instead of correctly recalling that it was asked and declined.
|
|
178
|
+
*/
|
|
179
|
+
recordDeclinedRequest(sessionId: string, userMessage: string, reason: string): Promise<void>;
|
|
180
|
+
/**
|
|
181
|
+
* Ends the current conversation: deletes the transcript, extracted facts, any active plan for
|
|
182
|
+
* this session, and the shell-result cache (see file-tools.ts's shell-result-cache doc comment
|
|
183
|
+
* — a fresh conversation shouldn't silently answer from a previous, unrelated conversation's
|
|
184
|
+
* shell results), plus a leftover in-flight-turn checkpoint if one exists (from an abandoned
|
|
185
|
+
* turn that never reached its normal cleanup). Deliberately leaves
|
|
186
|
+
* `experienceStore`/`reminderStore`/DURABLE_FACTS_KEY untouched — those are durable,
|
|
187
|
+
* cross-conversation learning, not per-conversation scratch state (see the README's "Three
|
|
188
|
+
* things live outside a single harness run" section).
|
|
189
|
+
*/
|
|
190
|
+
clearSession(sessionId: string): Promise<void>;
|
|
191
|
+
/**
|
|
192
|
+
* Scoped recovery for a stuck harness checkpoint: clears just `turn:${sessionId}`'s checkpoint
|
|
193
|
+
* (and its resume-attempt count) without touching transcript/facts/plan — unlike clearSession
|
|
194
|
+
* (`/clear`/`/new`), which wipes the whole conversation. Returns `{ cleared: false }` when there
|
|
195
|
+
* was nothing to clear, so a caller can report "nothing stuck" instead of a false "cleared".
|
|
196
|
+
*/
|
|
197
|
+
clearCheckpoint(sessionId: string): Promise<{
|
|
198
|
+
cleared: boolean;
|
|
199
|
+
stepsUsed?: number;
|
|
200
|
+
currentNode?: string;
|
|
201
|
+
}>;
|
|
202
|
+
/**
|
|
203
|
+
* Read-only counterpart to clearCheckpoint — reports whether `sessionId` has a checkpoint left
|
|
204
|
+
* behind by a prior turn and how many times in a row it has already failed to resume, without
|
|
205
|
+
* clearing anything. Lets a caller (cli.ts's `/checkpoint`) inspect before deciding whether to
|
|
206
|
+
* clear.
|
|
207
|
+
*/
|
|
208
|
+
getCheckpointStatus(sessionId: string): Promise<{
|
|
209
|
+
present: boolean;
|
|
210
|
+
stepsUsed?: number;
|
|
211
|
+
currentNode?: string;
|
|
212
|
+
failedResumeAttempts: number;
|
|
213
|
+
}>;
|
|
214
|
+
/**
|
|
215
|
+
* Removes the most recent exchange from conversation history — a completed turn drops its
|
|
216
|
+
* user message and assistant reply (2 entries); a turn that ended in `needs_approval` before
|
|
217
|
+
* any reply was appended drops just the pending user message (1 entry). Only affects what the
|
|
218
|
+
* model remembers: a real `write_file`/`run_shell_command` effect from the undone turn is not
|
|
219
|
+
* reversed. Returns `{ undone: false }` on an empty transcript instead of throwing.
|
|
220
|
+
*/
|
|
221
|
+
undoLastTurn(sessionId: string): Promise<{
|
|
222
|
+
undone: boolean;
|
|
223
|
+
}>;
|
|
224
|
+
/** The workspace backend/root a staged write/shell/revert action lives under — `undefined` when neither fileTools nor shellTools is configured (e.g. a webTools-only assistant). Public: also used by ActionApprovalService for the same lookup. */
|
|
225
|
+
undoWorkspace(): {
|
|
226
|
+
backend: FsBackend;
|
|
227
|
+
workspaceRoot: string;
|
|
228
|
+
} | undefined;
|
|
229
|
+
/** Real filesystem effects still on record as revertible, newest first — bounded by action-snapshot.ts's UNDO_LOG_MAX_ENTRIES retention cap. Backs `/undo-action` with no argument. Distinct from `/undo` (undoLastTurn above), which only forgets conversation history — see README's /undo-action section for the naming distinction. */
|
|
230
|
+
listUndoLogEntries(): Promise<UndoLogEntry[]>;
|
|
231
|
+
/**
|
|
232
|
+
* Stages a revert of undo-log entry `id` as its own approval-gated `PendingActionPayload` —
|
|
233
|
+
* reusing the exact same staging/approval machinery write_file/run_shell_command already use,
|
|
234
|
+
* rather than a new confirmation concept. Approve/decline it the same way any other staged
|
|
235
|
+
* action resolves: `turn('', { sessionId, approved, pendingActionId })`.
|
|
236
|
+
*/
|
|
237
|
+
stageUndoAction(id: string): Promise<{
|
|
238
|
+
status: 'staged';
|
|
239
|
+
pendingActionId: string;
|
|
240
|
+
reason: string;
|
|
241
|
+
} | {
|
|
242
|
+
status: 'error';
|
|
243
|
+
message: string;
|
|
244
|
+
}>;
|
|
245
|
+
/**
|
|
246
|
+
* Ranked search over the per-message index (see appendTranscriptMessage/IndexedMessage), not
|
|
247
|
+
* the whole session transcript — a hit resolves to the one exchange that matched. Deliberately
|
|
248
|
+
* not scoped to a single sessionId: this is a single local install's memory namespace, and
|
|
249
|
+
* "what did I tell you about my dentist appointment" should find it regardless of which
|
|
250
|
+
* session it was said in.
|
|
251
|
+
*
|
|
252
|
+
* `MemoryAdapter.search()` scores every stored key in one pass (facts, reminders, experience
|
|
253
|
+
* data, the message index itself, its counters, ...), so this asks for every entry scoring
|
|
254
|
+
* above 0 rather than a small topK directly, then filters to `transcript-msg:` keys and
|
|
255
|
+
* truncates afterward — otherwise a real match could be pushed out of a small topK by
|
|
256
|
+
* unrelated non-transcript entries that happen to score higher. Read-only and synchronous over
|
|
257
|
+
* already-persisted data: never an LLM call, never a network request, never a mutation. Used
|
|
258
|
+
* by `/search`.
|
|
259
|
+
*
|
|
260
|
+
* `FileSystemAdapter.search()` (packages/runtime) throws if ANY file under the memory namespace
|
|
261
|
+
* fails to parse as JSON — e.g. a transcript file left truncated by a process kill mid-write.
|
|
262
|
+
* One corrupt entry unrelated to this query must not turn `/search` into an uncaught error for
|
|
263
|
+
* the user — degrade to "no results" instead, same fail-open posture as the other two
|
|
264
|
+
* `memory.search()` call sites (backfillMessageIndex, sweepAbandonedPendingActionsOnStartup).
|
|
265
|
+
*/
|
|
266
|
+
searchTranscript(query: string, topK?: number): Promise<TranscriptSearchHit[]>;
|
|
267
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A real, non-mutating tool call the model made while producing a reply — grounds a reply in something other
|
|
3
|
+
* than the model's own words. write_file is deliberately excluded: until approved, nothing was actually read or
|
|
4
|
+
* changed. `path` holds the file path for read_file/list_directory, the URL for fetch_url, or the query for
|
|
5
|
+
* web_search — same shape, different meaning per tool, to keep this interface minimal.
|
|
6
|
+
*
|
|
7
|
+
* Split into its own file (rather than living on assistant.ts or agent-loop.ts) so both can import it without
|
|
8
|
+
* either owning it — AgentLoop produces AssistantSource entries while executing tool calls, and assistant.ts's
|
|
9
|
+
* public AssistantTurnResult.sources field is typed with it.
|
|
10
|
+
*/
|
|
11
|
+
export interface AssistantSource {
|
|
12
|
+
tool: 'read_file' | 'list_directory' | 'web_search' | 'fetch_url';
|
|
13
|
+
path: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { RiskState, LayerActivityEvent, AskQuestion } from '@buildaharness/harness';
|
|
2
|
+
import { TokenUsage } from '@buildaharness/runtime';
|
|
3
|
+
import { AnswerClaim } from './answer-claim.js';
|
|
4
|
+
import { TurnIntentClassification } from './turn-intent-classifier.js';
|
|
5
|
+
import { AssistantSource } from './assistant-source.js';
|
|
6
|
+
import { BatchBudgetTrace } from './agent-loop.js';
|
|
7
|
+
import { PlanPosition } from './plan-store.js';
|
|
8
|
+
/**
|
|
9
|
+
* The public result/progress shapes every extracted module (ActionApprovalService,
|
|
10
|
+
* TurnInterpreter, HarnessBridge, ResponseService) builds or consumes — split into its own file
|
|
11
|
+
* so those modules can depend on the shapes without depending on assistant.ts itself (which, in
|
|
12
|
+
* turn, depends on all of them to assemble the sequencer). assistant.ts re-exports these under
|
|
13
|
+
* their original names for full backward compatibility with existing imports
|
|
14
|
+
* (index.ts/cli.ts/cli-session.ts/assistant.test.ts).
|
|
15
|
+
*/
|
|
16
|
+
export interface AssistantTrace {
|
|
17
|
+
nodeExecutionOrder: string[];
|
|
18
|
+
verificationHealth: {
|
|
19
|
+
strength: number;
|
|
20
|
+
feasibility: number;
|
|
21
|
+
};
|
|
22
|
+
/** Every one of the 11 harness layers' fired/skipped report for this turn — see LayerActivityEvent. Powers the "Why?" panel's "What I checked" list and the 11-layer status grid. */
|
|
23
|
+
layerActivity: LayerActivityEvent[];
|
|
24
|
+
/**
|
|
25
|
+
* Present only when the batch-research path (batch-list-detector.ts / AgentLoop.runBatchToolLoop)
|
|
26
|
+
* drove this turn — absent otherwise, same "absent when unused" convention as sources/usage.
|
|
27
|
+
*/
|
|
28
|
+
batchBudget?: BatchBudgetTrace;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Which code path actually produced this turn's reply — a test/telemetry affordance for
|
|
32
|
+
* plans/chat_ui_browser_e2e_plan.html phase B1 (the browser-e2e lane that unblocks the
|
|
33
|
+
* ASSISTANT_ONE_LOOP default-flip, R5 of the D2 one-loop rewire). The reply text itself is
|
|
34
|
+
* identical across all three by design (INV-19); this field is how a test asserts "the flag
|
|
35
|
+
* changed which path ran" without diffing internal traces.
|
|
36
|
+
*
|
|
37
|
+
* - 'posthoc' — flag OFF (or a trivial / no-tool turn): AgentLoop.runToolLoop finished a
|
|
38
|
+
* draftReply before HarnessBridge constructed HarnessRuntime; the harness run
|
|
39
|
+
* is post-hoc bookkeeping over an already-produced reply.
|
|
40
|
+
* - 'flat-oneloop' — flag ON, non-batch: the flat tool loop ran inside the harness's own
|
|
41
|
+
* driveMainLoop via AgentLoop.createOneLoopProposer.
|
|
42
|
+
* - 'batch-oneloop' — flag ON, batch-research: AgentLoop.createBatchOneLoopProposer drove the
|
|
43
|
+
* probe→calibrate→confirm→resolve→synthesize sequence under driveMainLoop.
|
|
44
|
+
*/
|
|
45
|
+
export type ProposerKind = 'posthoc' | 'flat-oneloop' | 'batch-oneloop';
|
|
46
|
+
export interface AssistantTurnResult {
|
|
47
|
+
/**
|
|
48
|
+
* `needs_clarification` (Q2 of plans/ask_question_and_plan_mode_plan.html): a structured
|
|
49
|
+
* ask-question escalation (a populated `EscalationHalt.blocker.questions`, Q0) was staged for
|
|
50
|
+
* resume via AskClarificationService instead of terminating the turn — pass
|
|
51
|
+
* `pendingClarificationId` + a completed `AskResponse` back into
|
|
52
|
+
* `turn(message, { pendingClarificationId, clarificationAnswer })` to resolve it. Only reachable
|
|
53
|
+
* when the effective askMode (Q1's three-tier INV-29 resolution) is enabled; flag-off leaves
|
|
54
|
+
* every escalation on the pre-existing `escalated`/`reply: null` path, byte-identical to today.
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* `needs_plan_approval` (P2 of plans/ask_question_and_plan_mode_plan.html): a drafted plan
|
|
58
|
+
* (plan mode's P1/P3) was staged for the mandatory whole-plan approval gate instead of
|
|
59
|
+
* continuing to revise — pass `planApprovalId` back into
|
|
60
|
+
* `turn(message, { planApprovalId, planDecision, edits? })` to resolve it. Every drafted plan
|
|
61
|
+
* passes through this exactly once, regardless of risk (no risk-tiered skip — see
|
|
62
|
+
* PlanApprovalService's doc comment).
|
|
63
|
+
*/
|
|
64
|
+
status: 'ok' | 'needs_approval' | 'escalated' | 'needs_clarification' | 'needs_plan_approval';
|
|
65
|
+
reply: string | null;
|
|
66
|
+
reason?: string;
|
|
67
|
+
/** See ProposerKind. Always set (defaults to 'posthoc'), on every status. */
|
|
68
|
+
proposerKind?: ProposerKind;
|
|
69
|
+
riskLevel?: TurnIntentClassification['riskLevel'];
|
|
70
|
+
controlState?: {
|
|
71
|
+
riskState: RiskState;
|
|
72
|
+
escalationReason: string | null;
|
|
73
|
+
};
|
|
74
|
+
stepsUsed?: number;
|
|
75
|
+
/** True when the turn was answered by the triviality fast path — no HarnessRuntime.run() this turn. */
|
|
76
|
+
harnessSkipped?: boolean;
|
|
77
|
+
/** Structured harness telemetry for a "Why?" disclosure — the step sequence and verification confidence, not free-text reasoning. */
|
|
78
|
+
trace?: AssistantTrace;
|
|
79
|
+
/** Set only when `needs_approval` was triggered by a `write_file`/`run_shell_command`/`send_email` tool call — pass back into `turn(message, { approved, pendingActionId })` to apply or discard it. */
|
|
80
|
+
pendingActionId?: string;
|
|
81
|
+
/** Which kind of action `pendingActionId` refers to — a write shows path + content preview, a shell command shows the exact command + resolved cwd, an email shows recipient + subject + body, a batch confirmation shows the projected remaining search count, a revert (staged only via /undo-action, never by the model) shows which paths will be restored/removed. */
|
|
82
|
+
pendingActionKind?: 'write' | 'shell' | 'email' | 'batch' | 'revert';
|
|
83
|
+
/** Real read_file/list_directory calls made while producing this reply, in call order. Only set when fileTools is configured and at least one such call happened this turn. */
|
|
84
|
+
sources?: AssistantSource[];
|
|
85
|
+
/**
|
|
86
|
+
* Structured, durable plan progress — present whenever a templated plan (new or
|
|
87
|
+
* resumed) drove this turn's initialTasks, absent otherwise (same "absent when
|
|
88
|
+
* unused" convention as trace/sources). Unlike trace, this can be non-null across
|
|
89
|
+
* many consecutive turns in the same session: the plan persists in `memory` until
|
|
90
|
+
* every task is COMPLETE or the user abandons it. See plan-store.ts.
|
|
91
|
+
*/
|
|
92
|
+
planStatus?: {
|
|
93
|
+
templateName: string | null;
|
|
94
|
+
successCriteria: string;
|
|
95
|
+
completionPct: number;
|
|
96
|
+
tasks: {
|
|
97
|
+
id: string;
|
|
98
|
+
description: string;
|
|
99
|
+
status: string;
|
|
100
|
+
}[];
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Token usage accumulated across every real LLM call this turn made (can be more than one:
|
|
104
|
+
* decomposition, plan-building, up to maxSteps tool-loop round trips).
|
|
105
|
+
* Absent when the backend/response never reported usage at all (e.g. the claude-cli backend
|
|
106
|
+
* with no usage field) — same "absent when unused" convention as trace/sources. Never set on
|
|
107
|
+
* needs_approval/escalated, matching how trace/sources already behave.
|
|
108
|
+
*/
|
|
109
|
+
usage?: TokenUsage;
|
|
110
|
+
/** Set when the Contradiction layer flagged a conflict with an existing belief this turn — see assistant-session.ts's findContradictionNotice doc comment for why this is a separate field instead of folded into `reply`. */
|
|
111
|
+
contradictionNotice?: string;
|
|
112
|
+
/**
|
|
113
|
+
* An epistemic-honesty signal for replies that went through the harness loop (absent on the
|
|
114
|
+
* triviality fast path, same "absent when unused" convention as trace/sources — a
|
|
115
|
+
* harness-skipped reply has no verification/evidence trail to attach). See answer-claim.ts's
|
|
116
|
+
* doc comment for how each field is derived.
|
|
117
|
+
*/
|
|
118
|
+
answerClaim?: AnswerClaim;
|
|
119
|
+
/**
|
|
120
|
+
* Set only on a plan-pacing pause — the "Ready to continue with: ...?"/"All plan steps have
|
|
121
|
+
* run..." text appended to `reply` after the model's own draftReply. A caller that streamed
|
|
122
|
+
* `reply` token-by-token via `onToken` (see TurnOptions.onToken) already showed draftReply
|
|
123
|
+
* live and must print this separately, since draftReply is all `onToken` ever saw — this text
|
|
124
|
+
* is computed after the harness run completes, well after streaming finished.
|
|
125
|
+
*/
|
|
126
|
+
pausedNote?: string;
|
|
127
|
+
/** Set only on `status: 'needs_clarification'` — pass back into `turn(message, { pendingClarificationId, clarificationAnswer })` to resolve it. See AskClarificationService. */
|
|
128
|
+
pendingClarificationId?: string;
|
|
129
|
+
/** The batch of questions `pendingClarificationId` refers to — set only on `status: 'needs_clarification'`, absent otherwise (same "absent when unused" convention as trace/sources). */
|
|
130
|
+
questions?: AskQuestion[];
|
|
131
|
+
/** Set only on `status: 'needs_plan_approval'` — pass back into `turn(message, { planApprovalId, planDecision, edits? })` to resolve it. See PlanApprovalService. */
|
|
132
|
+
planApprovalId?: string;
|
|
133
|
+
/**
|
|
134
|
+
* The staged plan `planApprovalId` refers to — set only on `status: 'needs_plan_approval'`,
|
|
135
|
+
* absent otherwise (same "absent when unused" convention as trace/sources). Each task's own
|
|
136
|
+
* `riskLevel` is included so an approval screen can show it prominently even though nothing
|
|
137
|
+
* about risk ever skips this gate (Section 5b-2). `reviewNotes` stays absent until P6's
|
|
138
|
+
* self-verification pass exists.
|
|
139
|
+
*/
|
|
140
|
+
planApproval?: {
|
|
141
|
+
templateName: string | null;
|
|
142
|
+
successCriteria: string;
|
|
143
|
+
rationale: string;
|
|
144
|
+
tasks: {
|
|
145
|
+
id: string;
|
|
146
|
+
description: string;
|
|
147
|
+
riskLevel?: 'LOW' | 'MEDIUM' | 'HIGH';
|
|
148
|
+
}[];
|
|
149
|
+
reviewNotes?: string[];
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
export interface AssistantProgress {
|
|
153
|
+
stepsUsed: number;
|
|
154
|
+
maxSteps: number;
|
|
155
|
+
currentNode?: string;
|
|
156
|
+
/** Live, mid-run position within a durable plan — set only while a plan is actually driving this turn. Absent for an ad hoc single-task/decomposed turn, same "absent when unused" convention as AssistantTurnResult.planStatus. */
|
|
157
|
+
planPosition?: PlanPosition;
|
|
158
|
+
}
|