@buildaharness/aielia 0.3.1 → 0.3.2
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 +30 -7
- package/dist/agent-loop.d.ts +8 -8
- package/dist/ask-clarification-service.d.ts +1 -1
- package/dist/ask-mode-flag.d.ts +1 -1
- package/dist/assistant-session.d.ts +1 -1
- package/dist/assistant-types.d.ts +3 -3
- package/dist/assistant.d.ts +12 -12
- package/dist/batch-research-fixtures.d.ts +1 -1
- package/dist/benchmark-injected-failure.d.ts +1 -1
- package/dist/bin-CqkqT6KK.js +2107 -0
- package/dist/bin-CqkqT6KK.js.map +1 -0
- package/dist/bin-sea-bootstrap.d.ts +1 -0
- package/dist/bin.d.ts +1 -0
- package/dist/claude-cli-llm-client.d.ts +2 -2
- package/dist/cli-args.d.ts +20 -0
- package/dist/cli-icons.d.ts +28 -0
- package/dist/{cli-session-D0uLdT2A.js → cli-session-BiX2DOni.js} +550 -121
- package/dist/cli-session-BiX2DOni.js.map +1 -0
- package/dist/cli.d.ts +31 -1
- package/dist/cli.js +1 -1691
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +14 -6
- package/dist/contradiction-checker.d.ts +1 -1
- package/dist/diff-format.d.ts +16 -0
- package/dist/execution-mode.d.ts +1 -1
- package/dist/fact-extraction.d.ts +4 -4
- package/dist/failure-mode-matcher.d.ts +1 -1
- package/dist/file-tools-mcp-server.mjs +2 -2
- package/dist/file-tools.d.ts +10 -2
- package/dist/harness-bridge.d.ts +4 -4
- package/dist/index.js +68 -68
- package/dist/index.js.map +1 -1
- package/dist/ink-select-prompt.d.ts +21 -0
- package/dist/markdown-line.d.ts +42 -0
- package/dist/mcp-server-asset.d.ts +24 -0
- package/dist/memory-service.d.ts +2 -2
- package/dist/network-containment.d.ts +1 -1
- package/dist/node-display-names.d.ts +1 -1
- package/dist/one-loop-flag.d.ts +1 -1
- package/dist/plan-drafting-service.d.ts +1 -1
- package/dist/plan-drafting.d.ts +4 -4
- package/dist/plan-mode-flag.d.ts +1 -1
- package/dist/plan-sketch-service.d.ts +1 -1
- package/dist/plan-sketch.d.ts +1 -1
- package/dist/plan-store.d.ts +2 -2
- package/dist/plan-templates/index.d.ts +1 -1
- package/dist/plan-verification.d.ts +1 -1
- package/dist/scripted-llm-client.d.ts +1 -1
- package/dist/sea-cache.d.ts +9 -0
- package/dist/self-update.d.ts +101 -0
- package/dist/shell-tools.d.ts +1 -1
- package/dist/spike.js.map +1 -1
- package/dist/tool-control-plane.d.ts +1 -1
- package/dist/tool-policy.d.ts +1 -1
- package/dist/tool-step.d.ts +10 -0
- package/dist/trace-events.d.ts +5 -5
- package/dist/trust-tagging.d.ts +1 -1
- package/dist/{tui-app-DxLPSGh2.js → tui-app-4_TrzESz.js} +176 -20
- package/dist/tui-app-4_TrzESz.js.map +1 -0
- package/dist/tui-app.d.ts +34 -9
- package/dist/tui-mode-flag.d.ts +1 -1
- package/dist/turn-intent-classifier.d.ts +5 -5
- package/dist/turn-policy.d.ts +1 -1
- package/dist/update-check-flag.d.ts +11 -0
- package/dist/version.d.ts +6 -0
- package/package.json +7 -3
- package/dist/cli-session-D0uLdT2A.js.map +0 -1
- package/dist/tui-app-DxLPSGh2.js.map +0 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* MCP stdio server exposing read_file/list_directory/write_file/fetch_url/
|
|
4
4
|
* create_reminder/list_reminders/run_shell_command to the Claude CLI — the same
|
|
5
5
|
* MCP mechanism already proven for the coaching/planner agents (see
|
|
6
|
-
*
|
|
6
|
+
* the coaching and planner agents' MCP servers: both
|
|
7
7
|
* FastMCP stdio servers started via --mcp-config, alongside --dangerously-skip-permissions
|
|
8
8
|
* so a headless `claude -p` call can actually invoke them without an interactive
|
|
9
9
|
* permission prompt — this server is started the same way, for the same reason).
|
|
@@ -468,7 +468,7 @@ export async function fetchUrlSafely(url) {
|
|
|
468
468
|
// DuckDuckGo's HTML endpoint resets the TLS connection outright for any
|
|
469
469
|
// non-browser client, a block beneath the HTTP layer no request header or retry
|
|
470
470
|
// can work around (confirmed live during
|
|
471
|
-
//
|
|
471
|
+
// the internal plan's W8).
|
|
472
472
|
|
|
473
473
|
const WEB_SEARCH_MAX_RESULTS = 5
|
|
474
474
|
const NO_WEB_RESULTS_LITERAL = 'No results found.'
|
package/dist/file-tools.d.ts
CHANGED
|
@@ -27,6 +27,12 @@ export declare function resolveInWorkspace(workspaceRoot: string, requestedPath:
|
|
|
27
27
|
* doesn't implement `realpath`.
|
|
28
28
|
*/
|
|
29
29
|
export declare function assertRealPathInWorkspace(backend: FsBackend, workspaceRoot: string, resolvedPath: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Reads a file's current on-disk content for building a diff preview against a proposed write
|
|
32
|
+
* (see diff-format.ts's formatWriteDiff) — `undefined` if the file doesn't exist yet, the same
|
|
33
|
+
* "nothing to diff against" signal `applyPendingAction`'s own `previousContent` uses.
|
|
34
|
+
*/
|
|
35
|
+
export declare function readCurrentFileContent(backend: FsBackend, workspaceRoot: string, requestedPath: string): Promise<string | undefined>;
|
|
30
36
|
export declare const READ_FILE_TOOL: ToolDefinition;
|
|
31
37
|
export declare const LIST_DIRECTORY_TOOL: ToolDefinition;
|
|
32
38
|
export declare const WRITE_FILE_TOOL: ToolDefinition;
|
|
@@ -115,7 +121,9 @@ export interface ShellExecutionResult {
|
|
|
115
121
|
}
|
|
116
122
|
export type ApplyPendingActionResult = ({
|
|
117
123
|
kind: 'write';
|
|
118
|
-
} & PendingActionRecord
|
|
124
|
+
} & PendingActionRecord & {
|
|
125
|
+
previousContent?: string;
|
|
126
|
+
}) | ({
|
|
119
127
|
kind: 'shell';
|
|
120
128
|
} & PendingActionRecord & {
|
|
121
129
|
execution: ShellExecutionResult;
|
|
@@ -130,7 +138,7 @@ export type ApplyPendingActionResult = ({
|
|
|
130
138
|
* Stages an action for later approval. `id` is a random UUID, not derived from the session — see
|
|
131
139
|
* T4 of the file-tools plan.
|
|
132
140
|
*
|
|
133
|
-
* Structural hardening principle (
|
|
141
|
+
* Structural hardening principle (the internal plan Phase 4 step 1):
|
|
134
142
|
* this gate runs unconditionally on the tool call's own concrete arguments (the literal path/
|
|
135
143
|
* content or command/cwd being proposed), independent of whatever risk-classifier.ts's pre-flight
|
|
136
144
|
* text patterns concluded about the user's phrasing. That's deliberate — it's the same "gate on
|
package/dist/harness-bridge.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface HarnessRunParams {
|
|
|
25
25
|
userMessage: string;
|
|
26
26
|
facts: UserFact[];
|
|
27
27
|
/**
|
|
28
|
-
* Phase 4 of
|
|
28
|
+
* Phase 4 of the internal plan: this turn's
|
|
29
29
|
* merged lexical+LLM fact list — memory-service.ts's `buildTurnFacts()`, the exact helper
|
|
30
30
|
* `recordFacts()` itself uses to decide what to write to the fact stores — fed into
|
|
31
31
|
* `factExtractor` below as this turn's "isNew" belief seed. Replaces this file's former direct
|
|
@@ -43,7 +43,7 @@ export interface HarnessRunParams {
|
|
|
43
43
|
onProgress?: (progress: AssistantProgress) => void;
|
|
44
44
|
onUsage: (usage: TokenUsage) => void;
|
|
45
45
|
/**
|
|
46
|
-
* R2 of
|
|
46
|
+
* R2 of the internal plan: a harness-driven proposer (built by
|
|
47
47
|
* AgentLoop.createHarnessProposer) swapped in as the toolExecutors 'default' entry instead of
|
|
48
48
|
* `() => draftReply`, when the one-loop flag is enabled and a caller actually supplies one.
|
|
49
49
|
* `undefined` (every caller today, before R3 wires runTurn to build one) means `run()` falls
|
|
@@ -54,7 +54,7 @@ export interface HarnessRunParams {
|
|
|
54
54
|
oneLoopProposer?: (toolCtx: ToolExecutorContext) => unknown | Promise<unknown>;
|
|
55
55
|
/**
|
|
56
56
|
* Trajectory Supervisor GATHER_EVIDENCE host (S5 of
|
|
57
|
-
*
|
|
57
|
+
* the internal plan) — AgentLoop.runSupervisorInvestigation bound
|
|
58
58
|
* to this turn's read-only tools + risk hint. Passed straight through to
|
|
59
59
|
* HarnessRunOptions.runInvestigation. `undefined` (every caller until a supervisorDecider is
|
|
60
60
|
* also wired) → GATHER_EVIDENCE degrades to CONTINUE inside the harness, so this is inert by
|
|
@@ -62,7 +62,7 @@ export interface HarnessRunParams {
|
|
|
62
62
|
*/
|
|
63
63
|
runInvestigation?: (req: InvestigationRequestData) => Promise<InvestigationFinding[]>;
|
|
64
64
|
/**
|
|
65
|
-
* Q2 of
|
|
65
|
+
* Q2 of the internal plan — the already-resolved effective askMode
|
|
66
66
|
* (Q1's three-tier INV-29 resolution), threaded straight into HarnessRunOptions.askMode so the
|
|
67
67
|
* harness's own supervisor ASK_USER path (S3) batches questions consistently with
|
|
68
68
|
* AskClarificationService's own gating. Also decides whether a thrown EscalationHalt carrying
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import {
|
|
5
|
-
import { A, a, b, C, c, D, e, f, g,
|
|
4
|
+
import { B as classifyRisk } from "./cli-session-BiX2DOni.js";
|
|
5
|
+
import { A, a, b, C, c, D, e, f, g, i, F, j, I, L, k, l, m, P, n, o, R, p, S, q, W, r, s, t, u, v, w, x, y, z, G, H, J, K, M, N, O, T, U, X, Y, Z, _, $, a1, a2, a3, a7, a8, a9, ac, af, ag, ah, ai, aj, ak, al, am, an, ao, ap, aq, ar, at, au, av, aw, ax, ay, az, aA, aC, aD, aF, aG, aH, aI } from "./cli-session-BiX2DOni.js";
|
|
6
6
|
import { MAX_OPTIONS_PER_QUESTION, MAX_QUESTIONS_PER_BATCH, MIN_OPTIONS_PER_QUESTION, validateAskResponse } from "@buildaharness/harness";
|
|
7
7
|
const PLAN_SCHEMA = {
|
|
8
8
|
type: "object",
|
|
@@ -128,85 +128,85 @@ export {
|
|
|
128
128
|
e as DEFAULT_CONFIG,
|
|
129
129
|
f as DEFAULT_ONE_LOOP_MODE,
|
|
130
130
|
g as DEFAULT_PLAN_MODE,
|
|
131
|
-
|
|
131
|
+
i as EmailDeliveryError,
|
|
132
132
|
F as FETCH_URL_TOOL,
|
|
133
|
-
|
|
133
|
+
j as FILE_TOOLS,
|
|
134
134
|
I as InvalidEmailArgsError,
|
|
135
135
|
L as LAYER_DISPLAY_NAME,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
k as LAYER_ORDER,
|
|
137
|
+
l as LAYER_SHORT_CODE,
|
|
138
|
+
m as LIST_DIRECTORY_TOOL,
|
|
139
139
|
MAX_OPTIONS_PER_QUESTION,
|
|
140
140
|
MAX_QUESTIONS_PER_BATCH,
|
|
141
141
|
MIN_OPTIONS_PER_QUESTION,
|
|
142
142
|
P as PathOutsideWorkspaceError,
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
n as PersonalAssistant,
|
|
144
|
+
o as PrivateNetworkTargetError,
|
|
145
145
|
R as READ_FILE_TOOL,
|
|
146
|
-
|
|
146
|
+
p as RUN_SHELL_COMMAND_TOOL,
|
|
147
147
|
S as SEND_EMAIL_TOOL,
|
|
148
|
-
|
|
148
|
+
q as SHELL_TOOLS,
|
|
149
149
|
W as WEB_SEARCH_TOOL,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
150
|
+
r as WEB_TOOLS,
|
|
151
|
+
s as WRITE_FILE_TOOL,
|
|
152
|
+
t as abandonPlan,
|
|
153
|
+
u as applyPendingAction,
|
|
154
|
+
v as assertPublicHttpUrl,
|
|
155
|
+
w as braveSearch,
|
|
156
|
+
x as buildClaudePrompt,
|
|
157
157
|
buildPlanFromTemplate,
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
y as buildWhyChain,
|
|
159
|
+
z as classifyError,
|
|
160
160
|
classifyRisk,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
G as classifyToolYield,
|
|
162
|
+
H as classifyTurnIntent,
|
|
163
|
+
J as computePlanPosition,
|
|
164
|
+
K as createPlanRecord,
|
|
165
|
+
M as createResendSender,
|
|
166
166
|
createScriptedLLMClient,
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
167
|
+
N as createSmtpSender,
|
|
168
|
+
O as defaultExportFilename,
|
|
169
|
+
T as detectHomogeneousBatchList,
|
|
170
|
+
U as discardPendingAction,
|
|
171
|
+
X as estimateCostUsd,
|
|
172
|
+
Y as executeActionTool,
|
|
173
|
+
Z as executeFileTool,
|
|
174
|
+
_ as executeShellTool,
|
|
175
|
+
$ as executeWebTool,
|
|
176
|
+
a1 as formatCostSummary,
|
|
177
|
+
a2 as formatDoctorReport,
|
|
178
|
+
a3 as formatEmailApprovalReason,
|
|
179
|
+
a7 as formatMemorySummary,
|
|
180
|
+
a8 as formatPlanProgress,
|
|
181
|
+
a9 as formatSearchResults,
|
|
182
|
+
ac as formatTranscriptMarkdown,
|
|
183
|
+
af as isLikelyEmailAddress,
|
|
184
|
+
ag as listTemplateNames,
|
|
185
|
+
ah as loadActivePlan,
|
|
186
|
+
ai as loadPendingAction,
|
|
187
|
+
aj as loadTemplate,
|
|
188
|
+
ak as matchTemplateIfConfident,
|
|
189
|
+
al as nextPendingTask,
|
|
190
|
+
am as nodeDisplayName,
|
|
191
|
+
an as nodeToLayer,
|
|
192
|
+
ao as normalizeAskMode,
|
|
193
|
+
ap as normalizeOneLoopMode,
|
|
194
|
+
aq as normalizePlanMode,
|
|
195
|
+
ar as parseClaudeCliOutput,
|
|
196
|
+
at as pickTemplateForTask,
|
|
197
|
+
au as planCompletionPct,
|
|
198
|
+
av as resolveAskMode,
|
|
199
|
+
aw as resolveConfig,
|
|
200
|
+
ax as resolveInWorkspace,
|
|
201
|
+
ay as resolveOneLoopMode,
|
|
202
|
+
az as resolvePlanMode,
|
|
203
|
+
aA as savePlan,
|
|
204
|
+
aC as stagePendingAction,
|
|
205
|
+
aD as stagedActionInput,
|
|
206
|
+
aF as stripMcpToolPrefix,
|
|
207
|
+
aG as summarizeToolStep,
|
|
208
|
+
aH as updatePlanFromRun,
|
|
209
209
|
validateAskResponse,
|
|
210
|
-
|
|
210
|
+
aI as validateConfig
|
|
211
211
|
};
|
|
212
212
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/plan-builder.ts","../src/scripted-llm-client.ts"],"sourcesContent":["import type { ILLMClient, TokenUsage } from '@buildaharness/runtime'\nimport type { DecomposedTaskSpec } from './decomposition-classifier.js'\nimport type { PlanTemplate } from './plan-templates/index.js'\n\nexport interface Plan {\n templateName: string\n successCriteria: string\n tasks: DecomposedTaskSpec[]\n /**\n * Why this approach, not just what the success criteria are — optional for now since neither\n * buildPlanFromTemplate's template-personalization call nor any other current producer of a\n * Plan asks the LLM for one; plan-store.ts's createPlanRecord defaults it to '' when absent.\n * A future from-scratch drafting path (plan mode's P3) is expected to populate it.\n */\n rationale?: string\n}\n\nconst PLAN_SCHEMA = {\n type: 'object',\n properties: {\n tasks: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n description: { type: 'string' },\n depends_on: { type: 'array', items: { type: 'string' } },\n },\n required: ['id', 'description', 'depends_on'],\n },\n },\n },\n required: ['tasks'],\n}\n\nfunction buildSystemPrompt(template: PlanTemplate): string {\n const skeleton = template.tasks\n .map((t) => `- id: ${t.id}; title: ${t.title}; depends_on: [${t.depends_on.join(', ')}]`)\n .join('\\n')\n return (\n `You are adapting a \"${template.name}\" plan template to a specific user request. ` +\n `Here is the template's task skeleton — keep the exact same ids and depends_on structure, ` +\n `one output task per skeleton task, but personalize each description to the actual request:\\n${skeleton}\\n\\n` +\n `Success criteria for this kind of plan: ${template.success_criteria}\\n\\n` +\n 'Phrase each `description` starting with the concrete subject or object it acts on (e.g. \"the login tests: ' +\n 'rerun after the config fix\" rather than \"rerun the login tests after the config fix\"), so later comparisons ' +\n \"against this task's completion/failure beliefs share matching vocabulary. \" +\n 'Respond with JSON only, no prose: {\"tasks\":[{\"id\": string, \"description\": string, \"depends_on\": ' +\n 'string[]}]}. `id` and `depends_on` values must exactly match the skeleton above.'\n )\n}\n\n// Only the shape the LLM actually returns (id/description/depends_on) — riskLevel isn't asked for\n// here at all (see buildPlanFromTemplate's doc comment for why) and gets attached afterward from\n// the template's own already-curated risk_level, so this is deliberately narrower than\n// DecomposedTaskSpec itself.\ninterface RawPlanTask {\n id: string\n description: string\n depends_on: string[]\n}\n\nfunction isRawPlanTask(value: unknown): value is RawPlanTask {\n if (typeof value !== 'object' || value === null) return false\n const v = value as Record<string, unknown>\n return (\n typeof v.id === 'string' &&\n typeof v.description === 'string' &&\n Array.isArray(v.depends_on) &&\n v.depends_on.every((d) => typeof d === 'string')\n )\n}\n\n/**\n * Spends one real LLM call personalizing `template`'s task skeleton to `message` —\n * only called once classifyTurnIntent has already named a matchedPlanTemplate, so an\n * ordinary turn never pays for this. Same \"malformed/incomplete JSON is the expected\n * failure mode, not the edge case\" fallback classifyTurnIntent itself uses: any parse\n * failure or a response with fewer than 2 usable tasks returns null, meaning \"fall back to\n * the caller's own ad hoc decomposition for this turn\" rather than throwing.\n *\n * Each output task's `riskLevel` is attached from the matching skeleton task's own\n * `risk_level` (curated by whoever wrote the template's JSON) by id, not asked of the LLM —\n * the template already has this, more accurately than an LLM re-deriving it from a\n * personalized one-line description would, and it costs zero extra output tokens. Falls back\n * to 'LOW' only for the pathological case of a task id the LLM invented that doesn't match any\n * skeleton task (the id/depends_on-must-match-skeleton instruction above means this should not\n * happen in practice).\n */\nexport async function buildPlanFromTemplate(\n llmClient: ILLMClient,\n message: string,\n template: PlanTemplate,\n model?: string,\n onUsage?: (usage: TokenUsage) => void,\n): Promise<Plan | null> {\n try {\n const response = await llmClient.callChatStructured(\n [\n { role: 'system', content: buildSystemPrompt(template) },\n { role: 'user', content: message },\n ],\n undefined,\n { model, onUsage, structuredOutput: { schema: PLAN_SCHEMA } },\n )\n const parsed = JSON.parse(response.content) as { tasks?: unknown }\n if (!Array.isArray(parsed.tasks)) return null\n const rawTasks = parsed.tasks.filter(isRawPlanTask)\n if (rawTasks.length <= 1) return null\n const riskByTemplateId = new Map(template.tasks.map((t) => [t.id, t.risk_level]))\n const tasks: DecomposedTaskSpec[] = rawTasks.map((t) => ({ ...t, riskLevel: riskByTemplateId.get(t.id) ?? 'LOW' }))\n return { templateName: template.name, successCriteria: template.success_criteria, tasks }\n } catch {\n return null\n }\n}\n","import type {\n ChatMessage,\n ChatOptions,\n ILLMClient,\n LLMStructuredResponse,\n ToolDefinition,\n} from '@buildaharness/runtime'\nimport { classifyRisk } from './risk-classifier.js'\n\n/**\n * A deterministic, network-free {@link ILLMClient} for tests and demos — the one publicly\n * supported way to drive a real `PersonalAssistant` turn end to end without a proxy, an API key,\n * or a live model. Built for plans/chat_ui_browser_e2e_plan.html phase B1 (the browser-e2e lane\n * that unblocks the `ASSISTANT_ONE_LOOP` default-flip), and reusable by chat-ui's `demo-seed.ts`.\n *\n * It mirrors the shape of the internal `ScriptedToolLLMClient` that `assistant.test.ts` has used\n * for a long time: every turn spends exactly one mandatory `classifyTurnIntent` call up front,\n * which this client recognises (by a distinctive phrase from that classifier's system prompt) and\n * answers on its own from a lightweight, deterministic default — so `responses` only has to\n * script the *tool loop / one-loop proposer's* own calls, not that bookkeeping call.\n *\n * `responses` entries are consumed in order, one per non-classifier `callChatStructured` call:\n * - a plain `string` → `{ content: string }` (a final answer, no tool calls)\n * - an `LLMStructuredResponse` → use as-is (set `toolCalls` to simulate the model calling a tool)\n *\n * `callChat`/`callChatSync` (the no-tools reply path, and post-approval synthesis) stream\n * `streamChunks` (default: `['']`).\n */\nexport interface ScriptedLLMClientScript {\n /** Ordered structured responses for the tool loop / one-loop proposer. A bare string is shorthand for a final answer with no tool calls. */\n responses?: Array<string | LLMStructuredResponse>\n /** Chunks streamed from `callChat`/`callChatSync`. Defaults to `['']`. */\n streamChunks?: string[]\n /**\n * Per-user-message override of the auto-answered `classifyTurnIntent` JSON fields (e.g.\n * `{ isTrivial: true }`, `{ riskLevel: 'HIGH' }`, `{ decomposedTasks: [...] }`). Return\n * `undefined` to keep the derived default for that message.\n */\n classify?: (userMessage: string) => Record<string, unknown> | undefined\n}\n\n/** The phrase that opens `turn-intent-classifier.ts`'s system prompt — stable, and how every scripted client tells that mandatory call apart from a real tool-loop call. */\nconst TURN_INTENT_MARKER = 'eight independent judgments'\n\nfunction isTurnIntentRequest(messages: ChatMessage[]): boolean {\n return messages.some((m) => m.role === 'system' && m.content.includes(TURN_INTENT_MARKER))\n}\n\n/**\n * The deterministic stand-in for what a real model would return for `classifyTurnIntent` —\n * risk comes from the same lexical `classifyRisk` the real fallback path uses; everything else\n * is inert (not trivial, not a reminder, no decomposition, no plan template) unless `override`\n * says otherwise. `isTrivial` defaults to `false` so a scripted turn actually exercises the\n * harness loop (the interesting case for one-loop parity tests); pass `{ isTrivial: true }` via\n * `classify` for the triviality fast path.\n */\nfunction deriveTurnIntentJSON(messages: ChatMessage[], override?: Record<string, unknown>): string {\n const userContent = messages.find((m) => m.role === 'user')?.content ?? ''\n const risk = classifyRisk(userContent)\n const isReminderRequest = risk.reason.includes('reminder')\n const base = {\n riskLevel: risk.riskLevel,\n riskReason: risk.reason,\n isTrivial: false,\n decomposedTasks: [],\n isReminderRequest,\n isBulkReminderRequest: isReminderRequest && risk.requiresApproval,\n isAbandonRequest: false,\n matchedPlanTemplate: null,\n needsMultiStepPlan: false,\n statesDurableFacts: [],\n }\n return JSON.stringify({ ...base, ...override })\n}\n\nclass ScriptedLLMClient implements ILLMClient {\n /** Non-classifier `callChatStructured` calls made so far — the index into `responses`. */\n toolCalls = 0\n private responseIndex = 0\n\n constructor(private readonly script: ScriptedLLMClientScript) {}\n\n private get streamChunks(): string[] {\n return this.script.streamChunks && this.script.streamChunks.length > 0 ? this.script.streamChunks : ['']\n }\n\n async *callChat(_messages: ChatMessage[], _options?: ChatOptions): AsyncIterable<string> {\n for (const chunk of this.streamChunks) yield chunk\n }\n\n async callChatSync(messages: ChatMessage[], options?: ChatOptions): Promise<string> {\n const chunks: string[] = []\n for await (const chunk of this.callChat(messages, options)) chunks.push(chunk)\n return chunks.join('')\n }\n\n async callChatStructured(messages: ChatMessage[], _tools?: ToolDefinition[], _options?: ChatOptions): Promise<LLMStructuredResponse> {\n if (isTurnIntentRequest(messages)) {\n const userContent = messages.find((m) => m.role === 'user')?.content ?? ''\n return { content: deriveTurnIntentJSON(messages, this.script.classify?.(userContent)) }\n }\n this.toolCalls++\n const responses = this.script.responses ?? []\n if (this.responseIndex >= responses.length) {\n throw new Error(\n `createScriptedLLMClient: no scripted response for tool-loop call #${this.responseIndex + 1} (scripted ${responses.length})`,\n )\n }\n const next = responses[this.responseIndex++]\n return typeof next === 'string' ? { content: next } : next\n }\n}\n\n/**\n * Builds a scripted {@link ILLMClient}. See {@link ScriptedLLMClientScript}. The returned value\n * is a plain `ILLMClient` — pass it straight to `PersonalAssistant.create({ llmClient })`.\n */\nexport function createScriptedLLMClient(script: ScriptedLLMClientScript = {}): ILLMClient {\n return new ScriptedLLMClient(script)\n}\n"],"names":["t","v","m"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/plan-builder.ts","../src/scripted-llm-client.ts"],"sourcesContent":["import type { ILLMClient, TokenUsage } from '@buildaharness/runtime'\nimport type { DecomposedTaskSpec } from './decomposition-classifier.js'\nimport type { PlanTemplate } from './plan-templates/index.js'\n\nexport interface Plan {\n templateName: string\n successCriteria: string\n tasks: DecomposedTaskSpec[]\n /**\n * Why this approach, not just what the success criteria are — optional for now since neither\n * buildPlanFromTemplate's template-personalization call nor any other current producer of a\n * Plan asks the LLM for one; plan-store.ts's createPlanRecord defaults it to '' when absent.\n * A future from-scratch drafting path (plan mode's P3) is expected to populate it.\n */\n rationale?: string\n}\n\nconst PLAN_SCHEMA = {\n type: 'object',\n properties: {\n tasks: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n description: { type: 'string' },\n depends_on: { type: 'array', items: { type: 'string' } },\n },\n required: ['id', 'description', 'depends_on'],\n },\n },\n },\n required: ['tasks'],\n}\n\nfunction buildSystemPrompt(template: PlanTemplate): string {\n const skeleton = template.tasks\n .map((t) => `- id: ${t.id}; title: ${t.title}; depends_on: [${t.depends_on.join(', ')}]`)\n .join('\\n')\n return (\n `You are adapting a \"${template.name}\" plan template to a specific user request. ` +\n `Here is the template's task skeleton — keep the exact same ids and depends_on structure, ` +\n `one output task per skeleton task, but personalize each description to the actual request:\\n${skeleton}\\n\\n` +\n `Success criteria for this kind of plan: ${template.success_criteria}\\n\\n` +\n 'Phrase each `description` starting with the concrete subject or object it acts on (e.g. \"the login tests: ' +\n 'rerun after the config fix\" rather than \"rerun the login tests after the config fix\"), so later comparisons ' +\n \"against this task's completion/failure beliefs share matching vocabulary. \" +\n 'Respond with JSON only, no prose: {\"tasks\":[{\"id\": string, \"description\": string, \"depends_on\": ' +\n 'string[]}]}. `id` and `depends_on` values must exactly match the skeleton above.'\n )\n}\n\n// Only the shape the LLM actually returns (id/description/depends_on) — riskLevel isn't asked for\n// here at all (see buildPlanFromTemplate's doc comment for why) and gets attached afterward from\n// the template's own already-curated risk_level, so this is deliberately narrower than\n// DecomposedTaskSpec itself.\ninterface RawPlanTask {\n id: string\n description: string\n depends_on: string[]\n}\n\nfunction isRawPlanTask(value: unknown): value is RawPlanTask {\n if (typeof value !== 'object' || value === null) return false\n const v = value as Record<string, unknown>\n return (\n typeof v.id === 'string' &&\n typeof v.description === 'string' &&\n Array.isArray(v.depends_on) &&\n v.depends_on.every((d) => typeof d === 'string')\n )\n}\n\n/**\n * Spends one real LLM call personalizing `template`'s task skeleton to `message` —\n * only called once classifyTurnIntent has already named a matchedPlanTemplate, so an\n * ordinary turn never pays for this. Same \"malformed/incomplete JSON is the expected\n * failure mode, not the edge case\" fallback classifyTurnIntent itself uses: any parse\n * failure or a response with fewer than 2 usable tasks returns null, meaning \"fall back to\n * the caller's own ad hoc decomposition for this turn\" rather than throwing.\n *\n * Each output task's `riskLevel` is attached from the matching skeleton task's own\n * `risk_level` (curated by whoever wrote the template's JSON) by id, not asked of the LLM —\n * the template already has this, more accurately than an LLM re-deriving it from a\n * personalized one-line description would, and it costs zero extra output tokens. Falls back\n * to 'LOW' only for the pathological case of a task id the LLM invented that doesn't match any\n * skeleton task (the id/depends_on-must-match-skeleton instruction above means this should not\n * happen in practice).\n */\nexport async function buildPlanFromTemplate(\n llmClient: ILLMClient,\n message: string,\n template: PlanTemplate,\n model?: string,\n onUsage?: (usage: TokenUsage) => void,\n): Promise<Plan | null> {\n try {\n const response = await llmClient.callChatStructured(\n [\n { role: 'system', content: buildSystemPrompt(template) },\n { role: 'user', content: message },\n ],\n undefined,\n { model, onUsage, structuredOutput: { schema: PLAN_SCHEMA } },\n )\n const parsed = JSON.parse(response.content) as { tasks?: unknown }\n if (!Array.isArray(parsed.tasks)) return null\n const rawTasks = parsed.tasks.filter(isRawPlanTask)\n if (rawTasks.length <= 1) return null\n const riskByTemplateId = new Map(template.tasks.map((t) => [t.id, t.risk_level]))\n const tasks: DecomposedTaskSpec[] = rawTasks.map((t) => ({ ...t, riskLevel: riskByTemplateId.get(t.id) ?? 'LOW' }))\n return { templateName: template.name, successCriteria: template.success_criteria, tasks }\n } catch {\n return null\n }\n}\n","import type {\n ChatMessage,\n ChatOptions,\n ILLMClient,\n LLMStructuredResponse,\n ToolDefinition,\n} from '@buildaharness/runtime'\nimport { classifyRisk } from './risk-classifier.js'\n\n/**\n * A deterministic, network-free {@link ILLMClient} for tests and demos — the one publicly\n * supported way to drive a real `PersonalAssistant` turn end to end without a proxy, an API key,\n * or a live model. Built for the internal plan phase B1 (the browser-e2e lane\n * that unblocks the `ASSISTANT_ONE_LOOP` default-flip), and reusable by chat-ui's `demo-seed.ts`.\n *\n * It mirrors the shape of the internal `ScriptedToolLLMClient` that `assistant.test.ts` has used\n * for a long time: every turn spends exactly one mandatory `classifyTurnIntent` call up front,\n * which this client recognises (by a distinctive phrase from that classifier's system prompt) and\n * answers on its own from a lightweight, deterministic default — so `responses` only has to\n * script the *tool loop / one-loop proposer's* own calls, not that bookkeeping call.\n *\n * `responses` entries are consumed in order, one per non-classifier `callChatStructured` call:\n * - a plain `string` → `{ content: string }` (a final answer, no tool calls)\n * - an `LLMStructuredResponse` → use as-is (set `toolCalls` to simulate the model calling a tool)\n *\n * `callChat`/`callChatSync` (the no-tools reply path, and post-approval synthesis) stream\n * `streamChunks` (default: `['']`).\n */\nexport interface ScriptedLLMClientScript {\n /** Ordered structured responses for the tool loop / one-loop proposer. A bare string is shorthand for a final answer with no tool calls. */\n responses?: Array<string | LLMStructuredResponse>\n /** Chunks streamed from `callChat`/`callChatSync`. Defaults to `['']`. */\n streamChunks?: string[]\n /**\n * Per-user-message override of the auto-answered `classifyTurnIntent` JSON fields (e.g.\n * `{ isTrivial: true }`, `{ riskLevel: 'HIGH' }`, `{ decomposedTasks: [...] }`). Return\n * `undefined` to keep the derived default for that message.\n */\n classify?: (userMessage: string) => Record<string, unknown> | undefined\n}\n\n/** The phrase that opens `turn-intent-classifier.ts`'s system prompt — stable, and how every scripted client tells that mandatory call apart from a real tool-loop call. */\nconst TURN_INTENT_MARKER = 'eight independent judgments'\n\nfunction isTurnIntentRequest(messages: ChatMessage[]): boolean {\n return messages.some((m) => m.role === 'system' && m.content.includes(TURN_INTENT_MARKER))\n}\n\n/**\n * The deterministic stand-in for what a real model would return for `classifyTurnIntent` —\n * risk comes from the same lexical `classifyRisk` the real fallback path uses; everything else\n * is inert (not trivial, not a reminder, no decomposition, no plan template) unless `override`\n * says otherwise. `isTrivial` defaults to `false` so a scripted turn actually exercises the\n * harness loop (the interesting case for one-loop parity tests); pass `{ isTrivial: true }` via\n * `classify` for the triviality fast path.\n */\nfunction deriveTurnIntentJSON(messages: ChatMessage[], override?: Record<string, unknown>): string {\n const userContent = messages.find((m) => m.role === 'user')?.content ?? ''\n const risk = classifyRisk(userContent)\n const isReminderRequest = risk.reason.includes('reminder')\n const base = {\n riskLevel: risk.riskLevel,\n riskReason: risk.reason,\n isTrivial: false,\n decomposedTasks: [],\n isReminderRequest,\n isBulkReminderRequest: isReminderRequest && risk.requiresApproval,\n isAbandonRequest: false,\n matchedPlanTemplate: null,\n needsMultiStepPlan: false,\n statesDurableFacts: [],\n }\n return JSON.stringify({ ...base, ...override })\n}\n\nclass ScriptedLLMClient implements ILLMClient {\n /** Non-classifier `callChatStructured` calls made so far — the index into `responses`. */\n toolCalls = 0\n private responseIndex = 0\n\n constructor(private readonly script: ScriptedLLMClientScript) {}\n\n private get streamChunks(): string[] {\n return this.script.streamChunks && this.script.streamChunks.length > 0 ? this.script.streamChunks : ['']\n }\n\n async *callChat(_messages: ChatMessage[], _options?: ChatOptions): AsyncIterable<string> {\n for (const chunk of this.streamChunks) yield chunk\n }\n\n async callChatSync(messages: ChatMessage[], options?: ChatOptions): Promise<string> {\n const chunks: string[] = []\n for await (const chunk of this.callChat(messages, options)) chunks.push(chunk)\n return chunks.join('')\n }\n\n async callChatStructured(messages: ChatMessage[], _tools?: ToolDefinition[], _options?: ChatOptions): Promise<LLMStructuredResponse> {\n if (isTurnIntentRequest(messages)) {\n const userContent = messages.find((m) => m.role === 'user')?.content ?? ''\n return { content: deriveTurnIntentJSON(messages, this.script.classify?.(userContent)) }\n }\n this.toolCalls++\n const responses = this.script.responses ?? []\n if (this.responseIndex >= responses.length) {\n throw new Error(\n `createScriptedLLMClient: no scripted response for tool-loop call #${this.responseIndex + 1} (scripted ${responses.length})`,\n )\n }\n const next = responses[this.responseIndex++]\n return typeof next === 'string' ? { content: next } : next\n }\n}\n\n/**\n * Builds a scripted {@link ILLMClient}. See {@link ScriptedLLMClientScript}. The returned value\n * is a plain `ILLMClient` — pass it straight to `PersonalAssistant.create({ llmClient })`.\n */\nexport function createScriptedLLMClient(script: ScriptedLLMClientScript = {}): ILLMClient {\n return new ScriptedLLMClient(script)\n}\n"],"names":["t","v","m"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SelectOption } from './cli.js';
|
|
2
|
+
export interface SelectPromptProps {
|
|
3
|
+
/** The question text, rendered the same bold-yellow way `TuiInput`'s own `promptLabel` renders a pending askYesNo/askLine question. */
|
|
4
|
+
question: string;
|
|
5
|
+
/** Rendered in order, top to bottom — the highlighted (arrow-key-selected) option starts at index 0. Each option's `key` also doubles as its single-keystroke shortcut. */
|
|
6
|
+
options: SelectOption[];
|
|
7
|
+
/** Called once, with the chosen option's `key` — either Enter on the highlighted row, or a direct keystroke matching an option's `key`. */
|
|
8
|
+
onSubmit: (key: string) => void;
|
|
9
|
+
isActive?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Phase 7 of the internal plan: an arrow-key + numbered/shortcut-key
|
|
13
|
+
* selector, replacing free-text `y`/`N` typed into the same box used for chat (the report's
|
|
14
|
+
* finding — Codex/Claude Code both use a dedicated selector for this). Deliberately a standalone
|
|
15
|
+
* component rather than a mode bolted onto `TuiInput`: `TuiInput` is a general-purpose free-text
|
|
16
|
+
* editor (word-wrap, cursor movement, history recall) that has no notion of a fixed option list,
|
|
17
|
+
* and grafting one on would tangle two unrelated input models together for no shared benefit — the
|
|
18
|
+
* two components share only the surrounding visual grammar (bold label above a round-bordered
|
|
19
|
+
* box), which each renders independently.
|
|
20
|
+
*/
|
|
21
|
+
export declare function SelectPrompt(props: SelectPromptProps): React.JSX.Element;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recognizes one line of `diff-format.ts`'s output by shape: `DIFF_INDENT` followed by a
|
|
3
|
+
* right-aligned line number and a unified-diff sign character. That combination — a fixed
|
|
4
|
+
* two-space indent no ordinary assistant prose produces, paired with a line-number gutter no
|
|
5
|
+
* markdown construct produces — doesn't occur in natural writing (a markdown bullet is always
|
|
6
|
+
* `- text`, dash *then* a space with no number in front, which `CHECKBOX_RE`/plain rendering
|
|
7
|
+
* already own), so this can't misfire on a real bullet list. Context lines (sign is a plain
|
|
8
|
+
* space) are recognized too, with `color: undefined` — they still need to short-circuit
|
|
9
|
+
* `renderMarkdownLine`'s plain-text fallback below (which would otherwise prepend `ASSISTANT_BULLET`
|
|
10
|
+
* to them as if they were ordinary prose), they just aren't colored the way a real diff tool
|
|
11
|
+
* doesn't color its context lines either.
|
|
12
|
+
*/
|
|
13
|
+
export declare function matchDiffLine(text: string): {
|
|
14
|
+
content: string;
|
|
15
|
+
color: 'green' | 'red' | undefined;
|
|
16
|
+
} | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Renders one line as colored diff content if `matchDiffLine` recognizes it, otherwise
|
|
19
|
+
* `undefined` so the caller falls back to its own plain/markdown rendering. Used by
|
|
20
|
+
* `renderMarkdownLine` below (the `'assistant'`-kind, post-write confirmation) and by
|
|
21
|
+
* `tui-app.tsx`'s `'system'`-kind branch (the pre-approval preview) — the two `LogLine` kinds a
|
|
22
|
+
* `formatWriteDiff()` string actually reaches, per `classifyLineKind`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function renderDiffLine(text: string, key: React.Key): React.JSX.Element | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Colors the `[needs approval — write/shell command/…]`/`[needs approval — HIGH/…]` tag
|
|
27
|
+
* `cli.ts` prints ahead of every approval prompt (see its own `console.log` call sites) yellow —
|
|
28
|
+
* the same "worth your attention" color `StatusLine`'s own `⚠`-prefixed indicators already use —
|
|
29
|
+
* leaving the reason text after it in the default color. This line is `'system'`-classified (see
|
|
30
|
+
* `classifyLineKind`), not `'assistant'`, so it's checked from `tui-app.tsx`'s own `'system'`
|
|
31
|
+
* branch rather than from `renderMarkdownLine`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderNeedsApprovalLine(text: string, key: React.Key): React.JSX.Element | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Renders one committed line of assistant/system text, recognizing a leading `#`/`##`/… header or
|
|
36
|
+
* `- [ ]`/`- [x]` checkbox before falling back to plain inline-styled text. `key` is the React key
|
|
37
|
+
* for the returned element (the caller already has a per-line index from `<Static>`'s own render
|
|
38
|
+
* prop — see `tui-app.tsx`'s `LogLineText`). `continuation` is `LogLine.continuation` — true for
|
|
39
|
+
* every line of a multi-line reply after the first, which suppresses `ASSISTANT_BULLET` (the
|
|
40
|
+
* marker belongs once at the front of the whole message, not on every wrapped line).
|
|
41
|
+
*/
|
|
42
|
+
export declare function renderMarkdownLine(text: string, key: React.Key, continuation?: boolean): React.JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const MCP_SERVER_FILE_NAME = "file-tools-mcp-server.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* The lexical pattern files file-tools-mcp-server.mjs reads at startup via
|
|
4
|
+
* `join(__dirname, 'lexical/patterns/<name>')` — they have to sit beside the extracted server,
|
|
5
|
+
* so they are embedded as SEA assets (under this same relative key) and extracted with it.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MCP_SERVER_PATTERN_FILES: readonly ["injection-patterns.json", "fact-markers.json", "risk-patterns.json"];
|
|
8
|
+
export interface SeaModule {
|
|
9
|
+
isSea(): boolean;
|
|
10
|
+
getRawAsset(key: string): ArrayBuffer;
|
|
11
|
+
}
|
|
12
|
+
/** `node:sea` if this process is a Node single executable application, else null. Never throws. */
|
|
13
|
+
export declare function loadSea(): SeaModule | null;
|
|
14
|
+
/**
|
|
15
|
+
* Absolute on-disk path to file-tools-mcp-server.mjs, which the claude-cli backend hands to the
|
|
16
|
+
* separate `claude` binary to spawn as a `node <path>` MCP subprocess.
|
|
17
|
+
*
|
|
18
|
+
* - Inside a SEA binary, `import.meta.url` is not a real file location, so the embedded asset is
|
|
19
|
+
* extracted once (version-gated, see sea-cache.ts) and that path returned.
|
|
20
|
+
* - Otherwise (npm install, tsx), it is the file sitting next to this module, as before.
|
|
21
|
+
*
|
|
22
|
+
* `seaOverride` and `home` exist for tests; production callers pass nothing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveMcpServerPath(seaOverride?: SeaModule | null, home?: string): string;
|
package/dist/memory-service.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare const DURABLE_FACTS_KEY = "facts:durable";
|
|
|
8
8
|
/**
|
|
9
9
|
* Global, never cleared by `/new` — same retention shape as DURABLE_FACTS_KEY (a pending fact
|
|
10
10
|
* shouldn't vanish just because the session changed). Phase 3 of
|
|
11
|
-
*
|
|
11
|
+
* the internal plan: a `model_inferred` fact
|
|
12
12
|
* that reached `durable: true, confidence: 'medium'` lands here instead of being silently
|
|
13
13
|
* promoted or silently dropped — see `recordFacts()`'s promotion policy and
|
|
14
14
|
* `confirmPendingFact()`/`rejectPendingFact()` below.
|
|
@@ -77,7 +77,7 @@ export interface RecordFactsResult {
|
|
|
77
77
|
}
|
|
78
78
|
/**
|
|
79
79
|
* The exact lexical+LLM merge `recordFacts()` uses to decide what to write to the fact stores,
|
|
80
|
-
* exposed (Phase 4 of
|
|
80
|
+
* exposed (Phase 4 of the internal plan) so a
|
|
81
81
|
* caller can seed `HarnessRunParams.currentTurnFacts` (harness-bridge.ts's `factExtractor`) with
|
|
82
82
|
* the identical merged, deduplicated list — instead of harness-bridge.ts recomputing only the free
|
|
83
83
|
* lexical half and never seeing an LLM-caught fact at all, which left a same-turn LLM-caught fact
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node-level network containment for an approved run_shell_command execution — Decision 6 of
|
|
3
|
-
*
|
|
3
|
+
* the internal plan (Phase 4 step 2). This is the network-reachability
|
|
4
4
|
* half of that decision; the env-stripping half already existed in shell-executor.ts's
|
|
5
5
|
* allowlistedEnv(). Deliberately not a real OS sandbox (see the plan's Decision 6 for why that
|
|
6
6
|
* tradeoff was chosen): a minimal loopback-only forward proxy that relays a CONNECT tunnel
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LayerActivityEvent } from '@buildaharness/harness';
|
|
2
2
|
export declare function nodeDisplayName(node: string | undefined): string | undefined;
|
|
3
3
|
/**
|
|
4
|
-
* The 11 harness layer slugs, in the fixed order
|
|
4
|
+
* The 11 harness layer slugs, in the fixed order the internal developer notes' architecture table lists them —
|
|
5
5
|
* used by both the CLI's `/layers` and chat-ui's layer grid to render a stable 2-3 letter
|
|
6
6
|
* code per layer (Phase 3.3 of the harness layer activation plan). Matches the `layer` field
|
|
7
7
|
* on the `layer_activity` TraceEvent (see trace-events.ts) and HarnessRunOptions'
|
package/dist/one-loop-flag.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* a module-level default resolved once from process.env, threaded as an injectable constructor
|
|
6
6
|
* option so tests never touch real process.env.
|
|
7
7
|
*
|
|
8
|
-
* Backs
|
|
8
|
+
* Backs the internal plan's R2 phase: the harness-driven proposer that
|
|
9
9
|
* lets HarnessRuntime's driveMainLoop actually call AgentLoop's tool-calling machinery once per
|
|
10
10
|
* main-loop iteration, instead of receiving an already-finished draftReply after the fact. R2
|
|
11
11
|
* landed it default OFF; R3/R4 built the rest of the wiring behind it; R5 gathered the rollout
|
|
@@ -23,7 +23,7 @@ export type PlanDraftOutcome = {
|
|
|
23
23
|
fallThrough: true;
|
|
24
24
|
} | AssistantTurnResult;
|
|
25
25
|
/**
|
|
26
|
-
* P3 of
|
|
26
|
+
* P3 of the internal plan — how `assistant.ts`'s auto-trigger tells
|
|
27
27
|
* `draftTurn` what to seed a *fresh* draft with (ignored on every later revision of an
|
|
28
28
|
* already-drafting plan). `templateName` set means a template matched — the draft starts from
|
|
29
29
|
* that template's own skeleton (today's personalization behavior, reused, not discarded — see
|
package/dist/plan-drafting.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { AskQuestion } from '@buildaharness/harness';
|
|
|
3
3
|
import { DecomposedTaskSpec } from './decomposition-classifier.js';
|
|
4
4
|
import { PlanTaskRecord } from './plan-store.js';
|
|
5
5
|
/**
|
|
6
|
-
* P1 of
|
|
6
|
+
* P1 of the internal plan — the "plan-drafting revision call" P1's
|
|
7
7
|
* routing (assistant.ts's runTurn, PlanDraftingService) sends every message to while
|
|
8
8
|
* `planMode.active`. Deliberately narrower than P3's eventual version: no tools at all (P3 adds
|
|
9
9
|
* read_file/list_directory grounding for a from-scratch draft — see Section & non-goals),
|
|
@@ -18,7 +18,7 @@ export interface PlanDraftTurn {
|
|
|
18
18
|
successCriteria: string;
|
|
19
19
|
rationale: string;
|
|
20
20
|
/**
|
|
21
|
-
* P2 of
|
|
21
|
+
* P2 of the internal plan — true only once the model judges the user
|
|
22
22
|
* has clearly signaled they're satisfied with the current draft and want to proceed, at which
|
|
23
23
|
* point PlanDraftingService hands off to PlanApprovalService instead of returning another plain
|
|
24
24
|
* drafting reply. Defaults false on any malformed/missing value (see parsing below) — a
|
|
@@ -26,7 +26,7 @@ export interface PlanDraftTurn {
|
|
|
26
26
|
*/
|
|
27
27
|
readyForApproval: boolean;
|
|
28
28
|
/**
|
|
29
|
-
* P8 of
|
|
29
|
+
* P8 of the internal plan — set only when this revision hit a genuine
|
|
30
30
|
* ambiguity worth pausing on instead of guessing (an ambiguous scope boundary, or a
|
|
31
31
|
* MEDIUM/HIGH-risk branch point with more than one viable approach) — e.g. "which of these two
|
|
32
32
|
* frameworks should the plan target?" A drafting call that sets this always also leaves
|
|
@@ -48,7 +48,7 @@ export interface PlanDraftTurn {
|
|
|
48
48
|
*/
|
|
49
49
|
export declare function draftPlanRevision(llmClient: ILLMClient, userMessage: string, currentTasks: PlanTaskRecord[], currentSuccessCriteria: string, currentRationale: string, model?: string, onUsage?: (usage: TokenUsage) => void,
|
|
50
50
|
/**
|
|
51
|
-
* P3 of
|
|
51
|
+
* P3 of the internal plan — grounding text for a from-scratch (no
|
|
52
52
|
* template match) draft, gathered from a bounded read_file/list_directory investigation walk
|
|
53
53
|
* (see AgentLoop.runSupervisorInvestigation, reused rather than building a new search engine —
|
|
54
54
|
* Scope & non-goals) so a code-shaped request's draft can reference real repo state instead of
|
package/dist/plan-mode-flag.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* unit-testable in isolation, mirroring one-loop-flag.ts/ask-mode-flag.ts's exact shape
|
|
4
4
|
* (module-level default, an injectable resolver so tests never touch real process.env).
|
|
5
5
|
*
|
|
6
|
-
* P11 of
|
|
6
|
+
* P11 of the internal plan — the rollout flag gating P1-P8's whole
|
|
7
7
|
* plan-mode behavior change (exclusive drafting, mandatory approval, the generalized trigger,
|
|
8
8
|
* auto-advance, the markdown/delegate/trust-mode extensions) behind one switch. 'legacy' (the
|
|
9
9
|
* default, for the whole rollout window, per `DEFAULT_PLAN_MODE`): P3's judgment-based
|
|
@@ -3,7 +3,7 @@ import { AssistantTurnResult } from './assistant-types.js';
|
|
|
3
3
|
import { TraceEvent } from './trace-events.js';
|
|
4
4
|
import { AgentLoop } from './agent-loop.js';
|
|
5
5
|
/**
|
|
6
|
-
* P9 of
|
|
6
|
+
* P9 of the internal plan — the lightweight plan-sketch delegate: one
|
|
7
7
|
* bounded LLM call (grounded, best-effort, via the same read_file/list_directory investigation
|
|
8
8
|
* walk P3's from-scratch drafting reuses — AgentLoop.runSupervisorInvestigation, not a new search
|
|
9
9
|
* engine) that returns a plain proposed task list in the chat reply itself. Deliberately NOT
|
package/dist/plan-sketch.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ILLMClient, TokenUsage } from '@buildaharness/runtime';
|
|
2
2
|
/**
|
|
3
|
-
* P9 of
|
|
3
|
+
* P9 of the internal plan — the lightweight plan-sketch delegate's one
|
|
4
4
|
* bounded LLM call. Deliberately the cheapest possible shape: a single plain-text reply the
|
|
5
5
|
* caller shows directly in the chat, not a structured task list staged anywhere — there is no
|
|
6
6
|
* PlanRecord, no PlanTaskRecord, nothing for P2's approval gate to see (INV-35). Distinct from
|
package/dist/plan-store.d.ts
CHANGED
|
@@ -113,7 +113,7 @@ export declare function loadPlanRecord(memory: MemoryAdapter, sessionId: string,
|
|
|
113
113
|
export declare function loadActivePlan(memory: MemoryAdapter, sessionId: string, fsPersistence?: PlanFsPersistence): Promise<PlanRecord | null>;
|
|
114
114
|
/**
|
|
115
115
|
* Builds an immediately-`active` PlanRecord with no drafting/approval step at all. Before P3 of
|
|
116
|
-
*
|
|
116
|
+
* the internal plan, TurnInterpreter.resolveTasks called this the instant
|
|
117
117
|
* classifyTurnIntent matched a template; P3 retired that call site — a template match (or the
|
|
118
118
|
* newer general needsMultiStepPlan judgment) now always routes through plan mode's exclusive
|
|
119
119
|
* drafting+approval loop instead (see assistant.ts's auto-trigger and
|
|
@@ -227,6 +227,6 @@ export declare function computePlanPosition(plan: PlanRecord, taskGraphTasks: {
|
|
|
227
227
|
}[]): PlanPosition | null;
|
|
228
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
229
|
export declare function nextPendingTask(plan: PlanRecord): PlanTaskRecord | null;
|
|
230
|
-
/** Human-readable plan status — mirrors
|
|
230
|
+
/** Human-readable plan status — mirrors the Python planner agent's format_plan_progress, for callers (the CLI) that want text instead of the structured planStatus field. */
|
|
231
231
|
export declare function formatPlanProgress(plan: PlanRecord): string;
|
|
232
232
|
export {};
|
|
@@ -20,7 +20,7 @@ export declare function listTemplateNames(): string[];
|
|
|
20
20
|
/**
|
|
21
21
|
* Heuristic: match description keywords to a template name. Always returns a
|
|
22
22
|
* template — falls back to 'problem_solving' on zero keyword hits, exactly like
|
|
23
|
-
*
|
|
23
|
+
* the Python planner agent's pick_template_for_task. Used by buildPlanFromTemplate
|
|
24
24
|
* once a template has already been chosen some other way (e.g. matchTemplateIfConfident).
|
|
25
25
|
*/
|
|
26
26
|
export declare function pickTemplateForTask(description: string): string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ILLMClient, TokenUsage } from '@buildaharness/runtime';
|
|
2
2
|
import { PlanTaskRecord } from './plan-store.js';
|
|
3
3
|
/**
|
|
4
|
-
* P6 of
|
|
4
|
+
* P6 of the internal plan — runs between drafting (P1/P3) and staging
|
|
5
5
|
* for approval (P2). Two independent checks, deliberately ordered cheapest-and-most-certain
|
|
6
6
|
* first: (1) dependency-graph validity (pure, synchronous, no LLM call — the same three checks
|
|
7
7
|
* `adapter/harness/task_graph.py`'s `validate_task_graph` runs Python-side: orphaned `depends_on`
|
|
@@ -2,7 +2,7 @@ import { ILLMClient, LLMStructuredResponse } from '@buildaharness/runtime';
|
|
|
2
2
|
/**
|
|
3
3
|
* A deterministic, network-free {@link ILLMClient} for tests and demos — the one publicly
|
|
4
4
|
* supported way to drive a real `PersonalAssistant` turn end to end without a proxy, an API key,
|
|
5
|
-
* or a live model. Built for
|
|
5
|
+
* or a live model. Built for the internal plan phase B1 (the browser-e2e lane
|
|
6
6
|
* that unblocks the `ASSISTANT_ONE_LOOP` default-flip), and reusable by chat-ui's `demo-seed.ts`.
|
|
7
7
|
*
|
|
8
8
|
* It mirrors the shape of the internal `ScriptedToolLLMClient` that `assistant.test.ts` has used
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** `~/.buildaharness/personal-assistant/sea-cache/<version>` — same data dir as cli.ts's `defaultDataDir`. */
|
|
2
|
+
export declare function seaCacheDir(version: string, home?: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Writes `read()`'s bytes to `<dir>/<fileName>` unless that exact path already exists, and returns
|
|
5
|
+
* the path. Version-gated by construction: `dir` embeds the CLI version, so a new release never
|
|
6
|
+
* reuses a stale extraction. Written via a temp file + rename so a crash or a concurrent second
|
|
7
|
+
* launch can never observe (and then import) a half-written file.
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractAssetOnce(dir: string, fileName: string, read: () => string | ArrayBuffer): string;
|