specweave 2.1.0 → 2.2.1
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/.claude-plugin/marketplace.json +2 -2
- package/README.md +7 -6
- package/bin/specweave.js +28 -0
- package/dist/src/cli/commands/jev-helpers.d.ts +148 -0
- package/dist/src/cli/commands/jev-helpers.d.ts.map +1 -0
- package/dist/src/cli/commands/jev-helpers.js +582 -0
- package/dist/src/cli/commands/jev-helpers.js.map +1 -0
- package/dist/src/cli/commands/jev.d.ts +21 -0
- package/dist/src/cli/commands/jev.d.ts.map +1 -0
- package/dist/src/cli/commands/jev.js +350 -0
- package/dist/src/cli/commands/jev.js.map +1 -0
- package/dist/src/cli/helpers/init/instruction-file-writer.d.ts +4 -0
- package/dist/src/cli/helpers/init/instruction-file-writer.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/instruction-file-writer.js +11 -6
- package/dist/src/cli/helpers/init/instruction-file-writer.js.map +1 -1
- package/dist/src/core/auto/completion-evaluator.d.ts +22 -1
- package/dist/src/core/auto/completion-evaluator.d.ts.map +1 -1
- package/dist/src/core/auto/completion-evaluator.js +99 -9
- package/dist/src/core/auto/completion-evaluator.js.map +1 -1
- package/dist/src/core/config/types.d.ts +39 -1
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js +1 -0
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts +28 -5
- package/dist/src/core/hooks/handlers/pre-tool-use.d.ts.map +1 -1
- package/dist/src/core/hooks/handlers/pre-tool-use.js +162 -7
- package/dist/src/core/hooks/handlers/pre-tool-use.js.map +1 -1
- package/dist/src/core/jev/browse.d.ts +56 -0
- package/dist/src/core/jev/browse.d.ts.map +1 -0
- package/dist/src/core/jev/browse.js +721 -0
- package/dist/src/core/jev/browse.js.map +1 -0
- package/dist/src/core/jev/client.d.ts +134 -0
- package/dist/src/core/jev/client.d.ts.map +1 -0
- package/dist/src/core/jev/client.js +329 -0
- package/dist/src/core/jev/client.js.map +1 -0
- package/dist/src/core/jev/config.d.ts +69 -0
- package/dist/src/core/jev/config.d.ts.map +1 -0
- package/dist/src/core/jev/config.js +181 -0
- package/dist/src/core/jev/config.js.map +1 -0
- package/dist/src/core/jev/decide.d.ts +162 -0
- package/dist/src/core/jev/decide.d.ts.map +1 -0
- package/dist/src/core/jev/decide.js +333 -0
- package/dist/src/core/jev/decide.js.map +1 -0
- package/dist/src/core/jev/index.d.ts +20 -0
- package/dist/src/core/jev/index.d.ts.map +1 -0
- package/dist/src/core/jev/index.js +20 -0
- package/dist/src/core/jev/index.js.map +1 -0
- package/dist/src/core/jev/questions.d.ts +56 -0
- package/dist/src/core/jev/questions.d.ts.map +1 -0
- package/dist/src/core/jev/questions.js +159 -0
- package/dist/src/core/jev/questions.js.map +1 -0
- package/dist/src/core/jev/redact.d.ts +44 -0
- package/dist/src/core/jev/redact.d.ts.map +1 -0
- package/dist/src/core/jev/redact.js +102 -0
- package/dist/src/core/jev/redact.js.map +1 -0
- package/dist/src/core/jev/usage.d.ts +38 -0
- package/dist/src/core/jev/usage.d.ts.map +1 -0
- package/dist/src/core/jev/usage.js +70 -0
- package/dist/src/core/jev/usage.js.map +1 -0
- package/dist/src/utils/model-selection.d.ts +49 -0
- package/dist/src/utils/model-selection.d.ts.map +1 -1
- package/dist/src/utils/model-selection.js +70 -0
- package/dist/src/utils/model-selection.js.map +1 -1
- package/package.json +1 -1
- package/plugins/PLUGINS-INDEX.md +14 -4
- package/plugins/specweave/.claude-plugin/plugin.json +5 -3
- package/plugins/specweave/PLUGIN.md +16 -4
- package/plugins/specweave/hooks/run-worker.mjs +37 -2
- package/plugins/specweave/hooks/run.mjs +30 -0
- package/plugins/specweave/marketplace.json +6 -0
- package/plugins/specweave/skills/jev/SKILL.md +203 -0
- package/src/templates/AGENTS.md.template +22 -0
- package/src/templates/CLAUDE.md.template +22 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jev (TypeSafe System One) — configuration
|
|
3
|
+
*
|
|
4
|
+
* Precedence: JEV_DEFAULTS <- `.specweave/config.json` "jev" section <- environment.
|
|
5
|
+
*
|
|
6
|
+
* Consent is explicit: `jev.enabled` must be true in the project config (written by
|
|
7
|
+
* `specweave jev setup` after a live ping). `SPECWEAVE_JEV=0` disables Jev for a single
|
|
8
|
+
* process, `SPECWEAVE_JEV=1` enables it ad hoc.
|
|
9
|
+
*
|
|
10
|
+
* Nothing here ever reads, prints or stores an API key VALUE — only env var NAMES.
|
|
11
|
+
*
|
|
12
|
+
* @module core/jev/config
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { resolveEffectiveRoot } from '../../utils/find-project-root.js';
|
|
17
|
+
/** Built-in defaults. Jev is OFF until a project opts in. */
|
|
18
|
+
export const JEV_DEFAULTS = {
|
|
19
|
+
enabled: false,
|
|
20
|
+
provider: 'openrouter',
|
|
21
|
+
model: 'jev-1.13',
|
|
22
|
+
timeoutMs: 4000,
|
|
23
|
+
thresholds: { route: 0.7, guardDeny: 0.85, guardWarn: 0.5 },
|
|
24
|
+
guards: { bash: false },
|
|
25
|
+
modelRouting: true,
|
|
26
|
+
browse: { allowDomains: [], maxSteps: 20 },
|
|
27
|
+
};
|
|
28
|
+
/** System One endpoint per provider. */
|
|
29
|
+
export const JEV_PROVIDER_ENDPOINT = {
|
|
30
|
+
openrouter: 'https://openrouter.ai/api/v1/systemone',
|
|
31
|
+
typesafe: 'https://api.typesafe.ai/v1/systemone',
|
|
32
|
+
};
|
|
33
|
+
/** Default model id per provider (used when `model` is not set anywhere). */
|
|
34
|
+
export const JEV_PROVIDER_MODEL = {
|
|
35
|
+
openrouter: 'jev-1.13',
|
|
36
|
+
typesafe: 'jev-latest',
|
|
37
|
+
};
|
|
38
|
+
/** Default API key environment variable NAME per provider. */
|
|
39
|
+
export const JEV_PROVIDER_KEY_ENV = {
|
|
40
|
+
openrouter: 'OPENROUTER_API_KEY',
|
|
41
|
+
typesafe: 'TYPESAFE_API_KEY',
|
|
42
|
+
};
|
|
43
|
+
/** Provider-agnostic fallback key env var NAME. */
|
|
44
|
+
export const JEV_FALLBACK_KEY_ENV = 'JEV_API_KEY';
|
|
45
|
+
const PROVIDERS = ['openrouter', 'typesafe'];
|
|
46
|
+
function isProvider(value) {
|
|
47
|
+
return typeof value === 'string' && PROVIDERS.includes(value);
|
|
48
|
+
}
|
|
49
|
+
function num(value, fallback) {
|
|
50
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A probability threshold from repo-tracked config, forced into [0, 1].
|
|
54
|
+
*
|
|
55
|
+
* `.specweave/config.json` is a file anyone with commit rights can edit, and the
|
|
56
|
+
* guard verdict is a `>=` comparison: `guardDeny: 5` would be unreachable (every
|
|
57
|
+
* command allowed) and `guardDeny: -1` would deny everything. Clamping keeps a
|
|
58
|
+
* typo or a hostile edit inside the range the comparisons were designed for.
|
|
59
|
+
*/
|
|
60
|
+
function clamp01(value, fallback) {
|
|
61
|
+
const n = num(value, fallback);
|
|
62
|
+
return Math.min(1, Math.max(0, n));
|
|
63
|
+
}
|
|
64
|
+
function bool(value, fallback) {
|
|
65
|
+
return typeof value === 'boolean' ? value : fallback;
|
|
66
|
+
}
|
|
67
|
+
function str(value) {
|
|
68
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
69
|
+
}
|
|
70
|
+
function record(value) {
|
|
71
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
72
|
+
? value
|
|
73
|
+
: {};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Read the raw `jev` section from `<projectRoot>/.specweave/config.json`.
|
|
77
|
+
* Never throws: a missing or malformed config yields `{}`.
|
|
78
|
+
*/
|
|
79
|
+
function readJevSection(projectRoot) {
|
|
80
|
+
try {
|
|
81
|
+
const configPath = path.join(projectRoot, '.specweave', 'config.json');
|
|
82
|
+
if (!fs.existsSync(configPath))
|
|
83
|
+
return {};
|
|
84
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
85
|
+
return record(record(raw).jev);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Clamped, ordered thresholds.
|
|
93
|
+
*
|
|
94
|
+
* `guardDeny` is raised to `guardWarn` when it is configured lower: deny is the
|
|
95
|
+
* stricter verdict, so a deny bar *below* the warn bar would make a command warn
|
|
96
|
+
* at a probability that already denied it — an ordering the verdict table has no
|
|
97
|
+
* meaning for. Raising deny (rather than lowering warn) keeps the stricter of the
|
|
98
|
+
* two intents.
|
|
99
|
+
*/
|
|
100
|
+
function thresholds(file) {
|
|
101
|
+
const route = clamp01(file.route, JEV_DEFAULTS.thresholds.route);
|
|
102
|
+
const guardWarn = clamp01(file.guardWarn, JEV_DEFAULTS.thresholds.guardWarn);
|
|
103
|
+
const guardDeny = Math.max(clamp01(file.guardDeny, JEV_DEFAULTS.thresholds.guardDeny), guardWarn);
|
|
104
|
+
return { route, guardDeny, guardWarn };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Load the effective Jev config.
|
|
108
|
+
*
|
|
109
|
+
* @param projectRoot - Project root to read `.specweave/config.json` from. Used AS GIVEN
|
|
110
|
+
* (no upward walk) so callers stay deterministic; omit it to resolve
|
|
111
|
+
* the effective (umbrella-aware) root automatically.
|
|
112
|
+
* @param env - Environment to read overrides from (defaults to `process.env`).
|
|
113
|
+
*/
|
|
114
|
+
export function loadJevConfig(projectRoot, env = process.env) {
|
|
115
|
+
const root = projectRoot ?? resolveEffectiveRoot();
|
|
116
|
+
const file = readJevSection(root);
|
|
117
|
+
const fileThresholds = record(file.thresholds);
|
|
118
|
+
const fileGuards = record(file.guards);
|
|
119
|
+
const fileBrowse = record(file.browse);
|
|
120
|
+
// Provider: env wins, then file, then default.
|
|
121
|
+
const envProvider = str(env.SPECWEAVE_JEV_PROVIDER);
|
|
122
|
+
const provider = isProvider(envProvider)
|
|
123
|
+
? envProvider
|
|
124
|
+
: isProvider(file.provider)
|
|
125
|
+
? file.provider
|
|
126
|
+
: JEV_DEFAULTS.provider;
|
|
127
|
+
// Model: env wins, then file, then the provider's default model.
|
|
128
|
+
const model = str(env.SPECWEAVE_JEV_MODEL) ?? str(file.model) ?? JEV_PROVIDER_MODEL[provider];
|
|
129
|
+
// Enabled: SPECWEAVE_JEV=0 forces off, =1 forces on, otherwise the file decides.
|
|
130
|
+
let enabled = bool(file.enabled, JEV_DEFAULTS.enabled);
|
|
131
|
+
const flag = str(env.SPECWEAVE_JEV)?.toLowerCase();
|
|
132
|
+
if (flag === '0' || flag === 'false' || flag === 'off')
|
|
133
|
+
enabled = false;
|
|
134
|
+
else if (flag === '1' || flag === 'true' || flag === 'on')
|
|
135
|
+
enabled = true;
|
|
136
|
+
const allowDomains = Array.isArray(fileBrowse.allowDomains)
|
|
137
|
+
? fileBrowse.allowDomains.filter((d) => typeof d === 'string')
|
|
138
|
+
: [...JEV_DEFAULTS.browse.allowDomains];
|
|
139
|
+
return {
|
|
140
|
+
enabled,
|
|
141
|
+
provider,
|
|
142
|
+
model,
|
|
143
|
+
apiKeyEnv: str(file.apiKeyEnv),
|
|
144
|
+
timeoutMs: num(file.timeoutMs, JEV_DEFAULTS.timeoutMs),
|
|
145
|
+
thresholds: thresholds(fileThresholds),
|
|
146
|
+
guards: { bash: bool(fileGuards.bash, JEV_DEFAULTS.guards.bash) },
|
|
147
|
+
modelRouting: bool(file.modelRouting, JEV_DEFAULTS.modelRouting),
|
|
148
|
+
browse: {
|
|
149
|
+
allowDomains,
|
|
150
|
+
maxSteps: num(fileBrowse.maxSteps, JEV_DEFAULTS.browse.maxSteps),
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/** True when this project (and this process) may call Jev. */
|
|
155
|
+
export function isJevEnabled(projectRoot, env = process.env) {
|
|
156
|
+
return loadJevConfig(projectRoot, env).enabled;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Resolve the API key.
|
|
160
|
+
*
|
|
161
|
+
* Order: `cfg.apiKeyEnv` -> the provider's default var -> `JEV_API_KEY`.
|
|
162
|
+
*
|
|
163
|
+
* @returns `{ key, source }` where `source` is the env var NAME (safe to print), or null.
|
|
164
|
+
*/
|
|
165
|
+
export function resolveApiKey(cfg, env = process.env) {
|
|
166
|
+
const candidates = [cfg.apiKeyEnv, JEV_PROVIDER_KEY_ENV[cfg.provider], JEV_FALLBACK_KEY_ENV];
|
|
167
|
+
for (const name of candidates) {
|
|
168
|
+
if (!name)
|
|
169
|
+
continue;
|
|
170
|
+
const value = env[name];
|
|
171
|
+
if (typeof value === 'string' && value.trim()) {
|
|
172
|
+
return { key: value.trim(), source: name };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
/** Endpoint URL for a config's provider. */
|
|
178
|
+
export function jevEndpoint(cfg) {
|
|
179
|
+
return JEV_PROVIDER_ENDPOINT[cfg.provider] ?? JEV_PROVIDER_ENDPOINT.openrouter;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../src/core/jev/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jev decisions — typed helpers callers can use without handling HTTP.
|
|
3
|
+
*
|
|
4
|
+
* Every helper is "fail soft": when Jev is unavailable (disabled, no key, timeout,
|
|
5
|
+
* rate limit, schema drift) the helper returns `{ available: false, reason }` instead of
|
|
6
|
+
* throwing, so the caller keeps its existing heuristic. Nothing here ever throws.
|
|
7
|
+
*
|
|
8
|
+
* Fan-out rule: independent questions about the same state go in ONE request.
|
|
9
|
+
*
|
|
10
|
+
* @module core/jev/decide
|
|
11
|
+
*/
|
|
12
|
+
import { type JevClient } from './client.js';
|
|
13
|
+
import type { JevConfig } from './config.js';
|
|
14
|
+
import { type CommandScope, type RequestKind, type TaskComplexity, type TestFailureKind } from './questions.js';
|
|
15
|
+
/** Same three tiers as utils/model-selection; declared locally to avoid an import cycle. */
|
|
16
|
+
export type ModelTier = 'haiku' | 'sonnet' | 'opus';
|
|
17
|
+
export interface Unavailable {
|
|
18
|
+
available: false;
|
|
19
|
+
reason: string;
|
|
20
|
+
}
|
|
21
|
+
export interface RouteDecisionOk {
|
|
22
|
+
available: true;
|
|
23
|
+
skill: string | null;
|
|
24
|
+
skillConfidence: number;
|
|
25
|
+
skillProbabilities: Record<string, number>;
|
|
26
|
+
kind: RequestKind;
|
|
27
|
+
kindConfidence: number;
|
|
28
|
+
complexity: TaskComplexity;
|
|
29
|
+
complexityConfidence: number;
|
|
30
|
+
tier: ModelTier;
|
|
31
|
+
needsIncrement: number;
|
|
32
|
+
latencyMs: number;
|
|
33
|
+
cost?: number;
|
|
34
|
+
}
|
|
35
|
+
export type RouteDecision = RouteDecisionOk | Unavailable;
|
|
36
|
+
export interface TaskDecisionOk {
|
|
37
|
+
available: true;
|
|
38
|
+
complexity: TaskComplexity;
|
|
39
|
+
confidence: number;
|
|
40
|
+
tier: ModelTier;
|
|
41
|
+
needsIncrement: number;
|
|
42
|
+
latencyMs: number;
|
|
43
|
+
cost?: number;
|
|
44
|
+
}
|
|
45
|
+
export type TaskDecision = TaskDecisionOk | Unavailable;
|
|
46
|
+
export interface GuardDecisionOk {
|
|
47
|
+
available: true;
|
|
48
|
+
verdict: 'allow' | 'warn' | 'deny';
|
|
49
|
+
scope: CommandScope;
|
|
50
|
+
scopeConfidence: number;
|
|
51
|
+
destructive: number;
|
|
52
|
+
prefiltered: boolean;
|
|
53
|
+
probabilities: Record<string, number>;
|
|
54
|
+
reason: string;
|
|
55
|
+
latencyMs: number;
|
|
56
|
+
cost?: number;
|
|
57
|
+
}
|
|
58
|
+
export type GuardDecision = GuardDecisionOk | Unavailable;
|
|
59
|
+
export type AcDecision = {
|
|
60
|
+
id: string;
|
|
61
|
+
available: true;
|
|
62
|
+
satisfied: boolean;
|
|
63
|
+
probability: number;
|
|
64
|
+
} | {
|
|
65
|
+
id: string;
|
|
66
|
+
available: false;
|
|
67
|
+
reason: string;
|
|
68
|
+
};
|
|
69
|
+
export interface FailureDecisionOk {
|
|
70
|
+
available: true;
|
|
71
|
+
kind: TestFailureKind;
|
|
72
|
+
confidence: number;
|
|
73
|
+
probabilities: Record<string, number>;
|
|
74
|
+
latencyMs: number;
|
|
75
|
+
}
|
|
76
|
+
export type FailureDecision = FailureDecisionOk | Unavailable;
|
|
77
|
+
export interface ScreenDecisionOk {
|
|
78
|
+
available: true;
|
|
79
|
+
injection: number;
|
|
80
|
+
flagged: boolean;
|
|
81
|
+
latencyMs: number;
|
|
82
|
+
}
|
|
83
|
+
export type ScreenDecision = ScreenDecisionOk | Unavailable;
|
|
84
|
+
/** Hard cap on ACs judged in one fan-out request. */
|
|
85
|
+
export declare const MAX_ACS_PER_REQUEST = 40;
|
|
86
|
+
/**
|
|
87
|
+
* Complexity → model tier, with a confidence floor: an uncertain classification
|
|
88
|
+
* always escalates to opus rather than saving money on a task we misread.
|
|
89
|
+
*/
|
|
90
|
+
export declare function modelTierFor(complexity: TaskComplexity, confidence: number, threshold: number): ModelTier;
|
|
91
|
+
/**
|
|
92
|
+
* Turn the two guard answers into a verdict (spec 0878, "Verdict" bullet).
|
|
93
|
+
*
|
|
94
|
+
* Deny needs high confidence; warn is deliberately generous.
|
|
95
|
+
*
|
|
96
|
+
* The scope-confidence deny arm exists because a near-certain local wipe scores its
|
|
97
|
+
* *scope* high but its *destructiveness* only moderately: `rm -rf ~/Projects` comes back
|
|
98
|
+
* scope=local_irreversible at 0.99 with destructive=0.81, below guardDeny (0.85). Gating
|
|
99
|
+
* on the scope confidence catches it without lowering guardDeny, which would loosen every
|
|
100
|
+
* remote verdict too.
|
|
101
|
+
*
|
|
102
|
+
* `destructive_remote` is in the warn arm so a low-confidence remote-destruction reading
|
|
103
|
+
* degrades to warn rather than falling through to allow.
|
|
104
|
+
*/
|
|
105
|
+
export declare function guardVerdict(scope: string, scopeConf: number, destructive: number, t: JevConfig['thresholds'],
|
|
106
|
+
/** p(local_irreversible) + p(destructive_remote): the two scopes that both mean "data is gone". */
|
|
107
|
+
irreversibleMass?: number): 'allow' | 'warn' | 'deny';
|
|
108
|
+
/**
|
|
109
|
+
* Cheap regex gate in front of the guard: plainly read-only commands skip Jev entirely.
|
|
110
|
+
* Conservative by construction — anything unrecognised returns 'check'.
|
|
111
|
+
*/
|
|
112
|
+
export declare function prefilterCommand(command: string): 'skip' | 'check';
|
|
113
|
+
/**
|
|
114
|
+
* One fan-out request: which skill, what kind of request, how complex, increment needed.
|
|
115
|
+
*/
|
|
116
|
+
export declare function routePrompt(client: JevClient, prompt: string, ctx: {
|
|
117
|
+
skills: Array<{
|
|
118
|
+
name: string;
|
|
119
|
+
description: string;
|
|
120
|
+
}>;
|
|
121
|
+
activeIncrement?: string;
|
|
122
|
+
}): Promise<RouteDecision>;
|
|
123
|
+
/** How much reasoning one increment task needs → model tier. */
|
|
124
|
+
export declare function classifyTask(client: JevClient, task: {
|
|
125
|
+
id: string;
|
|
126
|
+
title: string;
|
|
127
|
+
body: string;
|
|
128
|
+
acs: string[];
|
|
129
|
+
approach?: string;
|
|
130
|
+
}): Promise<TaskDecision>;
|
|
131
|
+
/** Scope + destructiveness of a shell command, behind the regex prefilter. */
|
|
132
|
+
export declare function guardCommand(client: JevClient, cmd: {
|
|
133
|
+
command: string;
|
|
134
|
+
cwd?: string;
|
|
135
|
+
description?: string;
|
|
136
|
+
}): Promise<GuardDecision>;
|
|
137
|
+
/** One noul per acceptance criterion, all in one request (capped at 40). */
|
|
138
|
+
export declare function judgeAcs(client: JevClient, acs: Array<{
|
|
139
|
+
id: string;
|
|
140
|
+
text: string;
|
|
141
|
+
}>, evidence: {
|
|
142
|
+
diffSummary?: string;
|
|
143
|
+
testOutputTail?: string;
|
|
144
|
+
notes?: string;
|
|
145
|
+
}): Promise<AcDecision[]>;
|
|
146
|
+
/** Regression vs flake vs environment, from a failing test tail. */
|
|
147
|
+
export declare function classifyFailure(client: JevClient, outputTail: string, ctx?: {
|
|
148
|
+
changedFiles?: string[];
|
|
149
|
+
}): Promise<FailureDecision>;
|
|
150
|
+
/** Does untrusted text (issue body, page text, email) address an agent? */
|
|
151
|
+
export declare function screenText(client: JevClient, text: string, source?: string): Promise<ScreenDecision>;
|
|
152
|
+
/**
|
|
153
|
+
* Probability that a test/build output is a clean pass.
|
|
154
|
+
* `available: false` means the caller must fall back to its own parser — `passed` is 0
|
|
155
|
+
* in that case so treating "unknown" as "not passed" stays the safe default.
|
|
156
|
+
*/
|
|
157
|
+
export declare function testOutputPassed(client: JevClient, output: string, kind: 'tests' | 'build'): Promise<{
|
|
158
|
+
passed: number;
|
|
159
|
+
available: boolean;
|
|
160
|
+
reason?: string;
|
|
161
|
+
}>;
|
|
162
|
+
//# sourceMappingURL=decide.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decide.d.ts","sourceRoot":"","sources":["../../../../src/core/jev/decide.ts"],"names":[],"mappings":"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"}
|