@buildaharness/aielia 0.3.4 → 0.3.6
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 +156 -35
- package/dist/PlanGraphPane.d.ts +19 -0
- package/dist/_memory-core-generated.d.ts +1764 -0
- package/dist/action-approval-service.d.ts +34 -2
- package/dist/action-record.d.ts +13 -0
- package/dist/activity-log.d.ts +4 -0
- package/dist/agent-loop.d.ts +106 -9
- package/dist/ambiguity-guard-flag.d.ts +12 -0
- package/dist/ambiguity-guard.d.ts +20 -0
- package/dist/answer-claim.d.ts +13 -1
- package/dist/ask-clarification-service.d.ts +12 -1
- package/dist/ask-response-format.d.ts +6 -0
- package/dist/assistant-session.d.ts +32 -2
- package/dist/assistant-source.d.ts +7 -0
- package/dist/assistant-types.d.ts +20 -2
- package/dist/assistant.d.ts +160 -6
- package/dist/benchmark-injected-failure.d.ts +6 -0
- package/dist/bin-BYRl4xXM.js +4985 -0
- package/dist/bin-BYRl4xXM.js.map +1 -0
- package/dist/cli-config.d.ts +3 -0
- package/dist/cli-session.d.ts +19 -5
- package/dist/cli.d.ts +26 -2
- package/dist/cli.js +1 -1
- package/dist/code-plan-templates.d.ts +21 -0
- package/dist/config.d.ts +31 -0
- package/dist/constraint-check.d.ts +35 -0
- package/dist/contradiction-checker.d.ts +7 -0
- package/dist/debug-log.d.ts +10 -1
- package/dist/decomposed-answer.d.ts +13 -0
- package/dist/decomposition-classifier.d.ts +2 -0
- package/dist/doctor-checks.d.ts +3 -0
- package/dist/email.d.ts +5 -3
- package/dist/episodic-digest.d.ts +89 -0
- package/dist/fact-extraction.d.ts +39 -0
- package/dist/file-tools-mcp-server.mjs +288 -37
- package/dist/file-tools.d.ts +6 -0
- package/dist/first-run.d.ts +0 -2
- package/dist/git-exclude.d.ts +9 -0
- package/dist/goal-graph-store.d.ts +18 -18
- package/dist/grounding-check.d.ts +33 -0
- package/dist/harness-bridge.d.ts +75 -2
- package/dist/harness-token-budget.d.ts +9 -0
- package/dist/index.d.ts +21 -2
- package/dist/index.js +166 -94
- package/dist/index.js.map +1 -1
- package/dist/ink-select-prompt.d.ts +2 -0
- package/dist/ink-test-render.d.ts +2 -0
- package/dist/layer-policy-flag.d.ts +13 -0
- package/dist/layer-policy-wiring.d.ts +77 -0
- package/dist/layer-settings.d.ts +49 -0
- package/dist/layer-telemetry.d.ts +2 -0
- package/dist/lexical/lexical-mode.d.ts +29 -0
- package/dist/memory-consolidation.d.ts +80 -0
- package/dist/memory-governance.d.ts +30 -0
- package/dist/memory-reviewer.d.ts +114 -0
- package/dist/memory-service.d.ts +356 -9
- package/dist/model-json.d.ts +17 -0
- package/dist/next-step-context.d.ts +51 -0
- package/dist/next-step-proposer.d.ts +10 -2
- package/dist/plan-drafting-service.d.ts +3 -0
- package/dist/plan-graph-flag.d.ts +20 -0
- package/dist/plan-question.d.ts +60 -0
- package/dist/plan-service.d.ts +6 -2
- package/dist/plan-step-prompt.d.ts +15 -0
- package/dist/plan-store.d.ts +12 -1
- package/dist/plan-viz/normalize.d.ts +14 -0
- package/dist/plan-viz/plan-detail-lines.d.ts +3 -0
- package/dist/plan-viz/plan-layout.d.ts +48 -0
- package/dist/plan-viz/plan-nav.d.ts +29 -0
- package/dist/plan-viz/plan-raster.d.ts +36 -0
- package/dist/plan-viz/plan-routes.d.ts +6 -0
- package/dist/plan-viz/plan-snapshot.d.ts +73 -0
- package/dist/plan-viz/plan-viewport.d.ts +29 -0
- package/dist/plan-viz/plan-width.d.ts +15 -0
- package/dist/plan-viz/testkit/display-width.d.ts +12 -0
- package/dist/plan-viz/testkit/generators.d.ts +61 -0
- package/dist/plan-viz/testkit/grid-oracles.d.ts +38 -0
- package/dist/plan-viz/testkit/nav-oracles.d.ts +79 -0
- package/dist/plan-viz/testkit/types.d.ts +8 -0
- package/dist/plan-viz/types.d.ts +34 -0
- package/dist/{provider-setup-CPbB5y_V.js → provider-setup-CV3S_TAS.js} +10792 -5731
- package/dist/provider-setup-CV3S_TAS.js.map +1 -0
- package/dist/recall-tool.d.ts +43 -0
- package/dist/recovery-note.d.ts +22 -0
- package/dist/reply-audit.d.ts +67 -0
- package/dist/response-service.d.ts +11 -1
- package/dist/review-checker.d.ts +10 -4
- package/dist/reviewer-revision.d.ts +23 -0
- package/dist/risk-classifier.d.ts +7 -0
- package/dist/scripted-llm-client.d.ts +26 -1
- package/dist/semantic-compaction.d.ts +15 -0
- package/dist/semantic-hypotheses.d.ts +40 -0
- package/dist/side-call-policy.d.ts +30 -0
- package/dist/source-reliability.d.ts +71 -0
- package/dist/task-completion-check.d.ts +24 -0
- package/dist/tool-control-plane.d.ts +33 -1
- package/dist/tool-effect-class.d.ts +9 -0
- package/dist/tool-step.d.ts +6 -0
- package/dist/trace-events.d.ts +1 -1
- package/dist/transcript-compaction.d.ts +9 -0
- package/dist/trust-tagging.d.ts +2 -0
- package/dist/{tui-app-DeyZ4Pr9.js → tui-app-CJq6YaiP.js} +436 -21
- package/dist/tui-app-CJq6YaiP.js.map +1 -0
- package/dist/tui-app.d.ts +73 -0
- package/dist/turn-abort.d.ts +18 -0
- package/dist/turn-intent-classifier.d.ts +81 -14
- package/dist/turn-interpreter.d.ts +27 -3
- package/dist/version.d.ts +1 -1
- package/package.json +6 -6
- package/dist/bin-CdVTAe7j.js +0 -2209
- package/dist/bin-CdVTAe7j.js.map +0 -1
- package/dist/provider-setup-CPbB5y_V.js.map +0 -1
- package/dist/tui-app-DeyZ4Pr9.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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, h, i,
|
|
6
|
-
import { MAX_OPTIONS_PER_QUESTION, MAX_QUESTIONS_PER_BATCH, MIN_OPTIONS_PER_QUESTION, validateAskResponse } from "@buildaharness/harness";
|
|
4
|
+
import { bj as parseModelJson, a0 as classifyRiskLexical } from "./provider-setup-CV3S_TAS.js";
|
|
5
|
+
import { A, a, b, C, c, D, e, f, g, h, i, j, k, l, m, o, F, p, I, L, q, r, s, t, u, v, w, x, M, P, y, z, B, R, G, S, H, J, W, K, N, O, Q, T, U, V, X, Y, Z, _, $, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ac, ad, ae, ag, ah, ai, aj, ak, al, am, ao, aq, ar, as, at, av, aw, ay, az, aB, aC, aE, aF, aI, aK, aL, aM, aN, aP, aQ, aR, aS, aT, aU, aW, aX, aY, aZ, a_, a$, b0, b1, b2, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf, bh, bk, bl, bm, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, bA, bB, bC, bD, bE, bF, bH, bI, bK, bM, bN, bO, bP, bQ, bR, bS, bT } from "./provider-setup-CV3S_TAS.js";
|
|
6
|
+
import { DEFAULT_LAYER_POLICY_MODE, 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",
|
|
9
9
|
properties: {
|
|
@@ -46,7 +46,7 @@ async function buildPlanFromTemplate(llmClient, message, template, model, onUsag
|
|
|
46
46
|
void 0,
|
|
47
47
|
{ model, onUsage, structuredOutput: { schema: PLAN_SCHEMA } }
|
|
48
48
|
);
|
|
49
|
-
const parsed =
|
|
49
|
+
const parsed = parseModelJson(response.content);
|
|
50
50
|
if (!Array.isArray(parsed.tasks)) return null;
|
|
51
51
|
const rawTasks = parsed.tasks.filter(isRawPlanTask);
|
|
52
52
|
if (rawTasks.length <= 1) return null;
|
|
@@ -57,14 +57,30 @@ async function buildPlanFromTemplate(llmClient, message, template, model, onUsag
|
|
|
57
57
|
return null;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
const
|
|
60
|
+
const SIDE_CALL_MARKERS = [
|
|
61
|
+
["You check whether a proposed action genuinely conflicts", '{"conflict":false}'],
|
|
62
|
+
["You match a set of observed symptoms against a curated library", "null"],
|
|
63
|
+
["You are a security classifier analyzing untrusted external content", '{"flagged":false}'],
|
|
64
|
+
["You check whether an assistant's reply is faithful to the raw results", '{"verdict":"grounded"}'],
|
|
65
|
+
["You judge whether an assistant actually accomplished a task", '{"done":true}'],
|
|
66
|
+
["You condense the earlier part of a conversation", '{"summary":""}'],
|
|
67
|
+
["You judge whether an assistant's reply violates constraints", '{"violations":[]}'],
|
|
68
|
+
["You weigh the reliability of the sources an assistant read", '{"assessments":[],"weighed":true}'],
|
|
69
|
+
["You propose competing explanations for an underdetermined request", '{"hypotheses":[]}'],
|
|
70
|
+
["You decide which explanations a set of new observations rules out", '{"contradicted":[]}'],
|
|
71
|
+
["You write a session handoff digest", '{"digest":null}'],
|
|
72
|
+
["You consolidate a small store of remembered facts about a user", '{"proposals":[]}'],
|
|
73
|
+
["You review what a user said across several turns", '{"ops":[]}'],
|
|
74
|
+
["You check proposed memory entries against what a user actually said", '{"verdicts":[]}']
|
|
75
|
+
];
|
|
76
|
+
const TURN_INTENT_MARKER = " independent judgments";
|
|
61
77
|
function isTurnIntentRequest(messages) {
|
|
62
78
|
return messages.some((m2) => m2.role === "system" && m2.content.includes(TURN_INTENT_MARKER));
|
|
63
79
|
}
|
|
64
80
|
function deriveTurnIntentJSON(messages, override) {
|
|
65
81
|
var _a;
|
|
66
82
|
const userContent = ((_a = messages.find((m2) => m2.role === "user")) == null ? void 0 : _a.content) ?? "";
|
|
67
|
-
const risk =
|
|
83
|
+
const risk = classifyRiskLexical(userContent);
|
|
68
84
|
const isReminderRequest = risk.reason.includes("reminder");
|
|
69
85
|
const base = {
|
|
70
86
|
riskLevel: risk.riskLevel,
|
|
@@ -74,6 +90,8 @@ function deriveTurnIntentJSON(messages, override) {
|
|
|
74
90
|
isReminderRequest,
|
|
75
91
|
isBulkReminderRequest: isReminderRequest && risk.requiresApproval,
|
|
76
92
|
isAbandonRequest: false,
|
|
93
|
+
isPlanQuestion: false,
|
|
94
|
+
isUnderdetermined: false,
|
|
77
95
|
matchedPlanTemplate: null,
|
|
78
96
|
needsMultiStepPlan: false,
|
|
79
97
|
statesDurableFacts: []
|
|
@@ -99,11 +117,14 @@ class ScriptedLLMClient {
|
|
|
99
117
|
return chunks.join("");
|
|
100
118
|
}
|
|
101
119
|
async callChatStructured(messages, _tools, _options) {
|
|
102
|
-
var _a, _b, _c;
|
|
120
|
+
var _a, _b, _c, _d;
|
|
103
121
|
if (isTurnIntentRequest(messages)) {
|
|
104
122
|
const userContent = ((_a = messages.find((m2) => m2.role === "user")) == null ? void 0 : _a.content) ?? "";
|
|
105
123
|
return { content: deriveTurnIntentJSON(messages, (_c = (_b = this.script).classify) == null ? void 0 : _c.call(_b, userContent)) };
|
|
106
124
|
}
|
|
125
|
+
const system = ((_d = messages.find((m2) => m2.role === "system")) == null ? void 0 : _d.content) ?? "";
|
|
126
|
+
const side = [...this.script.sideResponses ?? [], ...SIDE_CALL_MARKERS].find(([marker]) => system.includes(marker));
|
|
127
|
+
if (side) return { content: side[1] };
|
|
107
128
|
this.toolCalls++;
|
|
108
129
|
const responses = this.script.responses ?? [];
|
|
109
130
|
if (this.responseIndex >= responses.length) {
|
|
@@ -124,104 +145,155 @@ export {
|
|
|
124
145
|
b as AskClarificationService,
|
|
125
146
|
C as CONFIG_KEYS,
|
|
126
147
|
c as ConfigValidationError,
|
|
127
|
-
D as
|
|
128
|
-
e as
|
|
129
|
-
f as
|
|
130
|
-
g as
|
|
131
|
-
h as
|
|
132
|
-
|
|
133
|
-
|
|
148
|
+
D as DEFAULT_AMBIGUITY_GUARD_MODE,
|
|
149
|
+
e as DEFAULT_ASK_MODE,
|
|
150
|
+
f as DEFAULT_CONFIG,
|
|
151
|
+
g as DEFAULT_GOAL_GRAPH_MODE,
|
|
152
|
+
h as DEFAULT_GOAL_GRAPH_SUGGEST_MODE,
|
|
153
|
+
DEFAULT_LAYER_POLICY_MODE,
|
|
154
|
+
i as DEFAULT_LEXICAL_MODE,
|
|
155
|
+
j as DEFAULT_MEMORY_WRITE_MODE,
|
|
156
|
+
k as DEFAULT_ONE_LOOP_MODE,
|
|
157
|
+
l as DEFAULT_PLAN_GRAPH_MODE,
|
|
158
|
+
m as DEFAULT_PLAN_MODE,
|
|
159
|
+
o as EmailDeliveryError,
|
|
134
160
|
F as FETCH_URL_TOOL,
|
|
135
|
-
|
|
161
|
+
p as FILE_TOOLS,
|
|
136
162
|
I as InvalidEmailArgsError,
|
|
137
163
|
L as LAYER_DISPLAY_NAME,
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
164
|
+
q as LAYER_ORDER,
|
|
165
|
+
r as LAYER_SETTINGS,
|
|
166
|
+
s as LAYER_SHORT_CODE,
|
|
167
|
+
t as LEXICAL_CHECK_FAMILIES,
|
|
168
|
+
u as LEXICAL_FAMILIES,
|
|
169
|
+
v as LIST_DIRECTORY_TOOL,
|
|
170
|
+
w as LayerSettingError,
|
|
171
|
+
x as LiveSteeringChannel,
|
|
142
172
|
MAX_OPTIONS_PER_QUESTION,
|
|
143
173
|
MAX_QUESTIONS_PER_BATCH,
|
|
174
|
+
M as MEMORY_WRITE_MODES,
|
|
144
175
|
MIN_OPTIONS_PER_QUESTION,
|
|
145
176
|
P as PROVIDER_SETUP,
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
177
|
+
y as PathOutsideWorkspaceError,
|
|
178
|
+
z as PersonalAssistant,
|
|
179
|
+
B as PrivateNetworkTargetError,
|
|
149
180
|
R as READ_FILE_TOOL,
|
|
150
|
-
|
|
151
|
-
S as
|
|
152
|
-
|
|
181
|
+
G as RUN_SHELL_COMMAND_TOOL,
|
|
182
|
+
S as SEMANTIC_ESCALATIONS,
|
|
183
|
+
H as SEND_EMAIL_TOOL,
|
|
184
|
+
J as SHELL_TOOLS,
|
|
153
185
|
W as WEB_SEARCH_TOOL,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
186
|
+
K as WEB_TOOLS,
|
|
187
|
+
N as WRITE_FILE_TOOL,
|
|
188
|
+
O as abandonPlan,
|
|
189
|
+
Q as applyLayerSettings,
|
|
190
|
+
T as applyPendingAction,
|
|
191
|
+
U as assertPublicHttpUrl,
|
|
192
|
+
V as braveSearch,
|
|
193
|
+
X as buildClaudePrompt,
|
|
161
194
|
buildPlanFromTemplate,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
classifyRisk,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
195
|
+
Y as buildWhyChain,
|
|
196
|
+
Z as checkApiKeyFormat,
|
|
197
|
+
_ as classifyError,
|
|
198
|
+
$ as classifyRisk,
|
|
199
|
+
a1 as classifyToolYield,
|
|
200
|
+
a2 as classifyTurnIntent,
|
|
201
|
+
a3 as cleanApiKey,
|
|
202
|
+
a4 as computePlanPosition,
|
|
203
|
+
a5 as createPlanRecord,
|
|
204
|
+
a6 as createResendSender,
|
|
172
205
|
createScriptedLLMClient,
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
206
|
+
a7 as createSmtpSender,
|
|
207
|
+
a8 as decisionNote,
|
|
208
|
+
a9 as decompositionReframeEnabled,
|
|
209
|
+
aa as defaultExportFilename,
|
|
210
|
+
ac as detectHomogeneousBatchList,
|
|
211
|
+
ad as discardPendingAction,
|
|
212
|
+
ae as effectiveState,
|
|
213
|
+
ag as escalationEnabled,
|
|
214
|
+
ah as estimateCostUsd,
|
|
215
|
+
ai as executeActionTool,
|
|
216
|
+
aj as executeFileTool,
|
|
217
|
+
ak as executeShellTool,
|
|
218
|
+
al as executeWebTool,
|
|
219
|
+
am as explicitEnvOverride,
|
|
220
|
+
ao as findLayer,
|
|
221
|
+
aq as formatCostSummary,
|
|
222
|
+
ar as formatDoctorReport,
|
|
223
|
+
as as formatEmailApprovalReason,
|
|
224
|
+
at as formatGoalGraphState,
|
|
225
|
+
av as formatLayerListing,
|
|
226
|
+
aw as formatMemoryArchive,
|
|
227
|
+
ay as formatMemoryHistory,
|
|
228
|
+
az as formatMemoryInjection,
|
|
229
|
+
aB as formatMemoryStatus,
|
|
230
|
+
aC as formatMemorySummary,
|
|
231
|
+
aE as formatPlanProgress,
|
|
232
|
+
aF as formatSearchResults,
|
|
233
|
+
aI as formatTranscriptMarkdown,
|
|
234
|
+
aK as getProviderSetup,
|
|
235
|
+
aL as harnessGatePolicy,
|
|
236
|
+
aM as injectionDetectionEnabled,
|
|
237
|
+
aN as isAdaptivePolicyEnabled,
|
|
238
|
+
aP as isGoalGraphEnabled,
|
|
239
|
+
aQ as isGoalGraphSuggestEnabled,
|
|
240
|
+
aR as isLayerPolicyMode,
|
|
241
|
+
aS as isLikelyEmailAddress,
|
|
242
|
+
aT as isPlanGraphEnabled,
|
|
243
|
+
aU as isPolicyRecordingEnabled,
|
|
244
|
+
aW as isToggleable,
|
|
245
|
+
aX as lexicalActive,
|
|
246
|
+
aY as lexicalOffEnvValue,
|
|
247
|
+
aZ as listTemplateNames,
|
|
248
|
+
a_ as loadActivePlan,
|
|
249
|
+
a$ as loadPendingAction,
|
|
250
|
+
b0 as loadTemplate,
|
|
251
|
+
b1 as lowerConfidenceSourceLines,
|
|
252
|
+
b2 as matchTemplateIfConfident,
|
|
253
|
+
b4 as memoryStatusChecks,
|
|
254
|
+
b5 as nextPendingTask,
|
|
255
|
+
b6 as nodeDisplayName,
|
|
256
|
+
b7 as nodeToLayer,
|
|
257
|
+
b8 as normalizeAmbiguityGuardMode,
|
|
258
|
+
b9 as normalizeAskMode,
|
|
259
|
+
ba as normalizeGoalGraphMode,
|
|
260
|
+
bb as normalizeGoalGraphSuggestMode,
|
|
261
|
+
bc as normalizeLayerPolicyMode,
|
|
262
|
+
bd as normalizeOneLoopMode,
|
|
263
|
+
be as normalizePlanGraphMode,
|
|
264
|
+
bf as normalizePlanMode,
|
|
265
|
+
bh as parseClaudeCliOutput,
|
|
266
|
+
bk as pickTemplateForTask,
|
|
267
|
+
bl as planCompletionPct,
|
|
268
|
+
bm as planToSnapshot,
|
|
269
|
+
bp as resolveAmbiguityGuardMode,
|
|
270
|
+
bq as resolveAskMode,
|
|
271
|
+
br as resolveConfig,
|
|
272
|
+
bs as resolveEscalationPlan,
|
|
273
|
+
bt as resolveGoalGraphMode,
|
|
274
|
+
bu as resolveGoalGraphSuggestMode,
|
|
275
|
+
bv as resolveInWorkspace,
|
|
276
|
+
bw as resolveLayerPolicyMode,
|
|
277
|
+
bx as resolveLayerPolicyModeFromConfig,
|
|
278
|
+
by as resolveLexicalMode,
|
|
279
|
+
bz as resolveLexicalOff,
|
|
280
|
+
bA as resolveMemoryWriteMode,
|
|
281
|
+
bB as resolveOneLoopMode,
|
|
282
|
+
bC as resolvePlanGraphMode,
|
|
283
|
+
bD as resolvePlanMode,
|
|
284
|
+
bE as sanitizeLayerChoices,
|
|
285
|
+
bF as savePlan,
|
|
286
|
+
bH as stagePendingAction,
|
|
287
|
+
bI as stagedActionInput,
|
|
288
|
+
bK as stripMcpToolPrefix,
|
|
289
|
+
bM as summarizeSettledToolStep,
|
|
290
|
+
bN as summarizeToolStep,
|
|
291
|
+
bO as syncHarnessLexicalEnv,
|
|
292
|
+
bP as testApiKey,
|
|
293
|
+
bQ as turnPolicyBudget,
|
|
294
|
+
bR as updatePlanFromRun,
|
|
224
295
|
validateAskResponse,
|
|
225
|
-
|
|
296
|
+
bS as validateConfig,
|
|
297
|
+
bT as withLayerChoice
|
|
226
298
|
};
|
|
227
299
|
//# 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 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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"}
|
|
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'\nimport { parseModelJson } from './model-json.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 = parseModelJson(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 { classifyRiskLexical } 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, non-side-call\n * `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 * A handful of other calls are auxiliary semantic escalations (the change reviewer, the failure\n * matcher, the LLM injection classifier) that a tool-using turn can trigger without any script\n * author asking for them — with lexicalMode's default now `disabled`, the lexical checks these\n * sit on top of never short-circuit them, so they fire on every qualifying call, not just ones a\n * scenario is specifically testing. Answered from a safe, inert default (see SIDE_CALL_MARKERS)\n * so they never silently consume a `responses` slot meant for the real tool loop — the exact\n * silent-consumption bug that broke chat-ui's W5 proxy e2e test when the default flipped. Override\n * or add to this list via `sideResponses` for a test that specifically wants one of these calls to\n * answer something else (e.g. a genuine injection flag).\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 * Overrides/extends SIDE_CALL_MARKERS' default answers for a system-prompt substring — checked\n * before the built-in defaults, so a test can make the change reviewer/failure matcher/injection\n * classifier answer something other than \"nothing to see here\" without it consuming a\n * `responses` slot either way. `[marker, rawJsonContent]` pairs, same shape as the built-ins.\n */\n sideResponses?: Array<[string, string]>\n}\n\n/**\n * Known auxiliary semantic-escalation system prompts and their safe, inert default answer — see\n * ScriptedLLMClientScript's doc comment. Kept in sync by hand with each check's own system prompt\n * (review-checker.ts, failure-mode-matcher.ts, trust-tagging.ts, grounding-check.ts, task-completion-check.ts, constraint-check.ts, semantic-compaction.ts, source-reliability.ts, semantic-hypotheses.ts, episodic-digest.ts, memory-consolidation.ts, memory-reviewer.ts) since there's no shared constant\n * to import without a circular dependency back into this package's own public surface.\n */\nexport const SIDE_CALL_MARKERS: Array<[string, string]> = [\n ['You check whether a proposed action genuinely conflicts', '{\"conflict\":false}'],\n ['You match a set of observed symptoms against a curated library', 'null'],\n ['You are a security classifier analyzing untrusted external content', '{\"flagged\":false}'],\n ['You check whether an assistant\\'s reply is faithful to the raw results', '{\"verdict\":\"grounded\"}'],\n ['You judge whether an assistant actually accomplished a task', '{\"done\":true}'],\n ['You condense the earlier part of a conversation', '{\"summary\":\"\"}'],\n ['You judge whether an assistant\\'s reply violates constraints', '{\"violations\":[]}'],\n ['You weigh the reliability of the sources an assistant read', '{\"assessments\":[],\"weighed\":true}'],\n ['You propose competing explanations for an underdetermined request', '{\"hypotheses\":[]}'],\n ['You decide which explanations a set of new observations rules out', '{\"contradicted\":[]}'],\n ['You write a session handoff digest', '{\"digest\":null}'],\n ['You consolidate a small store of remembered facts about a user', '{\"proposals\":[]}'],\n ['You review what a user said across several turns', '{\"ops\":[]}'],\n ['You check proposed memory entries against what a user actually said', '{\"verdicts\":[]}'],\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 = ' 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 = classifyRiskLexical(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 isPlanQuestion: false,\n isUnderdetermined: 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 const system = messages.find((m) => m.role === 'system')?.content ?? ''\n const side = [...(this.script.sideResponses ?? []), ...SIDE_CALL_MARKERS].find(([marker]) => system.includes(marker))\n if (side) return { content: side[1] }\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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"}
|
|
@@ -7,6 +7,8 @@ export interface SelectPromptProps {
|
|
|
7
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
8
|
onSubmit: (key: string) => void;
|
|
9
9
|
isActive?: boolean;
|
|
10
|
+
/** Keys arriving within this many ms of the prompt appearing are ignored: a key typed ahead for the previous turn (an Enter, a 'y') must not answer a question the user has not seen yet. Default 0 (no guard). */
|
|
11
|
+
armDelayMs?: number;
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* Phase 7 of the internal plan: an arrow-key + numbered/shortcut-key
|
|
@@ -17,6 +17,8 @@ export interface TestInkInstance {
|
|
|
17
17
|
lastFrame: () => string | undefined;
|
|
18
18
|
/** See `TestStdout.fullOutput`'s doc comment — needed to assert on `<Static>` content. */
|
|
19
19
|
fullOutput: () => string;
|
|
20
|
+
/** How many writes (frames and control sequences) the render has produced so far — constant while the UI is idle. */
|
|
21
|
+
writeCount: () => number;
|
|
20
22
|
rerender: (tree: ReactElement) => void;
|
|
21
23
|
unmount: () => void;
|
|
22
24
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DEFAULT_LAYER_POLICY_MODE, LayerPolicyMode } from '@buildaharness/harness';
|
|
2
|
+
export type { LayerPolicyMode };
|
|
3
|
+
export { DEFAULT_LAYER_POLICY_MODE };
|
|
4
|
+
export declare function isLayerPolicyMode(v: unknown): v is LayerPolicyMode;
|
|
5
|
+
/** The mode a resolved config means; unset ⇒ the package default (static). */
|
|
6
|
+
export declare function resolveLayerPolicyModeFromConfig(mode: LayerPolicyMode | undefined): LayerPolicyMode;
|
|
7
|
+
/** The single place a resolved `config.layerPolicyMode` becomes "does adaptive behaviour execute?". Shadow does not. */
|
|
8
|
+
export declare function isAdaptivePolicyEnabled(mode: LayerPolicyMode | undefined): boolean;
|
|
9
|
+
/** True when decisions should be computed and recorded (shadow or adaptive). */
|
|
10
|
+
export declare function isPolicyRecordingEnabled(mode: LayerPolicyMode | undefined): boolean;
|
|
11
|
+
/** Unset/empty ⇒ default silently; an unrecognized value ⇒ default with a warning naming `varName`. */
|
|
12
|
+
export declare function normalizeLayerPolicyMode(raw: string | undefined, varName?: string): LayerPolicyMode;
|
|
13
|
+
export declare function resolveLayerPolicyMode(env: NodeJS.ProcessEnv): LayerPolicyMode;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { LayerPolicy, LayerPolicyMode, OptInLayer, PolicyBudget, PolicyRules, RunState, TurnSignals, TurnTier } from '@buildaharness/harness';
|
|
2
|
+
import { TurnIntentClassification } from './turn-intent-classifier.js';
|
|
3
|
+
/**
|
|
4
|
+
* The opt-in layers (default OFF, one `AUDIT_*` flag each) the operator has switched on. Each flag is still read
|
|
5
|
+
* where the layer runs; this only tells the policy which ones exist this turn so its record is true.
|
|
6
|
+
*/
|
|
7
|
+
export declare function enabledOptInLayers(env?: Record<string, string | undefined>): OptInLayer[];
|
|
8
|
+
/**
|
|
9
|
+
* Whether an opt-in layer runs this turn. Restrict-only (AL-1): the layer's own `AUDIT_*` flag must be on (`flagOn`) AND the
|
|
10
|
+
* plan must not have switched it `off` (`budget_exhausted`) — a plan can never turn a disabled layer on. Only an `adaptive`
|
|
11
|
+
* plan exists (`resolveOptInPlan`); with none, or a decision the plan lacks, or a plan that throws, the flag alone decides,
|
|
12
|
+
* i.e. today's behaviour.
|
|
13
|
+
*/
|
|
14
|
+
export declare function optInLayerEnabled(layer: OptInLayer, flagOn: boolean, plan: EscalationPlan | undefined): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The plan the opt-in layers are gated by, resolved once per turn right after classification — before the tool-less draft
|
|
17
|
+
* and the harness run, which is where the earliest opt-in call sits. Their decisions depend only on which are enabled and on
|
|
18
|
+
* the per-turn call budget (risk and posture), none of which needs the task graph, so the minimal signals here give the same
|
|
19
|
+
* answer the harness's own plan would. `undefined` unless `adaptive`: static and shadow execute today's behaviour.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveOptInPlan(mode: LayerPolicyMode, classification: TurnIntentClassification): EscalationPlan | undefined;
|
|
22
|
+
export declare const SEMANTIC_ESCALATIONS: readonly ["semantic_contradiction", "failure_match", "criterion_coverage", "change_review", "model_inferred_facts"];
|
|
23
|
+
export type SemanticEscalation = (typeof SEMANTIC_ESCALATIONS)[number];
|
|
24
|
+
export declare const ESCALATION_ENV: Readonly<Record<SemanticEscalation, string>>;
|
|
25
|
+
/** `true`/`false` when the operator set the env var explicitly; `undefined` when unset/empty. */
|
|
26
|
+
export declare function explicitEnvOverride(varName: string, env?: Record<string, string | undefined>): boolean | undefined;
|
|
27
|
+
export interface EscalationPlan {
|
|
28
|
+
mode: LayerPolicyMode;
|
|
29
|
+
tier: TurnTier;
|
|
30
|
+
/** The policy that executes (static under static/shadow). */
|
|
31
|
+
policy: LayerPolicy;
|
|
32
|
+
/** Under shadow only: what adaptive would have decided. Recorded, never acted on. */
|
|
33
|
+
shadow?: {
|
|
34
|
+
policy: LayerPolicy;
|
|
35
|
+
tier: TurnTier;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** AL9a: the per-turn LLM-call budget view for a turn's signals (risk- and posture-scaled). Static mode ignores it. */
|
|
39
|
+
export declare function turnPolicyBudget(signals: TurnSignals): PolicyBudget;
|
|
40
|
+
/** Resolves the executed policy for a turn. Never throws; any failure ⇒ today's static behaviour. */
|
|
41
|
+
export declare function resolveEscalationPlan(mode: LayerPolicyMode, signals: TurnSignals, state: RunState | undefined, rules?: PolicyRules, budget?: PolicyBudget): EscalationPlan;
|
|
42
|
+
/**
|
|
43
|
+
* Whether `layer`'s semantic (LLM) form runs this turn. `cheap` is a non-linguistic form, so for
|
|
44
|
+
* these layers only `full` runs the LLM hook. An explicit env override wins; a missing/broken plan
|
|
45
|
+
* keeps today's behaviour (ON).
|
|
46
|
+
*/
|
|
47
|
+
export declare function escalationEnabled(layer: SemanticEscalation, plan: EscalationPlan | undefined, env?: Record<string, string | undefined>): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Whether a semantic escalation hook is supplied to the harness at all: unless the operator's explicit `AUDIT_*` value switches
|
|
50
|
+
* it off. The plan no longer decides this — it is consulted per call (`HarnessRunOptions.hookEnabled`), so a hook the opening
|
|
51
|
+
* plan would have left off can still run once mid-turn evidence changes the plan. With a static plan the result is exactly
|
|
52
|
+
* what `escalationEnabled` gave (the plan says `full`, so only the env override mattered).
|
|
53
|
+
*/
|
|
54
|
+
export declare function escalationHookWired(layer: SemanticEscalation, env?: Record<string, string | undefined>): boolean;
|
|
55
|
+
/** The policy the harness runtime's ad hoc gates read: only an executed `adaptive` policy differs from today's. */
|
|
56
|
+
export declare function harnessGatePolicy(plan: EscalationPlan | undefined): LayerPolicy | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Decomposition reframe (`reframeTaskDescriptionWithLLM`). Precedence: explicit `AUDIT_DECOMPOSITION`
|
|
59
|
+
* env > the policy's decision > today's ON. `cheap` has no LLM-free form ⇒ off (only `full` reframes).
|
|
60
|
+
*/
|
|
61
|
+
export declare function decompositionReframeEnabled(plan: EscalationPlan | undefined, env?: Record<string, string | undefined>): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* LLM injection detection on fetched content (AL-1 security rule). Skippable ONLY on a
|
|
64
|
+
* non-linguistic proof of safety: no untrusted content in context, or no tool-capable next step.
|
|
65
|
+
* Anything the policy says short of `full` without such a proof is overridden back to ON — the
|
|
66
|
+
* check never trusts a message/LLM-derived signal, and the wiring re-verifies what
|
|
67
|
+
* `resolveLayerPolicy` already clamps so a hand-built plan cannot bypass it.
|
|
68
|
+
*/
|
|
69
|
+
export declare function injectionDetectionEnabled(plan: EscalationPlan | undefined, proof: {
|
|
70
|
+
untrustedContentInContext: boolean;
|
|
71
|
+
toolCapableNextStep: boolean;
|
|
72
|
+
}): boolean;
|
|
73
|
+
/** One-line reason a layer's decision is worth surfacing (escalated / skipped-with-reason), or `undefined` when routine. */
|
|
74
|
+
export declare function decisionNote(event: {
|
|
75
|
+
decision?: string;
|
|
76
|
+
trigger?: string;
|
|
77
|
+
}): string | undefined;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The user-facing on/off surface for reasoning layers, shared by the CLI (`/layers`) and the desktop/browser
|
|
3
|
+
* Settings screen. It is a thin layer over the existing `AUDIT_*` env flags, which every layer already reads in
|
|
4
|
+
* exactly one place: `applyLayerSettings` writes the persisted `config.layers` choices into an env object, so no
|
|
5
|
+
* layer needed a second code path. An env value the operator set themselves always wins and renders as pinned,
|
|
6
|
+
* the same precedence `explicitEnvOverride` already gives it.
|
|
7
|
+
*
|
|
8
|
+
* Floor layers (safety, approval, tool policy, verification, diagnostics) are listed but locked: there is no
|
|
9
|
+
* flag for them and the adaptive policy's types make them un-decidable on purpose.
|
|
10
|
+
*/
|
|
11
|
+
export type LayerGroup = 'escalation' | 'opt_in' | 'floor';
|
|
12
|
+
export interface LayerSetting {
|
|
13
|
+
id: string;
|
|
14
|
+
group: LayerGroup;
|
|
15
|
+
/** The env flag that carries the choice; absent for locked layers. */
|
|
16
|
+
flag?: string;
|
|
17
|
+
/** What the layer does when the user hasn't chosen. */
|
|
18
|
+
defaultOn: boolean;
|
|
19
|
+
summary: string;
|
|
20
|
+
/** Rough extra cost when it fires, in plain words. */
|
|
21
|
+
cost: string;
|
|
22
|
+
/** What the audit found, in plain words — the record, not a promise. */
|
|
23
|
+
evidence: string;
|
|
24
|
+
}
|
|
25
|
+
export declare const LAYER_SETTINGS: readonly LayerSetting[];
|
|
26
|
+
export type LayerChoices = Readonly<Record<string, boolean>>;
|
|
27
|
+
export declare function findLayer(id: string): LayerSetting | undefined;
|
|
28
|
+
/** The layers a user may change: everything except the locked floor. */
|
|
29
|
+
export declare function isToggleable(layer: LayerSetting): boolean;
|
|
30
|
+
type Env = Record<string, string | undefined>;
|
|
31
|
+
export interface AppliedLayers {
|
|
32
|
+
/** Layer ids whose env flag was set outside this module (shell env / build var), so the saved choice is ignored. */
|
|
33
|
+
pinned: Set<string>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Makes `env` reflect `choices`. Idempotent: a layer no longer in `choices` is restored to unset, and a flag that
|
|
37
|
+
* holds a value this module did not write is left alone and reported as pinned. Unknown ids and locked layers are ignored.
|
|
38
|
+
*/
|
|
39
|
+
export declare function applyLayerSettings(choices: LayerChoices | undefined, env: Env): AppliedLayers;
|
|
40
|
+
/** What a layer will do right now: the saved choice, else its default; a pinned layer reports its env value. */
|
|
41
|
+
export declare function effectiveState(layer: LayerSetting, choices: LayerChoices | undefined, pinned: ReadonlySet<string>, env: Env): boolean;
|
|
42
|
+
/** Returns the choices after setting one layer; throws on an unknown or locked id so a typo never silently persists. */
|
|
43
|
+
export declare function withLayerChoice(choices: LayerChoices | undefined, id: string, on: boolean | undefined): Record<string, boolean>;
|
|
44
|
+
export declare class LayerSettingError extends Error {
|
|
45
|
+
}
|
|
46
|
+
/** Drops anything in a persisted value that is not a known toggleable layer with a boolean, so a stale or hand-edited file can't smuggle junk in. */
|
|
47
|
+
export declare function sanitizeLayerChoices(raw: unknown): Record<string, boolean>;
|
|
48
|
+
export declare function formatLayerListing(choices: LayerChoices | undefined, pinned: ReadonlySet<string>, env: Env): string;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const LEXICAL_FAMILIES: readonly ["fact-markers", "coding-fact", "injection", "enumeration", "risk", "task-cancel", "plan-mode", "batch-list", "tool-yield", "template-keywords"];
|
|
2
|
+
export type LexicalFamily = (typeof LEXICAL_FAMILIES)[number];
|
|
3
|
+
/** The families `lexicalMode: 'disabled'` (the default) switches off: all of them. */
|
|
4
|
+
export declare const LEXICAL_CHECK_FAMILIES: readonly LexicalFamily[];
|
|
5
|
+
export type LexicalMode = 'enabled' | 'disabled';
|
|
6
|
+
export declare const DEFAULT_LEXICAL_MODE: LexicalMode;
|
|
7
|
+
type EnvLike = Record<string, string | undefined>;
|
|
8
|
+
export declare function isLexicalFamily(v: string): v is LexicalFamily;
|
|
9
|
+
/** Unset/empty ⇒ default silently; an unrecognized value ⇒ default with a warning. */
|
|
10
|
+
export declare function resolveLexicalMode(env?: EnvLike): LexicalMode;
|
|
11
|
+
/** Every family currently switched off: named in OFF, or not named in ON while the mode is disabled. */
|
|
12
|
+
export declare function resolveLexicalOff(env?: EnvLike): ReadonlySet<LexicalFamily>;
|
|
13
|
+
/** True only if this family is switched on. */
|
|
14
|
+
export declare function lexicalActive(family: LexicalFamily, env?: EnvLike): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The off families as a comma list, for handing to an out-of-process helper (the MCP server). Always
|
|
17
|
+
* pass it, even empty: the helper reads an unset variable as "every family off" (the default) and an
|
|
18
|
+
* empty one as "none off".
|
|
19
|
+
*/
|
|
20
|
+
export declare function lexicalOffEnvValue(env?: EnvLike): string;
|
|
21
|
+
/**
|
|
22
|
+
* Hands this mode to the harness package's own lexical switch (`setHarnessLexicalMode`), so
|
|
23
|
+
* `ASSISTANT_LEXICAL_MODE=enabled` turns the harness's checks back on too. Works in a browser as well
|
|
24
|
+
* as in Node (it sets no environment variable), and an explicit HARNESS_LEXICAL_MODE /
|
|
25
|
+
* HARNESS_LEXICAL_ON / HARNESS_LEXICAL_OFF still wins inside the harness. Called at the start of every
|
|
26
|
+
* harness run.
|
|
27
|
+
*/
|
|
28
|
+
export declare function syncHarnessLexicalEnv(env?: EnvLike): void;
|
|
29
|
+
export {};
|