@fusengine/harness 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/index.d.mts +2 -2
- package/dist/cache/index.mjs +3 -2
- package/dist/{cache-DbPSJ9bC.mjs → cache-BzbX-ztL.mjs} +1 -25
- package/dist/cli/bin.mjs +1 -1
- package/dist/{handle-BUyS6NU6.mjs → handle-o5np-3T_.mjs} +86 -8
- package/dist/{index-uEteukGR.d.mts → index-CQOPshSM.d.mts} +25 -2
- package/dist/{index-B3Ve_bBu.d.mts → index-DPkCX_AR.d.mts} +11 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +5 -4
- package/dist/policy/index.d.mts +2 -2
- package/dist/policy/index.mjs +3 -3
- package/dist/{policy-C_pXmeNB.mjs → policy-Djmqxow2.mjs} +13 -1
- package/dist/runtime/index.d.mts +23 -4
- package/dist/runtime/index.mjs +2 -2
- package/dist/store-BlaEhjab.mjs +60 -0
- package/dist/{store-BWvwnnf6.mjs → store-gtbP-Bv9.mjs} +36 -10
- package/dist/tracking/index.d.mts +15 -4
- package/dist/tracking/index.mjs +2 -2
- package/dist/{apex-gGrHzvM2.mjs → verbosity-ZYT7tLCw.mjs} +29 -2
- package/package.json +1 -1
package/dist/cache/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { IndexSummary, compactMarkdown, extractText, jaccardSimilar, loadIndex, queryHash, summarizeIndex };
|
|
1
|
+
import { a as extractText, c as summarizeIndex, d as queryHash, i as mcpCacheKey, l as compactMarkdown, n as cachePath, o as IndexSummary, r as cacheStore, s as loadIndex, t as cacheLookup, u as jaccardSimilar } from "../index-DPkCX_AR.mjs";
|
|
2
|
+
export { IndexSummary, cacheLookup, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, queryHash, summarizeIndex };
|
package/dist/cache/index.mjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
|
|
1
|
+
import { a as queryHash, i as jaccardSimilar, n as summarizeIndex, r as compactMarkdown, t as loadIndex } from "../cache-BzbX-ztL.mjs";
|
|
2
|
+
import { a as extractText, i as mcpCacheKey, n as cachePath, r as cacheStore, t as cacheLookup } from "../store-BlaEhjab.mjs";
|
|
3
|
+
export { cacheLookup, cachePath, cacheStore, compactMarkdown, extractText, jaccardSimilar, loadIndex, mcpCacheKey, queryHash, summarizeIndex };
|
|
@@ -83,28 +83,4 @@ function summarizeIndex(index) {
|
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
//#endregion
|
|
86
|
-
|
|
87
|
-
const MAX_DEPTH = 5;
|
|
88
|
-
/**
|
|
89
|
-
* Extract usable markdown from an MCP `tool_response`: a string, a list of
|
|
90
|
-
* content blocks (non-text blocks skipped), or any JSON structure (fallback).
|
|
91
|
-
* Recurses up to depth 5 to guard against pathological/cyclic structures.
|
|
92
|
-
*/
|
|
93
|
-
function extractText(resp, depth = 0) {
|
|
94
|
-
if (depth >= MAX_DEPTH) return "";
|
|
95
|
-
if (typeof resp === "string") return resp;
|
|
96
|
-
if (Array.isArray(resp)) {
|
|
97
|
-
const parts = resp.filter((b) => typeof b === "object" && b !== null).filter((b) => b.type === "text").map((b) => b.text ?? "");
|
|
98
|
-
if (parts.length) return parts.join("\n\n");
|
|
99
|
-
const joined = resp.filter((b) => Array.isArray(b) || typeof b === "object" && b !== null).map((b) => extractText(b, depth + 1)).filter(Boolean).join("\n\n");
|
|
100
|
-
if (joined) return joined;
|
|
101
|
-
}
|
|
102
|
-
if (!resp) return "";
|
|
103
|
-
try {
|
|
104
|
-
return JSON.stringify(resp);
|
|
105
|
-
} catch {
|
|
106
|
-
return "";
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
//#endregion
|
|
110
|
-
export { jaccardSimilar as a, compactMarkdown as i, loadIndex as n, queryHash as o, summarizeIndex as r, extractText as t };
|
|
86
|
+
export { queryHash as a, jaccardSimilar as i, summarizeIndex as n, compactMarkdown as r, loadIndex as t };
|
package/dist/cli/bin.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { t as detectHarness } from "../harness-C8Nxxyn_.mjs";
|
|
3
3
|
import { n as stagedContent, r as stagedFiles, t as checkStaged } from "../run-D2na7Z2Z.mjs";
|
|
4
4
|
import { n as writeInitFile, t as initFor } from "../run-Cdp2Ef9B.mjs";
|
|
5
|
-
import { t as handleHook } from "../handle-
|
|
5
|
+
import { t as handleHook } from "../handle-o5np-3T_.mjs";
|
|
6
6
|
//#region src/cli/bin.ts
|
|
7
7
|
/**
|
|
8
8
|
* harness — CLI for @fusengine/harness.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { O as detectFramework, t as evaluate } from "./evaluate-DkTgwHNh.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { n as capVerbosity, o as evaluateApex } from "./verbosity-ZYT7tLCw.mjs";
|
|
3
3
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
4
|
+
import { a as extractText, r as cacheStore, t as cacheLookup } from "./store-BlaEhjab.mjs";
|
|
4
5
|
import { t as loadRefs } from "./loader-BephwI8n.mjs";
|
|
5
|
-
import { a as recordAgent,
|
|
6
|
+
import { a as recordAgent, c as recordRefRead, l as recordTrivialEdit, n as saveTrack, r as agentsFresh, s as recordDoc, t as loadTrack, u as trivialCount } from "./store-gtbP-Bv9.mjs";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
import { tmpdir } from "node:os";
|
|
8
9
|
//#region src/runtime/activity.ts
|
|
@@ -49,10 +50,12 @@ function activityFor(event) {
|
|
|
49
50
|
const REQUIRED_AGENTS = ["explore-codebase", "research-expert"];
|
|
50
51
|
/** Default freshness window for {@link REQUIRED_AGENTS} (4 min, the APEX TTL). */
|
|
51
52
|
const DEFAULT_WINDOW_MS = 24e4;
|
|
53
|
+
/** Trivial edits allowed within the window before the full APEX gates apply. */
|
|
54
|
+
const TRIVIAL_BUDGET = 4;
|
|
52
55
|
/**
|
|
53
|
-
* Full gate: the stateless guards (file-size, git) first, then
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
+
* Full gate: the stateless guards (file-size, git, security...) first, then a
|
|
57
|
+
* trivial-edit fast path, then the stateful APEX gates fed from the session
|
|
58
|
+
* track. Returns the first blocking prompt, or null to allow.
|
|
56
59
|
*/
|
|
57
60
|
async function gate(input) {
|
|
58
61
|
const quick = evaluate({
|
|
@@ -63,7 +66,13 @@ async function gate(input) {
|
|
|
63
66
|
});
|
|
64
67
|
if (quick.decision !== "allow" && quick.prompt) return quick.prompt;
|
|
65
68
|
if (!input.filePath) return null;
|
|
69
|
+
const window = input.windowMs ?? 24e4;
|
|
66
70
|
const track = await loadTrack(input.trackFile);
|
|
71
|
+
const lineCount = input.content === void 0 ? Number.POSITIVE_INFINITY : input.content.split("\n").length;
|
|
72
|
+
if (!input.isReplaceAll && lineCount < 5 && trivialCount(track, window, input.now) < 4) {
|
|
73
|
+
await saveTrack(input.trackFile, recordTrivialEdit(track, input.now, window, input.now));
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
67
76
|
return evaluateApex({
|
|
68
77
|
sessionId: input.sessionId,
|
|
69
78
|
framework: input.framework,
|
|
@@ -72,8 +81,68 @@ async function gate(input) {
|
|
|
72
81
|
authorizations: track.authorizations,
|
|
73
82
|
refs: input.refs,
|
|
74
83
|
refsRead: track.refsRead,
|
|
75
|
-
agentsFresh: agentsFresh(track, [...REQUIRED_AGENTS],
|
|
84
|
+
agentsFresh: agentsFresh(track, [...REQUIRED_AGENTS], window, input.now),
|
|
85
|
+
brainstormRequired: track.brainstormRequired,
|
|
86
|
+
brainstormFresh: agentsFresh(track, ["brainstorming"], window, input.now)
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/runtime/mcp.ts
|
|
91
|
+
/** Default freshness for cached MCP/WebFetch results (48h). */
|
|
92
|
+
const MCP_TTL_MS = 1728e5;
|
|
93
|
+
/** MCP doc tools + WebFetch whose calls are cached / verbosity-capped. */
|
|
94
|
+
function isMcpTool(tool) {
|
|
95
|
+
return /context7|exa|webfetch|web_fetch/i.test(tool) || tool === "WebFetch";
|
|
96
|
+
}
|
|
97
|
+
/** The query/url that keys the cache. */
|
|
98
|
+
function queryOf(input) {
|
|
99
|
+
const q = input.query ?? input.url ?? input.libraryId ?? "";
|
|
100
|
+
return typeof q === "string" ? q : JSON.stringify(q);
|
|
101
|
+
}
|
|
102
|
+
function denyWith(id, content) {
|
|
103
|
+
if (id === "claude-code" || id === "codex") return JSON.stringify({ hookSpecificOutput: {
|
|
104
|
+
hookEventName: "PreToolUse",
|
|
105
|
+
permissionDecision: "deny",
|
|
106
|
+
permissionDecisionReason: content
|
|
107
|
+
} });
|
|
108
|
+
if (id === "gemini-cli") return JSON.stringify({
|
|
109
|
+
decision: "deny",
|
|
110
|
+
reason: content
|
|
76
111
|
});
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
function mutateWith(id, input) {
|
|
115
|
+
if (id === "claude-code" || id === "codex") return JSON.stringify({ hookSpecificOutput: {
|
|
116
|
+
hookEventName: "PreToolUse",
|
|
117
|
+
permissionDecision: "allow",
|
|
118
|
+
updatedInput: input
|
|
119
|
+
} });
|
|
120
|
+
if (id === "gemini-cli") return JSON.stringify({ hookSpecificOutput: { tool_input: input } });
|
|
121
|
+
return "";
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Pre-event MCP interception: serve a fresh cache hit (deny + cached content),
|
|
125
|
+
* else cap exa verbosity (allow + mutated input), else null to allow normally.
|
|
126
|
+
* Harnesses without input-mutation/cache support fall through to null.
|
|
127
|
+
*/
|
|
128
|
+
function mcpPreIntercept(id, tool, input, dir, ttlMs, now) {
|
|
129
|
+
if (!isMcpTool(tool)) return null;
|
|
130
|
+
const cached = cacheLookup(dir, tool, queryOf(input), ttlMs, now);
|
|
131
|
+
if (cached) {
|
|
132
|
+
const served = denyWith(id, cached);
|
|
133
|
+
if (served) return served;
|
|
134
|
+
}
|
|
135
|
+
const capped = capVerbosity(tool, input);
|
|
136
|
+
if (capped) {
|
|
137
|
+
const mutated = mutateWith(id, capped);
|
|
138
|
+
if (mutated) return mutated;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
/** Post-event: store the MCP/WebFetch response (extracted to markdown) in the cache. */
|
|
143
|
+
function mcpPostStore(tool, input, response, dir) {
|
|
144
|
+
if (!isMcpTool(tool)) return;
|
|
145
|
+
cacheStore(dir, tool, queryOf(input), extractText(response));
|
|
77
146
|
}
|
|
78
147
|
//#endregion
|
|
79
148
|
//#region src/runtime/normalize.ts
|
|
@@ -187,9 +256,12 @@ function harnessTrackDir(id, projectRoot) {
|
|
|
187
256
|
*/
|
|
188
257
|
async function handleHook(id, payload, opts) {
|
|
189
258
|
const event = normalizeEvent(id, payload);
|
|
190
|
-
const
|
|
259
|
+
const dir = harnessTrackDir(id, opts.cwd);
|
|
260
|
+
const file = trackFile(event.sessionId, dir);
|
|
261
|
+
const mcpDir = join(dir, "mcp");
|
|
191
262
|
const framework = detectFramework(event.filePath ?? "", event.content ?? "");
|
|
192
263
|
if (event.phase === "post") {
|
|
264
|
+
mcpPostStore(event.tool, event.input, payload.tool_response ?? payload.tool_output, mcpDir);
|
|
193
265
|
const activity = activityFor({
|
|
194
266
|
tool: event.tool,
|
|
195
267
|
input: event.input,
|
|
@@ -203,6 +275,11 @@ async function handleHook(id, payload, opts) {
|
|
|
203
275
|
exit: 0
|
|
204
276
|
};
|
|
205
277
|
}
|
|
278
|
+
const intercept = mcpPreIntercept(id, event.tool, event.input, mcpDir, MCP_TTL_MS, opts.now);
|
|
279
|
+
if (intercept !== null) return {
|
|
280
|
+
stdout: intercept,
|
|
281
|
+
exit: 0
|
|
282
|
+
};
|
|
206
283
|
const prompt = await gate({
|
|
207
284
|
sessionId: event.sessionId,
|
|
208
285
|
framework,
|
|
@@ -211,6 +288,7 @@ async function handleHook(id, payload, opts) {
|
|
|
211
288
|
content: event.content,
|
|
212
289
|
command: event.command,
|
|
213
290
|
refs: opts.refsDir ? await loadRefs(opts.refsDir) : void 0,
|
|
291
|
+
isReplaceAll: event.input.replace_all === true,
|
|
214
292
|
now: opts.now,
|
|
215
293
|
trackFile: file
|
|
216
294
|
});
|
|
@@ -223,4 +301,4 @@ async function handleHook(id, payload, opts) {
|
|
|
223
301
|
};
|
|
224
302
|
}
|
|
225
303
|
//#endregion
|
|
226
|
-
export { trackFile as a,
|
|
304
|
+
export { trackFile as a, isMcpTool as c, queryOf as d, DEFAULT_WINDOW_MS as f, activityFor as g, gate as h, recordActivity as i, mcpPostStore as l, TRIVIAL_BUDGET as m, harnessTrackDir as n, normalizeEvent as o, REQUIRED_AGENTS as p, respond as r, MCP_TTL_MS as s, handleHook as t, mcpPreIntercept as u };
|
|
@@ -98,6 +98,10 @@ interface ApexContext {
|
|
|
98
98
|
refsRead?: string[];
|
|
99
99
|
/** Whether the required prior agents (explore + research) ran within the freshness window. */
|
|
100
100
|
agentsFresh?: boolean;
|
|
101
|
+
/** Whether brainstorming is required for this edit (creation intent on a new file). */
|
|
102
|
+
brainstormRequired?: boolean;
|
|
103
|
+
/** Whether the brainstorming agent ran within the window. */
|
|
104
|
+
brainstormFresh?: boolean;
|
|
101
105
|
}
|
|
102
106
|
/** A single APEX gate: returns a blocking {@link Prompt}, or null to pass. */
|
|
103
107
|
type ApexGate = (ctx: ApexContext) => Prompt | null;
|
|
@@ -107,7 +111,9 @@ declare const docConsultedGate: ApexGate;
|
|
|
107
111
|
declare const solidReadGate: ApexGate;
|
|
108
112
|
/** Gate: the required prior agents (explore + research) must have run within the window. */
|
|
109
113
|
declare const freshnessGate: ApexGate;
|
|
110
|
-
/**
|
|
114
|
+
/** Gate: brainstorming must precede creating new files when flagged. */
|
|
115
|
+
declare const brainstormGate: ApexGate;
|
|
116
|
+
/** Default APEX gate chain (brainstorm, freshness, docs, SOLID refs). */
|
|
111
117
|
declare const APEX_GATES: ReadonlyArray<ApexGate>;
|
|
112
118
|
/**
|
|
113
119
|
* Run the APEX gates (chain-of-responsibility): the first failing gate's prompt
|
|
@@ -184,4 +190,21 @@ declare const GUARDS: ReadonlyArray<Guard>;
|
|
|
184
190
|
/** Run the guard chain; the first firing guard's Prompt wins, else null. */
|
|
185
191
|
declare function runGuards(ctx: GuardContext): Prompt | null;
|
|
186
192
|
//#endregion
|
|
187
|
-
|
|
193
|
+
//#region src/policy/creation-intent.d.ts
|
|
194
|
+
/**
|
|
195
|
+
* True when a prompt expresses creation intent (a new feature/component) and is
|
|
196
|
+
* not a fix/refactor — the signal that brainstorming should precede creation.
|
|
197
|
+
* The harness calls this on UserPromptSubmit, then `recordBrainstormRequired`.
|
|
198
|
+
*/
|
|
199
|
+
declare function detectCreationIntent(prompt: string): boolean;
|
|
200
|
+
//#endregion
|
|
201
|
+
//#region src/policy/verbosity.d.ts
|
|
202
|
+
/** Max results an exa MCP call may request. */
|
|
203
|
+
declare const MAX_EXA_RESULTS = 3;
|
|
204
|
+
/**
|
|
205
|
+
* Cap an exa MCP call to {@link MAX_EXA_RESULTS} results. Returns the capped
|
|
206
|
+
* input (a mutation for the harness to apply) when a cap is needed, else null.
|
|
207
|
+
*/
|
|
208
|
+
declare function capVerbosity(tool: string, input: Record<string, unknown>): Record<string, unknown> | null;
|
|
209
|
+
//#endregion
|
|
210
|
+
export { evaluateApex as A, matchPatterns as B, Guard as C, ApexGate as D, ApexContext as E, evaluate as F, DEV_KEYWORDS as G, countLines as H, GIT_ASK as I, isApexCommand as J, ProjectType as K, GIT_BLOCKED as L, solidReadGate as M, PolicyContext as N, brainstormGate as O, PolicyResult as P, PROJECT_INSTALL as R, securityGuard as S, APEX_GATES as T, evaluateFileSize as U, FileSizeVerdict as V, detectFramework as W, bashWriteGuard as _, runGuards as a, ASK_PATTERNS as b, installGuard as c, SWIFT_PROTO_RE as d, TS_DECL_RE as f, CODE_REDIRECT as g, CODE_MUTATORS as h, GUARDS as i, freshnessGate as j, docConsultedGate as k, PHP_DECL_RE as l, ASK_WRITERS as m, capVerbosity as n, PROJECT_INSTALL_RE as o, interfaceSeparationGuard as p, detectProjectType as q, detectCreationIntent as r, SYSTEM_INSTALL_RE as s, MAX_EXA_RESULTS as t, PY_MODEL_RE as u, PROTECTED_FRAGMENTS as v, GuardContext as w, CRITICAL_PATTERNS as x, protectedPathGuard as y, SYSTEM_INSTALL as z };
|
|
@@ -27,4 +27,14 @@ declare function summarizeIndex(index: unknown[]): IndexSummary;
|
|
|
27
27
|
*/
|
|
28
28
|
declare function extractText(resp: unknown, depth?: number): string;
|
|
29
29
|
//#endregion
|
|
30
|
-
|
|
30
|
+
//#region src/cache/store.d.ts
|
|
31
|
+
/** Stable 16-char key from a tool name + query (FNV-1a, non-crypto). */
|
|
32
|
+
declare function mcpCacheKey(tool: string, query: string): string;
|
|
33
|
+
/** Path of a cache entry under `dir`. */
|
|
34
|
+
declare function cachePath(dir: string, tool: string, query: string): string;
|
|
35
|
+
/** Read a cached entry if it exists and is fresh (mtime within `ttlMs`), else null. */
|
|
36
|
+
declare function cacheLookup(dir: string, tool: string, query: string, ttlMs: number, now: number): string | null;
|
|
37
|
+
/** Store a cache entry (creates the dir; no-op on empty content). */
|
|
38
|
+
declare function cacheStore(dir: string, tool: string, query: string, content: string): void;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { extractText as a, summarizeIndex as c, queryHash as d, mcpCacheKey as i, compactMarkdown as l, cachePath as n, IndexSummary as o, cacheStore as r, loadIndex as s, cacheLookup as t, jaccardSimilar as u };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { n as PromptKind, r as formatPrompt, t as Prompt } from "./types-D56jSgD9.mjs";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as extractText, c as summarizeIndex, d as queryHash, i as mcpCacheKey, l as compactMarkdown, n as cachePath, o as IndexSummary, r as cacheStore, s as loadIndex, t as cacheLookup, u as jaccardSimilar } from "./index-DPkCX_AR.mjs";
|
|
3
3
|
import { a as DEFAULT_TTL_SEC, c as ttlLabel, i as splitTarget, l as parseEnvInt, n as MAX_LINES_ENV_KEY, o as TTL_ENV_KEY, r as resolveMaxLines, s as resolveTtlSec, t as DEFAULT_MAX_LINES } from "./index-B-z0CCiU.mjs";
|
|
4
4
|
import { a as detectHarness, i as HarnessVia, n as HarnessInfo, o as detectMode, r as HarnessMode, s as modeFor, t as HarnessId } from "./harness-DwJskkz_.mjs";
|
|
5
5
|
import { a as isDocConsulted, i as formatDocSatisfactionStatus, n as DocSatisfactionStatus, o as resolveSessions, r as formatDocDeny, t as AuthEntry } from "./doc-helpers-CG1nuf-c.mjs";
|
|
6
6
|
import { t as incrementTrivialEditCounter } from "./index-BOBXQ91y.mjs";
|
|
7
7
|
import { i as compactJson, n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./index-C1vLIMwN.mjs";
|
|
8
|
-
import { A as
|
|
8
|
+
import { A as evaluateApex, B as matchPatterns, C as Guard, D as ApexGate, E as ApexContext, F as evaluate, G as DEV_KEYWORDS, H as countLines, I as GIT_ASK, J as isApexCommand, K as ProjectType, L as GIT_BLOCKED, M as solidReadGate, N as PolicyContext, O as brainstormGate, P as PolicyResult, R as PROJECT_INSTALL, S as securityGuard, T as APEX_GATES, U as evaluateFileSize, V as FileSizeVerdict, W as detectFramework, _ as bashWriteGuard, a as runGuards, b as ASK_PATTERNS, c as installGuard, d as SWIFT_PROTO_RE, f as TS_DECL_RE, g as CODE_REDIRECT, h as CODE_MUTATORS, i as GUARDS, j as freshnessGate, k as docConsultedGate, l as PHP_DECL_RE, m as ASK_WRITERS, n as capVerbosity, o as PROJECT_INSTALL_RE, p as interfaceSeparationGuard, q as detectProjectType, r as detectCreationIntent, s as SYSTEM_INSTALL_RE, t as MAX_EXA_RESULTS, u as PY_MODEL_RE, v as PROTECTED_FRAGMENTS, w as GuardContext, x as CRITICAL_PATTERNS, y as protectedPathGuard, z as SYSTEM_INSTALL } from "./index-CQOPshSM.mjs";
|
|
9
9
|
import { n as RouteResult, r as ScoredRef, t as RefMeta } from "./types-CY5qT2X1.mjs";
|
|
10
10
|
import { a as ReminderState, c as setStateField, i as registryFile, l as stateFileFor, n as addRoot, o as nowStamp, r as readRoots, s as readState, t as ensureMemoryGitignore, u as throttleMs } from "./index-BEM-mQMC.mjs";
|
|
11
11
|
import { a as globToRe, i as scoreReferences, n as toRefMeta, o as parseFrontmatter, r as routeReferences, t as loadRefs } from "./index-hL_r6tlc.mjs";
|
|
12
12
|
import { a as taskStart, c as ensureStateDir, d as stateFilePath, f as acquireLock, i as taskCreate, l as loadState, n as ApexTaskFile, o as ApexState, r as taskComplete, s as apexStateDir, t as ApexTask, u as saveState } from "./index-CPoF_hLP.mjs";
|
|
13
13
|
import { _ as TIME_INTERVALS, a as formatCost, c as formatTokens, d as colors, f as progressiveColor, g as PROGRESS_CHARS, h as PROGRESS_BAR_DEFAULTS, i as formatBasename, l as ColorFn, m as GRADIENT_BLOCKS, n as generateGradientBar, o as formatPath, p as COLOR_THRESHOLDS, r as generateProgressBar, s as formatTimeLeft, t as ProgressBarOptions, u as Palette } from "./index-BWK8slRi.mjs";
|
|
14
|
-
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexState, ApexTask, ApexTaskFile, AuthEntry, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DocSatisfactionStatus, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, GUARDS, Guard, GuardContext, HarnessId, HarnessInfo, HarnessMode, HarnessVia, IndexSummary, MAX_LINES_ENV_KEY, PHP_DECL_RE, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, Palette, PolicyContext, PolicyResult, ProgressBarOptions, ProjectType, Prompt, PromptKind, RefMeta, ReminderState, RouteResult, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, ScoredRef, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, colors, compactJson, compactMarkdown, countLines, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadRefs, loadState, matchPatterns, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel };
|
|
14
|
+
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, ApexState, ApexTask, ApexTaskFile, AuthEntry, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, ColorFn, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, DocSatisfactionStatus, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, GUARDS, Guard, GuardContext, HarnessId, HarnessInfo, HarnessMode, HarnessVia, IndexSummary, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, PHP_DECL_RE, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, Palette, PolicyContext, PolicyResult, ProgressBarOptions, ProjectType, Prompt, PromptKind, RefMeta, ReminderState, RouteResult, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, ScoredRef, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, cacheLookup, cachePath, cacheStore, capVerbosity, colors, compactJson, compactMarkdown, countLines, detectCreationIntent, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel };
|
package/dist/index.mjs
CHANGED
|
@@ -3,16 +3,17 @@ import { i as ttlLabel, n as TTL_ENV_KEY, r as resolveTtlSec, t as DEFAULT_TTL_S
|
|
|
3
3
|
import { t as compactJson } from "./compact-json-DK2nX-MK.mjs";
|
|
4
4
|
import { n as projectRoot, r as projectRootOrNull, t as isCodeFile } from "./project-root-C4ks_q1G.mjs";
|
|
5
5
|
import { n as detectMode, r as modeFor, t as detectHarness } from "./harness-C8Nxxyn_.mjs";
|
|
6
|
-
import { n as
|
|
6
|
+
import { i as isApexCommand, n as DEV_KEYWORDS, r as detectProjectType, t as detectCreationIntent } from "./policy-Djmqxow2.mjs";
|
|
7
7
|
import { C as PROJECT_INSTALL, D as evaluateFileSize, E as countLines, O as detectFramework, S as GIT_BLOCKED, T as matchPatterns, _ as protectedPathGuard, a as SYSTEM_INSTALL_RE, b as securityGuard, c as PY_MODEL_RE, d as interfaceSeparationGuard, f as ASK_WRITERS, g as PROTECTED_FRAGMENTS, h as bashWriteGuard, i as PROJECT_INSTALL_RE, l as SWIFT_PROTO_RE, m as CODE_REDIRECT, n as GUARDS, o as installGuard, p as CODE_MUTATORS, r as runGuards, s as PHP_DECL_RE, t as evaluate, u as TS_DECL_RE, v as ASK_PATTERNS, w as SYSTEM_INSTALL, x as GIT_ASK, y as CRITICAL_PATTERNS } from "./evaluate-DkTgwHNh.mjs";
|
|
8
8
|
import { i as resolveSessions, n as formatDocSatisfactionStatus, r as isDocConsulted, t as formatDocDeny } from "./doc-helpers-Dd_x1-tZ.mjs";
|
|
9
9
|
import { i as parseFrontmatter, n as scoreReferences, r as globToRe, t as routeReferences } from "./router-Dj3AfgBE.mjs";
|
|
10
|
-
import { a as solidReadGate, i as
|
|
10
|
+
import { a as docConsultedGate, c as solidReadGate, i as brainstormGate, n as capVerbosity, o as evaluateApex, r as APEX_GATES, s as freshnessGate, t as MAX_EXA_RESULTS } from "./verbosity-ZYT7tLCw.mjs";
|
|
11
11
|
import { t as formatPrompt } from "./types-ernB1Dy3.mjs";
|
|
12
12
|
import { a as readState, c as throttleMs, i as nowStamp, l as ensureMemoryGitignore, n as readRoots, o as setStateField, r as registryFile, s as stateFileFor, t as addRoot } from "./memory-BVNt4Ary.mjs";
|
|
13
|
-
import { a as
|
|
13
|
+
import { a as queryHash, i as jaccardSimilar, n as summarizeIndex, r as compactMarkdown, t as loadIndex } from "./cache-BzbX-ztL.mjs";
|
|
14
|
+
import { a as extractText, i as mcpCacheKey, n as cachePath, r as cacheStore, t as cacheLookup } from "./store-BlaEhjab.mjs";
|
|
14
15
|
import { t as incrementTrivialEditCounter } from "./freshness-BK9Xg6oG.mjs";
|
|
15
16
|
import { n as toRefMeta, t as loadRefs } from "./loader-BephwI8n.mjs";
|
|
16
17
|
import { a as ensureStateDir, c as stateFilePath, i as apexStateDir, l as acquireLock, n as taskCreate, o as loadState, r as taskStart, s as saveState, t as taskComplete } from "./state-Bc4wdnCG.mjs";
|
|
17
18
|
import { a as formatPath, c as colors, d as GRADIENT_BLOCKS, f as PROGRESS_BAR_DEFAULTS, i as formatCost, l as progressiveColor, m as TIME_INTERVALS, n as generateProgressBar, o as formatTimeLeft, p as PROGRESS_CHARS, r as formatBasename, s as formatTokens, t as generateGradientBar, u as COLOR_THRESHOLDS } from "./statusline-D87eUNXl.mjs";
|
|
18
|
-
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, GUARDS, MAX_LINES_ENV_KEY, PHP_DECL_RE, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, colors, compactJson, compactMarkdown, countLines, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadRefs, loadState, matchPatterns, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel };
|
|
19
|
+
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, COLOR_THRESHOLDS, CRITICAL_PATTERNS, DEFAULT_MAX_LINES, DEFAULT_TTL_SEC, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GRADIENT_BLOCKS, GUARDS, MAX_EXA_RESULTS, MAX_LINES_ENV_KEY, PHP_DECL_RE, PROGRESS_BAR_DEFAULTS, PROGRESS_CHARS, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, TIME_INTERVALS, TS_DECL_RE, TTL_ENV_KEY, acquireLock, addRoot, apexStateDir, bashWriteGuard, brainstormGate, cacheLookup, cachePath, cacheStore, capVerbosity, colors, compactJson, compactMarkdown, countLines, detectCreationIntent, detectFramework, detectHarness, detectMode, detectProjectType, docConsultedGate, ensureMemoryGitignore, ensureStateDir, evaluate, evaluateApex, evaluateFileSize, extractText, formatBasename, formatCost, formatDocDeny, formatDocSatisfactionStatus, formatPath, formatPrompt, formatTimeLeft, formatTokens, freshnessGate, generateGradientBar, generateProgressBar, globToRe, incrementTrivialEditCounter, installGuard, interfaceSeparationGuard, isApexCommand, isCodeFile, isDocConsulted, jaccardSimilar, loadIndex, loadRefs, loadState, matchPatterns, mcpCacheKey, modeFor, nowStamp, parseEnvInt, parseFrontmatter, progressiveColor, projectRoot, projectRootOrNull, protectedPathGuard, queryHash, readRoots, readState, registryFile, resolveMaxLines, resolveSessions, resolveTtlSec, routeReferences, runGuards, saveState, scoreReferences, securityGuard, setStateField, solidReadGate, splitTarget, stateFileFor, stateFilePath, summarizeIndex, taskComplete, taskCreate, taskStart, throttleMs, toRefMeta, ttlLabel };
|
package/dist/policy/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GUARDS, Guard, GuardContext, PHP_DECL_RE, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, PolicyContext, PolicyResult, ProjectType, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, TS_DECL_RE, bashWriteGuard, countLines, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
|
|
1
|
+
import { A as evaluateApex, B as matchPatterns, C as Guard, D as ApexGate, E as ApexContext, F as evaluate, G as DEV_KEYWORDS, H as countLines, I as GIT_ASK, J as isApexCommand, K as ProjectType, L as GIT_BLOCKED, M as solidReadGate, N as PolicyContext, O as brainstormGate, P as PolicyResult, R as PROJECT_INSTALL, S as securityGuard, T as APEX_GATES, U as evaluateFileSize, V as FileSizeVerdict, W as detectFramework, _ as bashWriteGuard, a as runGuards, b as ASK_PATTERNS, c as installGuard, d as SWIFT_PROTO_RE, f as TS_DECL_RE, g as CODE_REDIRECT, h as CODE_MUTATORS, i as GUARDS, j as freshnessGate, k as docConsultedGate, l as PHP_DECL_RE, m as ASK_WRITERS, n as capVerbosity, o as PROJECT_INSTALL_RE, p as interfaceSeparationGuard, q as detectProjectType, r as detectCreationIntent, s as SYSTEM_INSTALL_RE, t as MAX_EXA_RESULTS, u as PY_MODEL_RE, v as PROTECTED_FRAGMENTS, w as GuardContext, x as CRITICAL_PATTERNS, y as protectedPathGuard, z as SYSTEM_INSTALL } from "../index-CQOPshSM.mjs";
|
|
2
|
+
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, ApexContext, ApexGate, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, FileSizeVerdict, GIT_ASK, GIT_BLOCKED, GUARDS, Guard, GuardContext, MAX_EXA_RESULTS, PHP_DECL_RE, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, PolicyContext, PolicyResult, ProjectType, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, TS_DECL_RE, bashWriteGuard, brainstormGate, capVerbosity, countLines, detectCreationIntent, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
|
package/dist/policy/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as
|
|
1
|
+
import { i as isApexCommand, n as DEV_KEYWORDS, r as detectProjectType, t as detectCreationIntent } from "../policy-Djmqxow2.mjs";
|
|
2
2
|
import { C as PROJECT_INSTALL, D as evaluateFileSize, E as countLines, O as detectFramework, S as GIT_BLOCKED, T as matchPatterns, _ as protectedPathGuard, a as SYSTEM_INSTALL_RE, b as securityGuard, c as PY_MODEL_RE, d as interfaceSeparationGuard, f as ASK_WRITERS, g as PROTECTED_FRAGMENTS, h as bashWriteGuard, i as PROJECT_INSTALL_RE, l as SWIFT_PROTO_RE, m as CODE_REDIRECT, n as GUARDS, o as installGuard, p as CODE_MUTATORS, r as runGuards, s as PHP_DECL_RE, t as evaluate, u as TS_DECL_RE, v as ASK_PATTERNS, w as SYSTEM_INSTALL, x as GIT_ASK, y as CRITICAL_PATTERNS } from "../evaluate-DkTgwHNh.mjs";
|
|
3
|
-
import { a as solidReadGate, i as
|
|
4
|
-
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GUARDS, PHP_DECL_RE, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, TS_DECL_RE, bashWriteGuard, countLines, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
|
|
3
|
+
import { a as docConsultedGate, c as solidReadGate, i as brainstormGate, n as capVerbosity, o as evaluateApex, r as APEX_GATES, s as freshnessGate, t as MAX_EXA_RESULTS } from "../verbosity-ZYT7tLCw.mjs";
|
|
4
|
+
export { APEX_GATES, ASK_PATTERNS, ASK_WRITERS, CODE_MUTATORS, CODE_REDIRECT, CRITICAL_PATTERNS, DEV_KEYWORDS, GIT_ASK, GIT_BLOCKED, GUARDS, MAX_EXA_RESULTS, PHP_DECL_RE, PROJECT_INSTALL, PROJECT_INSTALL_RE, PROTECTED_FRAGMENTS, PY_MODEL_RE, SWIFT_PROTO_RE, SYSTEM_INSTALL, SYSTEM_INSTALL_RE, TS_DECL_RE, bashWriteGuard, brainstormGate, capVerbosity, countLines, detectCreationIntent, detectFramework, detectProjectType, docConsultedGate, evaluate, evaluateApex, evaluateFileSize, freshnessGate, installGuard, interfaceSeparationGuard, isApexCommand, matchPatterns, protectedPathGuard, runGuards, securityGuard, solidReadGate };
|
|
@@ -30,4 +30,16 @@ function detectProjectType(dir) {
|
|
|
30
30
|
return "generic";
|
|
31
31
|
}
|
|
32
32
|
//#endregion
|
|
33
|
-
|
|
33
|
+
//#region src/policy/creation-intent.ts
|
|
34
|
+
const CREATE_RE = /\b(?:create|implement|add|build|new|feature|component|generate|make|develop|scaffold)\b/i;
|
|
35
|
+
const SKIP_RE = /\b(?:fix|bug|debug|update|refactor|rename|move|delete|remove|commit|push|edit|modify|change)\b/i;
|
|
36
|
+
/**
|
|
37
|
+
* True when a prompt expresses creation intent (a new feature/component) and is
|
|
38
|
+
* not a fix/refactor — the signal that brainstorming should precede creation.
|
|
39
|
+
* The harness calls this on UserPromptSubmit, then `recordBrainstormRequired`.
|
|
40
|
+
*/
|
|
41
|
+
function detectCreationIntent(prompt) {
|
|
42
|
+
return CREATE_RE.test(prompt) && !SKIP_RE.test(prompt);
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { isApexCommand as i, DEV_KEYWORDS as n, detectProjectType as r, detectCreationIntent as t };
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -56,6 +56,8 @@ declare function activityFor(event: ToolEvent): Activity | null;
|
|
|
56
56
|
declare const REQUIRED_AGENTS: ReadonlyArray<string>;
|
|
57
57
|
/** Default freshness window for {@link REQUIRED_AGENTS} (4 min, the APEX TTL). */
|
|
58
58
|
declare const DEFAULT_WINDOW_MS = 24e4;
|
|
59
|
+
/** Trivial edits allowed within the window before the full APEX gates apply. */
|
|
60
|
+
declare const TRIVIAL_BUDGET = 4;
|
|
59
61
|
/** A tool-use to gate, plus the session pointers needed for the stateful gates. */
|
|
60
62
|
interface GateInput {
|
|
61
63
|
sessionId: string;
|
|
@@ -68,11 +70,12 @@ interface GateInput {
|
|
|
68
70
|
now: number;
|
|
69
71
|
trackFile: string;
|
|
70
72
|
windowMs?: number;
|
|
73
|
+
isReplaceAll?: boolean;
|
|
71
74
|
}
|
|
72
75
|
/**
|
|
73
|
-
* Full gate: the stateless guards (file-size, git) first, then
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
+
* Full gate: the stateless guards (file-size, git, security...) first, then a
|
|
77
|
+
* trivial-edit fast path, then the stateful APEX gates fed from the session
|
|
78
|
+
* track. Returns the first blocking prompt, or null to allow.
|
|
76
79
|
*/
|
|
77
80
|
declare function gate(input: GateInput): Promise<Prompt | null>;
|
|
78
81
|
//#endregion
|
|
@@ -102,6 +105,22 @@ declare function normalizeEvent(id: string, payload: Record<string, unknown>): N
|
|
|
102
105
|
*/
|
|
103
106
|
declare function respond(id: string, prompt: Prompt): string;
|
|
104
107
|
//#endregion
|
|
108
|
+
//#region src/runtime/mcp.d.ts
|
|
109
|
+
/** Default freshness for cached MCP/WebFetch results (48h). */
|
|
110
|
+
declare const MCP_TTL_MS = 1728e5;
|
|
111
|
+
/** MCP doc tools + WebFetch whose calls are cached / verbosity-capped. */
|
|
112
|
+
declare function isMcpTool(tool: string): boolean;
|
|
113
|
+
/** The query/url that keys the cache. */
|
|
114
|
+
declare function queryOf(input: Record<string, unknown>): string;
|
|
115
|
+
/**
|
|
116
|
+
* Pre-event MCP interception: serve a fresh cache hit (deny + cached content),
|
|
117
|
+
* else cap exa verbosity (allow + mutated input), else null to allow normally.
|
|
118
|
+
* Harnesses without input-mutation/cache support fall through to null.
|
|
119
|
+
*/
|
|
120
|
+
declare function mcpPreIntercept(id: string, tool: string, input: Record<string, unknown>, dir: string, ttlMs: number, now: number): string | null;
|
|
121
|
+
/** Post-event: store the MCP/WebFetch response (extracted to markdown) in the cache. */
|
|
122
|
+
declare function mcpPostStore(tool: string, input: Record<string, unknown>, response: unknown, dir: string): void;
|
|
123
|
+
//#endregion
|
|
105
124
|
//#region src/runtime/handle.d.ts
|
|
106
125
|
/** Options for {@link handleHook} (caller supplies the clock + project root). */
|
|
107
126
|
interface HandleOptions {
|
|
@@ -123,4 +142,4 @@ interface HandleOutcome {
|
|
|
123
142
|
*/
|
|
124
143
|
declare function handleHook(id: string, payload: Record<string, unknown>, opts: HandleOptions): Promise<HandleOutcome>;
|
|
125
144
|
//#endregion
|
|
126
|
-
export { Activity, DEFAULT_WINDOW_MS, GateInput, HandleOptions, HandleOutcome, NormalizedEvent, REQUIRED_AGENTS, ToolEvent, activityFor, gate, handleHook, harnessTrackDir, normalizeEvent, recordActivity, respond, trackFile };
|
|
145
|
+
export { Activity, DEFAULT_WINDOW_MS, GateInput, HandleOptions, HandleOutcome, MCP_TTL_MS, NormalizedEvent, REQUIRED_AGENTS, TRIVIAL_BUDGET, ToolEvent, activityFor, gate, handleHook, harnessTrackDir, isMcpTool, mcpPostStore, mcpPreIntercept, normalizeEvent, queryOf, recordActivity, respond, trackFile };
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as trackFile, c as
|
|
2
|
-
export { DEFAULT_WINDOW_MS, REQUIRED_AGENTS, activityFor, gate, handleHook, harnessTrackDir, normalizeEvent, recordActivity, respond, trackFile };
|
|
1
|
+
import { a as trackFile, c as isMcpTool, d as queryOf, f as DEFAULT_WINDOW_MS, g as activityFor, h as gate, i as recordActivity, l as mcpPostStore, m as TRIVIAL_BUDGET, n as harnessTrackDir, o as normalizeEvent, p as REQUIRED_AGENTS, r as respond, s as MCP_TTL_MS, t as handleHook, u as mcpPreIntercept } from "../handle-o5np-3T_.mjs";
|
|
2
|
+
export { DEFAULT_WINDOW_MS, MCP_TTL_MS, REQUIRED_AGENTS, TRIVIAL_BUDGET, activityFor, gate, handleHook, harnessTrackDir, isMcpTool, mcpPostStore, mcpPreIntercept, normalizeEvent, queryOf, recordActivity, respond, trackFile };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
//#region src/cache/mcp-response.ts
|
|
4
|
+
const MAX_DEPTH = 5;
|
|
5
|
+
/**
|
|
6
|
+
* Extract usable markdown from an MCP `tool_response`: a string, a list of
|
|
7
|
+
* content blocks (non-text blocks skipped), or any JSON structure (fallback).
|
|
8
|
+
* Recurses up to depth 5 to guard against pathological/cyclic structures.
|
|
9
|
+
*/
|
|
10
|
+
function extractText(resp, depth = 0) {
|
|
11
|
+
if (depth >= MAX_DEPTH) return "";
|
|
12
|
+
if (typeof resp === "string") return resp;
|
|
13
|
+
if (Array.isArray(resp)) {
|
|
14
|
+
const parts = resp.filter((b) => typeof b === "object" && b !== null).filter((b) => b.type === "text").map((b) => b.text ?? "");
|
|
15
|
+
if (parts.length) return parts.join("\n\n");
|
|
16
|
+
const joined = resp.filter((b) => Array.isArray(b) || typeof b === "object" && b !== null).map((b) => extractText(b, depth + 1)).filter(Boolean).join("\n\n");
|
|
17
|
+
if (joined) return joined;
|
|
18
|
+
}
|
|
19
|
+
if (!resp) return "";
|
|
20
|
+
try {
|
|
21
|
+
return JSON.stringify(resp);
|
|
22
|
+
} catch {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/cache/store.ts
|
|
28
|
+
/** Stable 16-char key from a tool name + query (FNV-1a, non-crypto). */
|
|
29
|
+
function mcpCacheKey(tool, query) {
|
|
30
|
+
const s = `${tool}\n${query}`;
|
|
31
|
+
let h = 2166136261;
|
|
32
|
+
for (let i = 0; i < s.length; i++) {
|
|
33
|
+
h ^= s.charCodeAt(i);
|
|
34
|
+
h = Math.imul(h, 16777619);
|
|
35
|
+
}
|
|
36
|
+
return (h >>> 0).toString(16).padStart(8, "0") + (s.length >>> 0).toString(16).padStart(8, "0");
|
|
37
|
+
}
|
|
38
|
+
/** Path of a cache entry under `dir`. */
|
|
39
|
+
function cachePath(dir, tool, query) {
|
|
40
|
+
return join(dir, `${mcpCacheKey(tool, query)}.md`);
|
|
41
|
+
}
|
|
42
|
+
/** Read a cached entry if it exists and is fresh (mtime within `ttlMs`), else null. */
|
|
43
|
+
function cacheLookup(dir, tool, query, ttlMs, now) {
|
|
44
|
+
const path = cachePath(dir, tool, query);
|
|
45
|
+
try {
|
|
46
|
+
if (!existsSync(path) || now - statSync(path).mtimeMs > ttlMs) return null;
|
|
47
|
+
return readFileSync(path, "utf8");
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Store a cache entry (creates the dir; no-op on empty content). */
|
|
53
|
+
function cacheStore(dir, tool, query, content) {
|
|
54
|
+
if (!content) return;
|
|
55
|
+
const path = cachePath(dir, tool, query);
|
|
56
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
57
|
+
writeFileSync(path, content);
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { extractText as a, mcpCacheKey as i, cachePath as n, cacheStore as r, cacheLookup as t };
|
|
@@ -5,7 +5,8 @@ function emptyTrack() {
|
|
|
5
5
|
return {
|
|
6
6
|
authorizations: {},
|
|
7
7
|
refsRead: [],
|
|
8
|
-
agents: []
|
|
8
|
+
agents: [],
|
|
9
|
+
trivialEdits: []
|
|
9
10
|
};
|
|
10
11
|
}
|
|
11
12
|
/** Record a doc consultation (Context7/Exa) for a framework in this session. Immutable. */
|
|
@@ -34,20 +35,45 @@ function recordRefRead(track, path) {
|
|
|
34
35
|
refsRead: [...track.refsRead, path]
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
|
-
/** Record an agent/tool call with a timestamp. Immutable. */
|
|
38
|
-
function recordAgent(track, name, ts) {
|
|
38
|
+
/** Record an agent/tool call with a timestamp + optional quality. Immutable. */
|
|
39
|
+
function recordAgent(track, name, ts, quality) {
|
|
40
|
+
const entry = quality ? {
|
|
41
|
+
name,
|
|
42
|
+
ts,
|
|
43
|
+
quality
|
|
44
|
+
} : {
|
|
45
|
+
name,
|
|
46
|
+
ts
|
|
47
|
+
};
|
|
39
48
|
return {
|
|
40
49
|
...track,
|
|
41
|
-
agents: [...track.agents,
|
|
42
|
-
name,
|
|
43
|
-
ts
|
|
44
|
-
}]
|
|
50
|
+
agents: [...track.agents, entry]
|
|
45
51
|
};
|
|
46
52
|
}
|
|
47
|
-
/** True when ALL of `names`
|
|
53
|
+
/** True when ALL of `names` ran within `windowMs` with non-insufficient quality. */
|
|
48
54
|
function agentsFresh(track, names, windowMs, now) {
|
|
49
55
|
const cutoff = now - windowMs;
|
|
50
|
-
return names.every((n) => track.agents.some((a) => a.name === n && a.ts > cutoff));
|
|
56
|
+
return names.every((n) => track.agents.some((a) => a.name === n && a.ts > cutoff && a.quality !== "insufficient"));
|
|
57
|
+
}
|
|
58
|
+
/** Record a trivial edit timestamp (sliding window; old evicted). Immutable. */
|
|
59
|
+
function recordTrivialEdit(track, ts, windowMs, now) {
|
|
60
|
+
const cutoff = now - windowMs;
|
|
61
|
+
return {
|
|
62
|
+
...track,
|
|
63
|
+
trivialEdits: [...(track.trivialEdits ?? []).filter((t) => t > cutoff), ts]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Count trivial edits within the sliding window. */
|
|
67
|
+
function trivialCount(track, windowMs, now) {
|
|
68
|
+
const cutoff = now - windowMs;
|
|
69
|
+
return (track.trivialEdits ?? []).filter((t) => t > cutoff).length;
|
|
70
|
+
}
|
|
71
|
+
/** Set the brainstorm-required flag (from creation-intent detection). Immutable. */
|
|
72
|
+
function recordBrainstormRequired(track, required) {
|
|
73
|
+
return {
|
|
74
|
+
...track,
|
|
75
|
+
brainstormRequired: required
|
|
76
|
+
};
|
|
51
77
|
}
|
|
52
78
|
//#endregion
|
|
53
79
|
//#region src/tracking/store.ts
|
|
@@ -60,4 +86,4 @@ async function saveTrack(file, track) {
|
|
|
60
86
|
await writeJsonFile(file, track);
|
|
61
87
|
}
|
|
62
88
|
//#endregion
|
|
63
|
-
export { recordAgent as a, emptyTrack as i, saveTrack as n,
|
|
89
|
+
export { recordAgent as a, recordRefRead as c, emptyTrack as i, recordTrivialEdit as l, saveTrack as n, recordBrainstormRequired as o, agentsFresh as r, recordDoc as s, loadTrack as t, trivialCount as u };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { t as AuthEntry } from "../doc-helpers-CG1nuf-c.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/tracking/session-state.d.ts
|
|
4
|
+
/** Quality of a recorded agent call (the freshness gate ignores insufficient ones). */
|
|
5
|
+
type AgentQuality = "sufficient" | "insufficient";
|
|
4
6
|
/** Recorded session activity that feeds the APEX gates. */
|
|
5
7
|
interface SessionTrack {
|
|
6
8
|
authorizations: Record<string, AuthEntry>;
|
|
@@ -8,7 +10,10 @@ interface SessionTrack {
|
|
|
8
10
|
agents: {
|
|
9
11
|
name: string;
|
|
10
12
|
ts: number;
|
|
13
|
+
quality?: AgentQuality;
|
|
11
14
|
}[];
|
|
15
|
+
trivialEdits: number[];
|
|
16
|
+
brainstormRequired?: boolean;
|
|
12
17
|
}
|
|
13
18
|
/** A fresh, empty track. */
|
|
14
19
|
declare function emptyTrack(): SessionTrack;
|
|
@@ -16,10 +21,16 @@ declare function emptyTrack(): SessionTrack;
|
|
|
16
21
|
declare function recordDoc(track: SessionTrack, framework: string, sessionId: string, source: string): SessionTrack;
|
|
17
22
|
/** Record that a SOLID reference file was read (deduped). Immutable. */
|
|
18
23
|
declare function recordRefRead(track: SessionTrack, path: string): SessionTrack;
|
|
19
|
-
/** Record an agent/tool call with a timestamp. Immutable. */
|
|
20
|
-
declare function recordAgent(track: SessionTrack, name: string, ts: number): SessionTrack;
|
|
21
|
-
/** True when ALL of `names`
|
|
24
|
+
/** Record an agent/tool call with a timestamp + optional quality. Immutable. */
|
|
25
|
+
declare function recordAgent(track: SessionTrack, name: string, ts: number, quality?: AgentQuality): SessionTrack;
|
|
26
|
+
/** True when ALL of `names` ran within `windowMs` with non-insufficient quality. */
|
|
22
27
|
declare function agentsFresh(track: SessionTrack, names: string[], windowMs: number, now: number): boolean;
|
|
28
|
+
/** Record a trivial edit timestamp (sliding window; old evicted). Immutable. */
|
|
29
|
+
declare function recordTrivialEdit(track: SessionTrack, ts: number, windowMs: number, now: number): SessionTrack;
|
|
30
|
+
/** Count trivial edits within the sliding window. */
|
|
31
|
+
declare function trivialCount(track: SessionTrack, windowMs: number, now: number): number;
|
|
32
|
+
/** Set the brainstorm-required flag (from creation-intent detection). Immutable. */
|
|
33
|
+
declare function recordBrainstormRequired(track: SessionTrack, required: boolean): SessionTrack;
|
|
23
34
|
//#endregion
|
|
24
35
|
//#region src/tracking/store.d.ts
|
|
25
36
|
/** Load a session track from a file (an empty track if absent/corrupt). */
|
|
@@ -27,4 +38,4 @@ declare function loadTrack(file: string): Promise<SessionTrack>;
|
|
|
27
38
|
/** Persist a session track. */
|
|
28
39
|
declare function saveTrack(file: string, track: SessionTrack): Promise<void>;
|
|
29
40
|
//#endregion
|
|
30
|
-
export { SessionTrack, agentsFresh, emptyTrack, loadTrack, recordAgent, recordDoc, recordRefRead, saveTrack };
|
|
41
|
+
export { AgentQuality, SessionTrack, agentsFresh, emptyTrack, loadTrack, recordAgent, recordBrainstormRequired, recordDoc, recordRefRead, recordTrivialEdit, saveTrack, trivialCount };
|
package/dist/tracking/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as recordAgent, i as emptyTrack, n as saveTrack, o as
|
|
2
|
-
export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordDoc, recordRefRead, saveTrack };
|
|
1
|
+
import { a as recordAgent, c as recordRefRead, i as emptyTrack, l as recordTrivialEdit, n as saveTrack, o as recordBrainstormRequired, r as agentsFresh, s as recordDoc, t as loadTrack, u as trivialCount } from "../store-gtbP-Bv9.mjs";
|
|
2
|
+
export { agentsFresh, emptyTrack, loadTrack, recordAgent, recordBrainstormRequired, recordDoc, recordRefRead, recordTrivialEdit, saveTrack, trivialCount };
|
|
@@ -30,8 +30,16 @@ const freshnessGate = (ctx) => ctx.agentsFresh === false ? {
|
|
|
30
30
|
reason: `Run explore-codebase and research-expert (within the freshness window) before editing ${ctx.framework}.`,
|
|
31
31
|
actions: ["Launch the explore-codebase agent", "Launch the research-expert agent"]
|
|
32
32
|
} : null;
|
|
33
|
-
/**
|
|
33
|
+
/** Gate: brainstorming must precede creating new files when flagged. */
|
|
34
|
+
const brainstormGate = (ctx) => ctx.brainstormRequired && ctx.brainstormFresh === false ? {
|
|
35
|
+
kind: "block",
|
|
36
|
+
title: "APEX: brainstorm first",
|
|
37
|
+
reason: `Creation intent detected — brainstorm before creating new ${ctx.framework} files.`,
|
|
38
|
+
actions: ["Launch the brainstorming agent"]
|
|
39
|
+
} : null;
|
|
40
|
+
/** Default APEX gate chain (brainstorm, freshness, docs, SOLID refs). */
|
|
34
41
|
const APEX_GATES = [
|
|
42
|
+
brainstormGate,
|
|
35
43
|
freshnessGate,
|
|
36
44
|
docConsultedGate,
|
|
37
45
|
solidReadGate
|
|
@@ -44,4 +52,23 @@ function evaluateApex(ctx, gates = APEX_GATES) {
|
|
|
44
52
|
return gates.reduce((hit, gate) => hit ?? gate(ctx), null);
|
|
45
53
|
}
|
|
46
54
|
//#endregion
|
|
47
|
-
|
|
55
|
+
//#region src/policy/verbosity.ts
|
|
56
|
+
/** Exa MCP tools whose result count is capped (Context7 has no verbosity knob). */
|
|
57
|
+
const EXA_TOOLS = /exa__web_search|exa__get_code_context|exa_web_search|exa_get_code_context/i;
|
|
58
|
+
/** Max results an exa MCP call may request. */
|
|
59
|
+
const MAX_EXA_RESULTS = 3;
|
|
60
|
+
/**
|
|
61
|
+
* Cap an exa MCP call to {@link MAX_EXA_RESULTS} results. Returns the capped
|
|
62
|
+
* input (a mutation for the harness to apply) when a cap is needed, else null.
|
|
63
|
+
*/
|
|
64
|
+
function capVerbosity(tool, input) {
|
|
65
|
+
if (!EXA_TOOLS.test(tool)) return null;
|
|
66
|
+
const n = input.numResults;
|
|
67
|
+
if (typeof n === "number" && n <= 3) return null;
|
|
68
|
+
return {
|
|
69
|
+
...input,
|
|
70
|
+
numResults: 3
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { docConsultedGate as a, solidReadGate as c, brainstormGate as i, capVerbosity as n, evaluateApex as o, APEX_GATES as r, freshnessGate as s, MAX_EXA_RESULTS as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fusengine/harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Harness-agnostic toolkit for AI coding agents: runtime harness detection (Claude Code, Codex, Cursor, Cline, Gemini, Aider...), pure policy core (env config, project/framework detection, SOLID/file-size limits, APEX freshness, guard patterns, portable prompts), cache, project memory, ref routing, state/locks, statusline, per-harness adapters (Claude/Cursor/Cline/Gemini) and a cli-mode harness-check binary. Bun-native, with a built dist for Node + bundlers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "src/index.ts",
|