@aiwayds/dsh-mcp-adapter 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -3
- package/README.zh.md +50 -4
- package/lib/index.d.ts +275 -22
- package/lib/index.js +612 -70
- package/lib/index.js.map +1 -1
- package/lib/jev/backends.d.ts +35 -0
- package/lib/jev/backends.js +48 -0
- package/lib/jev/backends.js.map +1 -0
- package/lib/jev/client.d.ts +133 -0
- package/lib/jev/client.js +288 -0
- package/lib/jev/client.js.map +1 -0
- package/lib/jev/config.d.ts +82 -0
- package/lib/jev/config.js +55 -0
- package/lib/jev/config.js.map +1 -0
- package/lib/jev/dual.d.ts +42 -0
- package/lib/jev/dual.js +42 -0
- package/lib/jev/dual.js.map +1 -0
- package/lib/jev/gate.d.ts +51 -0
- package/lib/jev/gate.js +141 -0
- package/lib/jev/gate.js.map +1 -0
- package/lib/jev/log.d.ts +93 -0
- package/lib/jev/log.js +138 -0
- package/lib/jev/log.js.map +1 -0
- package/lib/jev/seams.d.ts +118 -0
- package/lib/jev/seams.js +364 -0
- package/lib/jev/seams.js.map +1 -0
- package/lib/jev/thresholds.d.ts +37 -0
- package/lib/jev/thresholds.js +44 -0
- package/lib/jev/thresholds.js.map +1 -0
- package/lib/rank.d.ts +157 -0
- package/lib/rank.js +327 -0
- package/lib/rank.js.map +1 -0
- package/lib/usage.d.ts +232 -0
- package/lib/usage.js +362 -0
- package/lib/usage.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System One decision client — direct HTTP with zero dependencies beyond
|
|
3
|
+
* node:child_process and the global fetch (design 2026-09-25 §3; ADR
|
|
4
|
+
* proposal: direct HTTP over MCP). Ported from dsh-topics-memory
|
|
5
|
+
* `src/jev/client.ts`; the ONE structural change is the telemetry sink: the
|
|
6
|
+
* decisions.jsonl log is injected (see ./log.ts) instead of resolved from a
|
|
7
|
+
* paths module at call time.
|
|
8
|
+
*
|
|
9
|
+
* Semantics (§1/§3.2):
|
|
10
|
+
* - one AbortSignal.timeout per request, NO retry — the calling lane's next
|
|
11
|
+
* cadence is the natural retry (fail-open hard-coded, red line ②);
|
|
12
|
+
* - every failure returns as a structured {ok:false} result, never a throw;
|
|
13
|
+
* - the COMPLETE serialized body is gate-scanned before anything leaves the
|
|
14
|
+
* process (red line ③, design §3.3).
|
|
15
|
+
*
|
|
16
|
+
* @module jev/client
|
|
17
|
+
*/
|
|
18
|
+
import { execFile } from 'node:child_process';
|
|
19
|
+
import { promisify } from 'node:util';
|
|
20
|
+
import { resolveBackend } from "./backends.js";
|
|
21
|
+
import { currentSecrets, scanForSecrets } from "./gate.js";
|
|
22
|
+
/** Guard for non-positive/absent values from bare harnesses. */
|
|
23
|
+
const DEFAULT_TIMEOUT_MS = 3000;
|
|
24
|
+
function isPlainObject(v) {
|
|
25
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
26
|
+
}
|
|
27
|
+
/** 'svc' -> {service:'svc'}; 'svc:acct' -> {service:'svc', account:'acct'} */
|
|
28
|
+
export function parseKeychainSpec(spec) {
|
|
29
|
+
const i = spec.indexOf(':');
|
|
30
|
+
if (i === -1)
|
|
31
|
+
return { service: spec };
|
|
32
|
+
return { service: spec.slice(0, i), account: spec.slice(i + 1) };
|
|
33
|
+
}
|
|
34
|
+
const execFileAsync = promisify(execFile);
|
|
35
|
+
/**
|
|
36
|
+
* Budget for one `security find-generic-password` call. This lookup is awaited
|
|
37
|
+
* BEFORE the fetch, so it sits OUTSIDE the caller's jevTimeoutMs — and a
|
|
38
|
+
* locked keychain makes `security` block indefinitely (it waits for a UI
|
|
39
|
+
* unlock the user may never see), which parked the model-path seams outright
|
|
40
|
+
* and, on the background seam, held `suggestInFlight` true forever so every
|
|
41
|
+
* later 20-call crossing was silently swallowed. 3s is generous for a local
|
|
42
|
+
* `security` on a loaded machine and short enough that a locked keychain costs
|
|
43
|
+
* one batch, not the feature.
|
|
44
|
+
*
|
|
45
|
+
* A timeout is a MISSING KEY: it returns the same structured `missing_key`
|
|
46
|
+
* refusal as "no key at all" (fail-open, red line ②) and never throws. That is
|
|
47
|
+
* the same semantics the lookup already had for "entry not found" — the key
|
|
48
|
+
* is simply not available, and the lane's own fallback is the answer.
|
|
49
|
+
*/
|
|
50
|
+
export const KEYCHAIN_TIMEOUT_MS = 3000;
|
|
51
|
+
/** Default runner: `security find-generic-password … -w`; errors (missing
|
|
52
|
+
* entry, no binary on non-macOS, access denied) surface as rejections. The
|
|
53
|
+
* exec-level `timeout` SIGTERMs the child, so nothing is left running after
|
|
54
|
+
* the seam has moved on. */
|
|
55
|
+
const securityRunner = async (file, args) => {
|
|
56
|
+
return (await execFileAsync(file, args, { encoding: 'utf8', timeout: KEYCHAIN_TIMEOUT_MS }));
|
|
57
|
+
};
|
|
58
|
+
/** Runs `security find-generic-password -w`; null on any failure (silent).
|
|
59
|
+
*
|
|
60
|
+
* The seam that must not block is THIS one, so the deadline is enforced here
|
|
61
|
+
* rather than trusted to the transport: a runner that ignores its own bound
|
|
62
|
+
* (a future transport, a child that traps SIGTERM) still returns null.
|
|
63
|
+
* @param spec - keychain service[:account] to read.
|
|
64
|
+
* @param runner - injectable transport (tests never touch the real keychain).
|
|
65
|
+
* @param timeoutMs - deadline for one lookup; a non-positive/absent value
|
|
66
|
+
* falls back to {@link KEYCHAIN_TIMEOUT_MS} rather than disabling the bound.
|
|
67
|
+
*/
|
|
68
|
+
export async function keychainLookup(spec, runner = securityRunner, timeoutMs = KEYCHAIN_TIMEOUT_MS) {
|
|
69
|
+
const args = ['find-generic-password', '-s', spec.service];
|
|
70
|
+
if (spec.account !== undefined)
|
|
71
|
+
args.push('-a', spec.account);
|
|
72
|
+
const budget = Number(timeoutMs) > 0 ? Number(timeoutMs) : KEYCHAIN_TIMEOUT_MS;
|
|
73
|
+
let timer;
|
|
74
|
+
try {
|
|
75
|
+
// The loser's rejection is absorbed HERE: once the deadline wins, a late
|
|
76
|
+
// rejection must not surface as an unhandled rejection. The call itself
|
|
77
|
+
// stays inside the try, so a runner that throws SYNCHRONOUSLY is a lookup
|
|
78
|
+
// failure like any other rather than an escaping exception.
|
|
79
|
+
const attempt = runner('security', [...args, '-w']).then(result => ({ result }), () => undefined);
|
|
80
|
+
const deadline = new Promise(resolve => { timer = setTimeout(() => resolve(undefined), budget); });
|
|
81
|
+
const winner = await Promise.race([attempt, deadline]);
|
|
82
|
+
return winner?.result.stdout.trim() || null;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
if (timer !== undefined)
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolved-key memo, per process. `security` is a fork+exec per seam call:
|
|
94
|
+
* with zen the default backend (keychainByDefault) and no JEV_ZEN_API_KEY set,
|
|
95
|
+
* EVERY batch spawned one, so a locked keychain spawned a process per call
|
|
96
|
+
* too. Only a SUCCESSFUL keychain answer is memoized — a miss must stay a
|
|
97
|
+
* miss that is re-probed, so unlocking the keychain needs no restart. The
|
|
98
|
+
* trade-off this buys is that a key ROTATED in the keychain is picked up on
|
|
99
|
+
* the next dsh start, not sooner; the env path (the documented fix for a
|
|
100
|
+
* rotated key) is read on every call and is never memoized.
|
|
101
|
+
*/
|
|
102
|
+
const apiKeyMemo = new Map();
|
|
103
|
+
/** Resolve the API key for a backend: env var wins, then the keychain spec
|
|
104
|
+
* (explicit JEV_KEYCHAIN, or the backend default for zen only), memoized per
|
|
105
|
+
* backend name + resolved spec — JEV_KEYCHAIN is volatile, so repointing it
|
|
106
|
+
* must not be answered out of another service's memo entry. */
|
|
107
|
+
export async function resolveApiKey(backend, lookup = keychainLookup) {
|
|
108
|
+
const envKey = process.env[backend.keyEnv] ?? '';
|
|
109
|
+
if (envKey !== '')
|
|
110
|
+
return envKey;
|
|
111
|
+
const explicit = process.env.JEV_KEYCHAIN?.trim() ?? '';
|
|
112
|
+
const spec = explicit !== '' ? parseKeychainSpec(explicit) : backend.keychainByDefault ? { service: backend.keychainService } : null;
|
|
113
|
+
if (spec === null)
|
|
114
|
+
return '';
|
|
115
|
+
const memoKey = `${backend.name} ${spec.service} ${spec.account ?? ''}`;
|
|
116
|
+
const memoized = apiKeyMemo.get(memoKey);
|
|
117
|
+
if (memoized !== undefined)
|
|
118
|
+
return memoized;
|
|
119
|
+
const key = (await lookup(spec)) ?? '';
|
|
120
|
+
if (key !== '')
|
|
121
|
+
apiKeyMemo.set(memoKey, key);
|
|
122
|
+
return key;
|
|
123
|
+
}
|
|
124
|
+
function where(id) {
|
|
125
|
+
return `questions['${id}']`;
|
|
126
|
+
}
|
|
127
|
+
export function normalizeQuestions(questions, family) {
|
|
128
|
+
if (!isPlainObject(questions) || Object.keys(questions).length === 0) {
|
|
129
|
+
return { ok: false, message: "'questions' must be a non-empty object mapping question id -> {type, instructions, criteria?}." };
|
|
130
|
+
}
|
|
131
|
+
const systemone = family === 'systemone';
|
|
132
|
+
const out = {};
|
|
133
|
+
for (const [id, q] of Object.entries(questions)) {
|
|
134
|
+
if (!isPlainObject(q))
|
|
135
|
+
return { ok: false, message: `${where(id)} must be an object.` };
|
|
136
|
+
if (q.type !== 'noul' && q.type !== 'choice' && q.type !== 'score') {
|
|
137
|
+
return { ok: false, message: `${where(id)}.type must be 'noul' | 'choice' | 'score'.` };
|
|
138
|
+
}
|
|
139
|
+
if (typeof q.instructions !== 'string' || q.instructions === '') {
|
|
140
|
+
return { ok: false, message: `${where(id)}.instructions must be a non-empty string (the plugin always sends instructions).` };
|
|
141
|
+
}
|
|
142
|
+
const wire = { type: q.type, instructions: q.instructions };
|
|
143
|
+
if (q.type === 'noul') {
|
|
144
|
+
if (q.criteria === undefined) {
|
|
145
|
+
wire.criteria = { true: q.instructions, false: `NOT: ${q.instructions}` };
|
|
146
|
+
}
|
|
147
|
+
else if (isPlainObject(q.criteria) && q.criteria.true !== undefined && q.criteria.false !== undefined) {
|
|
148
|
+
if (!systemone && (typeof q.criteria.true !== 'string' || typeof q.criteria.false !== 'string')) {
|
|
149
|
+
return { ok: false, message: `${where(id)}: noul criteria must be the two-sided {true: string, false: string} on the decisions protocol.` };
|
|
150
|
+
}
|
|
151
|
+
wire.criteria = q.criteria;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
return { ok: false, message: `${where(id)}: noul criteria, when given, must be the two-sided {true: ..., false: ...}.` };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (q.type === 'choice') {
|
|
158
|
+
if (!isPlainObject(q.criteria)) {
|
|
159
|
+
return { ok: false, message: `${where(id)}: choice criteria must be an OBJECT mapping option -> description.` };
|
|
160
|
+
}
|
|
161
|
+
wire.criteria = q.criteria;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// score: 2-10 ordered levels; strings everywhere (plugin convention);
|
|
165
|
+
// the systemone protocol additionally tolerates structured levels.
|
|
166
|
+
if (!Array.isArray(q.criteria) || q.criteria.length < 2 || q.criteria.length > 10) {
|
|
167
|
+
return { ok: false, message: `${where(id)}: score criteria must be an ARRAY of 2-10 level descriptions.` };
|
|
168
|
+
}
|
|
169
|
+
const stringLevels = q.criteria.every((l) => typeof l === 'string' && l.length > 0);
|
|
170
|
+
if (!stringLevels && !systemone) {
|
|
171
|
+
return { ok: false, message: `${where(id)}: score levels must be non-empty strings on the decisions protocol.` };
|
|
172
|
+
}
|
|
173
|
+
wire.criteria = q.criteria;
|
|
174
|
+
}
|
|
175
|
+
out[id] = wire;
|
|
176
|
+
}
|
|
177
|
+
return { ok: true, questions: out };
|
|
178
|
+
}
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
/** Ask the System One decision model. One request for the whole batch;
|
|
181
|
+
* resolves to a structured failure (never throws) so the calling lane can
|
|
182
|
+
* fail open. Writes the decisions.jsonl call-layer row on EVERY path. */
|
|
183
|
+
export async function jevAsk(args) {
|
|
184
|
+
// Pace-maker override: a synthetic laya backend (no key — the auth header
|
|
185
|
+
// carries a placeholder the local server ignores; the secret gate and the
|
|
186
|
+
// call-layer row still run, tagged backend='laya').
|
|
187
|
+
const backend = args.overrides
|
|
188
|
+
? {
|
|
189
|
+
name: 'laya',
|
|
190
|
+
family: 'systemone',
|
|
191
|
+
endpoint: args.overrides.endpoint,
|
|
192
|
+
defaultModel: args.overrides.model,
|
|
193
|
+
keyEnv: '',
|
|
194
|
+
keychainService: '',
|
|
195
|
+
keychainByDefault: false,
|
|
196
|
+
}
|
|
197
|
+
: resolveBackend(args.config.jevBackend);
|
|
198
|
+
// '' is the sentinel; undefined (bare harnesses) resolves to the default too
|
|
199
|
+
const model = args.overrides ? args.overrides.model : args.config.jevModel ? args.config.jevModel : backend.defaultModel;
|
|
200
|
+
const fallback = args.fallback === true; // absent => false: the column is call-time-invariant (fail-open = outcome !== ok)
|
|
201
|
+
const state = typeof args.state === 'string' ? args.state : '';
|
|
202
|
+
const started = Date.now();
|
|
203
|
+
async function emit(outcome, usage) {
|
|
204
|
+
await args.log.logCall({
|
|
205
|
+
at: new Date().toISOString(),
|
|
206
|
+
lane: args.lane,
|
|
207
|
+
backend: backend.name,
|
|
208
|
+
model,
|
|
209
|
+
questionCount: isPlainObject(args.questions) ? Object.keys(args.questions).length : 0,
|
|
210
|
+
stateChars: state.length,
|
|
211
|
+
latencyMs: Date.now() - started,
|
|
212
|
+
usage,
|
|
213
|
+
outcome,
|
|
214
|
+
fallback,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const refuse = async (outcome, message) => {
|
|
218
|
+
await emit(outcome, null);
|
|
219
|
+
return { ok: false, outcome, message, latencyMs: Date.now() - started };
|
|
220
|
+
};
|
|
221
|
+
// 1. Local protocol check — nothing about the request has left the process.
|
|
222
|
+
const normalized = normalizeQuestions(args.questions, backend.family);
|
|
223
|
+
if (!normalized.ok)
|
|
224
|
+
return refuse('invalid_request', normalized.message);
|
|
225
|
+
const wireQuestions = normalized.questions;
|
|
226
|
+
// 2. Secret gate over the COMPLETE outbound body — the exact string below
|
|
227
|
+
// is what goes on the wire, so the gate sees byte-identical traffic.
|
|
228
|
+
// A hit sends NOTHING.
|
|
229
|
+
const secretFile = args.config.jevSecretFile ?? '';
|
|
230
|
+
const wire = JSON.stringify({ model, state, questions: wireQuestions });
|
|
231
|
+
const hit = scanForSecrets(wire, currentSecrets(secretFile, args.logger), secretFile);
|
|
232
|
+
if (hit !== null) {
|
|
233
|
+
return refuse('secret_gate_blocked', `outbound payload matched secret pattern '${hit.pattern}' at offset ${hit.offset}; NOTHING was sent. Remove the secret from the request body (model/state/questions).`);
|
|
234
|
+
}
|
|
235
|
+
// 3. API key — absent key fails before any network activity. The laya
|
|
236
|
+
// pace-maker skips resolution entirely (local, no auth).
|
|
237
|
+
const key = args.overrides ? 'laya-local' : await resolveApiKey(backend);
|
|
238
|
+
if (key === '') {
|
|
239
|
+
const hint = backend.keychainByDefault
|
|
240
|
+
? ` (set ${backend.keyEnv}, or store the key in the macOS keychain service '${backend.keychainService}')`
|
|
241
|
+
: ` (set ${backend.keyEnv}, or JEV_KEYCHAIN='<service>[:<account>]' for a macOS keychain lookup)`;
|
|
242
|
+
return refuse('missing_key', `no API key for the active backend '${backend.name}'${hint}.`);
|
|
243
|
+
}
|
|
244
|
+
// 4. One request, one timeout, NO retry.
|
|
245
|
+
const timeoutMs = Number(args.config.jevTimeoutMs) > 0 ? Number(args.config.jevTimeoutMs) : DEFAULT_TIMEOUT_MS;
|
|
246
|
+
let res;
|
|
247
|
+
try {
|
|
248
|
+
res = await fetch(backend.endpoint, {
|
|
249
|
+
method: 'POST',
|
|
250
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${key}` },
|
|
251
|
+
body: wire,
|
|
252
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
const outcome = err instanceof Error && (err.name === 'TimeoutError' || err.name === 'AbortError') ? 'timeout' : 'network';
|
|
257
|
+
return refuse(outcome, `${outcome} error: ${err instanceof Error ? err.message : String(err)}`);
|
|
258
|
+
}
|
|
259
|
+
if (!res.ok) {
|
|
260
|
+
const text = (await res.text().catch(() => '')).slice(0, 300);
|
|
261
|
+
const outcome = res.status >= 500 ? 'http_5xx' : 'http_4xx';
|
|
262
|
+
return refuse(outcome, `${backend.name} responded ${res.status}: ${text}`);
|
|
263
|
+
}
|
|
264
|
+
let json;
|
|
265
|
+
try {
|
|
266
|
+
json = await res.json();
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
return refuse('bad_json', `${backend.name} returned non-JSON body: ${err instanceof Error ? err.message : String(err)}`);
|
|
270
|
+
}
|
|
271
|
+
// 5. Response shape: answers must be an object covering every question id
|
|
272
|
+
// (a half-answered batch is a bad response, not a partial success).
|
|
273
|
+
if (!isPlainObject(json)) {
|
|
274
|
+
return refuse('bad_json', `${backend.name} returned no answers object.`);
|
|
275
|
+
}
|
|
276
|
+
const answers = json.answers;
|
|
277
|
+
if (!isPlainObject(answers)) {
|
|
278
|
+
return refuse('bad_json', `${backend.name} returned no answers object.`);
|
|
279
|
+
}
|
|
280
|
+
const missing = Object.keys(wireQuestions).filter((id) => answers[id] === undefined);
|
|
281
|
+
if (missing.length > 0) {
|
|
282
|
+
return refuse('bad_json', `${backend.name} returned no answer for question id(s): ${missing.join(', ')}.`);
|
|
283
|
+
}
|
|
284
|
+
const usage = isPlainObject(json.usage) ? json.usage : null;
|
|
285
|
+
await emit('ok', usage);
|
|
286
|
+
return { ok: true, answers: answers, usage, latencyMs: Date.now() - started };
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/jev/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System One (jev) configuration slice — the seven keys, ported from
|
|
3
|
+
* dsh-topics-memory `src/config.ts` §4 and expressed in this plugin's
|
|
4
|
+
* schemastery form.
|
|
5
|
+
*
|
|
6
|
+
* WHY A SEPARATE MODULE (not a sixth entry in ../index.ts's `Config`):
|
|
7
|
+
* the adapter's settings schema is the single `z.object({...})` exported as
|
|
8
|
+
* `Config` from src/index.ts, and this stage forbids touching that file.
|
|
9
|
+
* So the seven keys live here as a plain field record with two export forms:
|
|
10
|
+
*
|
|
11
|
+
* - {@link JEV_CONFIG_FIELDS} — a plain `Record<key, Schema>`, written to
|
|
12
|
+
* be SPREAD into the host schema next stage:
|
|
13
|
+
* export const Config = z.object({ ...JEV_CONFIG_FIELDS, ...ownFields }) as unknown as z<AdapterConfig>
|
|
14
|
+
* (verified: schemastery `z.object` accepts spread field records and the
|
|
15
|
+
* defaults survive);
|
|
16
|
+
* - {@link JevConfig} — the same seven keys as a standalone schema, so the
|
|
17
|
+
* keys are already runtime-validatable (and tested) before they are wired
|
|
18
|
+
* into the settings page.
|
|
19
|
+
*
|
|
20
|
+
* Until that wiring lands, nothing reads these values: `src/index.ts` does
|
|
21
|
+
* not import this module, so the plugin behaves exactly as before.
|
|
22
|
+
*
|
|
23
|
+
* All seven are VOLATILE (0.1.7 settings contract: a settings-page write
|
|
24
|
+
* swaps the value in place without remounting the plugin), so every consumer
|
|
25
|
+
* must read through the ref per use.
|
|
26
|
+
*
|
|
27
|
+
* @module jev/config
|
|
28
|
+
*/
|
|
29
|
+
import z from '@deepseek-ai/schemastery';
|
|
30
|
+
/**
|
|
31
|
+
* Live-reference shape. Structurally identical to `VolatileRef` in
|
|
32
|
+
* ../index.ts; redeclared here so this module never imports the plugin
|
|
33
|
+
* entry (which will import this module once the keys are wired).
|
|
34
|
+
*/
|
|
35
|
+
export interface JevVolatileRef<T> {
|
|
36
|
+
get(): T;
|
|
37
|
+
}
|
|
38
|
+
/** Resolved value of the seven keys, as `apply` will read them. */
|
|
39
|
+
export interface JevConfigValue {
|
|
40
|
+
/** Default-off master switch: false means zero behavior change anywhere —
|
|
41
|
+
* no call is made, and the usage stats stop with it. */
|
|
42
|
+
jevEnabled: JevVolatileRef<boolean>;
|
|
43
|
+
/** Decision backend: zen (free, opencode.ai) | native (typesafe
|
|
44
|
+
* first-party) | openrouter (decisions protocol). */
|
|
45
|
+
jevBackend: JevVolatileRef<'zen' | 'native' | 'openrouter'>;
|
|
46
|
+
/** Pinned model id; '' = resolve the backend default at call time
|
|
47
|
+
* (distillProvider sentinel pattern): jev-1.13-free / jev-1.13.0 /
|
|
48
|
+
* typesafe/jev-1.13 — an upgrade is a deliberate act. */
|
|
49
|
+
jevModel: JevVolatileRef<string>;
|
|
50
|
+
/** Single-request timeout in ms (positive integer); one AbortSignal, NO
|
|
51
|
+
* retry (the calling lane's next cadence is the natural retry, fail-open
|
|
52
|
+
* hard-coded). */
|
|
53
|
+
jevTimeoutMs: JevVolatileRef<number>;
|
|
54
|
+
/** External secret list for the outbound secret gate (JEV_SECRET_FILE
|
|
55
|
+
* semantics, design §3.3); swapping the path hot reloads the list. */
|
|
56
|
+
jevSecretFile: JevVolatileRef<string>;
|
|
57
|
+
/** Local laya pace-maker (design §3.4): fire a parallel laya call on every
|
|
58
|
+
* jev request; its verdict is logged for comparison and takes over ONLY
|
|
59
|
+
* when the primary backend fails (degraded). Default off — zero extra
|
|
60
|
+
* requests, and laya-serve not running costs a refused connection. */
|
|
61
|
+
jevLayaFallback: JevVolatileRef<boolean>;
|
|
62
|
+
/** laya-serve systemone endpoint for the pace-maker. */
|
|
63
|
+
jevLayaUrl: JevVolatileRef<string>;
|
|
64
|
+
}
|
|
65
|
+
/** The seven keys, in settings-page order. */
|
|
66
|
+
export declare const JEV_CONFIG_KEYS: readonly ["jevEnabled", "jevBackend", "jevModel", "jevTimeoutMs", "jevSecretFile", "jevLayaFallback", "jevLayaUrl"];
|
|
67
|
+
export type JevConfigKey = (typeof JEV_CONFIG_KEYS)[number];
|
|
68
|
+
/**
|
|
69
|
+
* The seven field schemas as a plain record — the wiring form for the host
|
|
70
|
+
* schema (`z.object({ ...JEV_CONFIG_FIELDS, … })`).
|
|
71
|
+
*/
|
|
72
|
+
export declare const JEV_CONFIG_FIELDS: {
|
|
73
|
+
readonly jevEnabled: z<boolean, boolean, "volatile-defined">;
|
|
74
|
+
readonly jevBackend: z<"zen" | "native" | "openrouter", "zen" | "native" | "openrouter", "volatile-defined">;
|
|
75
|
+
readonly jevModel: z<string, string, "volatile-defined">;
|
|
76
|
+
readonly jevTimeoutMs: z<number, number, "volatile-defined">;
|
|
77
|
+
readonly jevSecretFile: z<string, string, "volatile-defined">;
|
|
78
|
+
readonly jevLayaFallback: z<boolean, boolean, "volatile-defined">;
|
|
79
|
+
readonly jevLayaUrl: z<string, string, "volatile-defined">;
|
|
80
|
+
};
|
|
81
|
+
/** The same seven keys as a standalone schema (runtime validation today). */
|
|
82
|
+
export declare const JevConfig: z<JevConfigValue>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System One (jev) configuration slice — the seven keys, ported from
|
|
3
|
+
* dsh-topics-memory `src/config.ts` §4 and expressed in this plugin's
|
|
4
|
+
* schemastery form.
|
|
5
|
+
*
|
|
6
|
+
* WHY A SEPARATE MODULE (not a sixth entry in ../index.ts's `Config`):
|
|
7
|
+
* the adapter's settings schema is the single `z.object({...})` exported as
|
|
8
|
+
* `Config` from src/index.ts, and this stage forbids touching that file.
|
|
9
|
+
* So the seven keys live here as a plain field record with two export forms:
|
|
10
|
+
*
|
|
11
|
+
* - {@link JEV_CONFIG_FIELDS} — a plain `Record<key, Schema>`, written to
|
|
12
|
+
* be SPREAD into the host schema next stage:
|
|
13
|
+
* export const Config = z.object({ ...JEV_CONFIG_FIELDS, ...ownFields }) as unknown as z<AdapterConfig>
|
|
14
|
+
* (verified: schemastery `z.object` accepts spread field records and the
|
|
15
|
+
* defaults survive);
|
|
16
|
+
* - {@link JevConfig} — the same seven keys as a standalone schema, so the
|
|
17
|
+
* keys are already runtime-validatable (and tested) before they are wired
|
|
18
|
+
* into the settings page.
|
|
19
|
+
*
|
|
20
|
+
* Until that wiring lands, nothing reads these values: `src/index.ts` does
|
|
21
|
+
* not import this module, so the plugin behaves exactly as before.
|
|
22
|
+
*
|
|
23
|
+
* All seven are VOLATILE (0.1.7 settings contract: a settings-page write
|
|
24
|
+
* swaps the value in place without remounting the plugin), so every consumer
|
|
25
|
+
* must read through the ref per use.
|
|
26
|
+
*
|
|
27
|
+
* @module jev/config
|
|
28
|
+
*/
|
|
29
|
+
import z from '@deepseek-ai/schemastery';
|
|
30
|
+
/** The seven keys, in settings-page order. */
|
|
31
|
+
export const JEV_CONFIG_KEYS = [
|
|
32
|
+
'jevEnabled',
|
|
33
|
+
'jevBackend',
|
|
34
|
+
'jevModel',
|
|
35
|
+
'jevTimeoutMs',
|
|
36
|
+
'jevSecretFile',
|
|
37
|
+
'jevLayaFallback',
|
|
38
|
+
'jevLayaUrl',
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* The seven field schemas as a plain record — the wiring form for the host
|
|
42
|
+
* schema (`z.object({ ...JEV_CONFIG_FIELDS, … })`).
|
|
43
|
+
*/
|
|
44
|
+
export const JEV_CONFIG_FIELDS = {
|
|
45
|
+
jevEnabled: z.boolean().default(false).volatile(),
|
|
46
|
+
jevBackend: z.union([z.const('zen'), z.const('native'), z.const('openrouter')]).default('zen').volatile(),
|
|
47
|
+
jevModel: z.string().default('').volatile(),
|
|
48
|
+
jevTimeoutMs: z.natural().min(1).default(3000).volatile(),
|
|
49
|
+
jevSecretFile: z.string().default('').volatile(),
|
|
50
|
+
jevLayaFallback: z.boolean().default(false).volatile(),
|
|
51
|
+
jevLayaUrl: z.string().default('http://127.0.0.1:8000/v1/systemone').volatile(),
|
|
52
|
+
};
|
|
53
|
+
/** The same seven keys as a standalone schema (runtime validation today). */
|
|
54
|
+
export const JevConfig = z.object(JEV_CONFIG_FIELDS);
|
|
55
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/jev/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAuCxC,8CAA8C;AAC9C,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,YAAY;IACZ,YAAY;IACZ,UAAU;IACV,cAAc;IACd,eAAe;IACf,iBAAiB;IACjB,YAAY;CACJ,CAAA;AAIV;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE;IACjD,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE;IACzG,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC3C,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IACzD,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE;IACtD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,QAAQ,EAAE;CACvE,CAAA;AAEV,6EAA6E;AAC7E,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAiC,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dual-run pace-maker (design §3.4, added 09-27): fire the configured
|
|
3
|
+
* backend AND the local laya-serve endpoint in parallel on every jev call.
|
|
4
|
+
* Ported verbatim from dsh-topics-memory `src/jev/dual.ts` — the wrapper is
|
|
5
|
+
* business-agnostic.
|
|
6
|
+
*
|
|
7
|
+
* - the PRIMARY answer drives the decision whenever it is ok;
|
|
8
|
+
* - the laya answer is always logged (its own call-layer row tagged
|
|
9
|
+
* backend='laya' plus verdict rows) — a free, permanently running
|
|
10
|
+
* laya-vs-jev comparison on identical questions;
|
|
11
|
+
* - when the primary FAILS (cold-start spike, timeout, 5xx…), the laya
|
|
12
|
+
* answer — by then already resolved — takes over as DEGRADED: the calling
|
|
13
|
+
* seam may only use laya's RELATIVE ranking, never its absolute scores
|
|
14
|
+
* against jev-calibrated thresholds (laya's negatives sit in the same band
|
|
15
|
+
* as its positives — the 4-way bench, 09-27);
|
|
16
|
+
* - laya not running = a refused connection in milliseconds — the switch
|
|
17
|
+
* can stay on unconditionally.
|
|
18
|
+
*
|
|
19
|
+
* @module jev/dual
|
|
20
|
+
*/
|
|
21
|
+
import type { JevAskArgs, JevAskConfig, JevAskResult } from './client.ts';
|
|
22
|
+
/** Default local laya-serve endpoint (laya repo: `laya[serve]` binds 8000). */
|
|
23
|
+
export declare const DEFAULT_LAYA_URL = "http://127.0.0.1:8000/v1/systemone";
|
|
24
|
+
export interface JevDualConfig extends JevAskConfig {
|
|
25
|
+
/** Pace-maker switch (default OFF — zero extra requests when false). */
|
|
26
|
+
jevLayaFallback: boolean;
|
|
27
|
+
/** Local laya-serve systemone endpoint. */
|
|
28
|
+
jevLayaUrl: string;
|
|
29
|
+
}
|
|
30
|
+
export interface JevDualArgs extends Omit<JevAskArgs, 'config'> {
|
|
31
|
+
config: JevDualConfig;
|
|
32
|
+
}
|
|
33
|
+
export type JevDualSource = 'primary' | 'laya';
|
|
34
|
+
export type JevDualResult = JevAskResult & {
|
|
35
|
+
/** Which answer DRIVES the decision. When 'laya', the calling seam must
|
|
36
|
+
* only use RELATIVE ordering (degraded mode — see module doc). */
|
|
37
|
+
source: JevDualSource;
|
|
38
|
+
/** The pace-maker's own result when it ran (null when the switch is off),
|
|
39
|
+
* always logged by jevAsk via the overrides hook. */
|
|
40
|
+
layaResult: JevAskResult | null;
|
|
41
|
+
};
|
|
42
|
+
export declare function jevAskDual(args: JevDualArgs): Promise<JevDualResult>;
|
package/lib/jev/dual.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dual-run pace-maker (design §3.4, added 09-27): fire the configured
|
|
3
|
+
* backend AND the local laya-serve endpoint in parallel on every jev call.
|
|
4
|
+
* Ported verbatim from dsh-topics-memory `src/jev/dual.ts` — the wrapper is
|
|
5
|
+
* business-agnostic.
|
|
6
|
+
*
|
|
7
|
+
* - the PRIMARY answer drives the decision whenever it is ok;
|
|
8
|
+
* - the laya answer is always logged (its own call-layer row tagged
|
|
9
|
+
* backend='laya' plus verdict rows) — a free, permanently running
|
|
10
|
+
* laya-vs-jev comparison on identical questions;
|
|
11
|
+
* - when the primary FAILS (cold-start spike, timeout, 5xx…), the laya
|
|
12
|
+
* answer — by then already resolved — takes over as DEGRADED: the calling
|
|
13
|
+
* seam may only use laya's RELATIVE ranking, never its absolute scores
|
|
14
|
+
* against jev-calibrated thresholds (laya's negatives sit in the same band
|
|
15
|
+
* as its positives — the 4-way bench, 09-27);
|
|
16
|
+
* - laya not running = a refused connection in milliseconds — the switch
|
|
17
|
+
* can stay on unconditionally.
|
|
18
|
+
*
|
|
19
|
+
* @module jev/dual
|
|
20
|
+
*/
|
|
21
|
+
import { jevAsk } from "./client.js";
|
|
22
|
+
/** Default local laya-serve endpoint (laya repo: `laya[serve]` binds 8000). */
|
|
23
|
+
export const DEFAULT_LAYA_URL = 'http://127.0.0.1:8000/v1/systemone';
|
|
24
|
+
export async function jevAskDual(args) {
|
|
25
|
+
if (args.config.jevLayaFallback !== true) {
|
|
26
|
+
const r = await jevAsk(args);
|
|
27
|
+
return { ...r, source: 'primary', layaResult: null };
|
|
28
|
+
}
|
|
29
|
+
const layaArgs = {
|
|
30
|
+
...args,
|
|
31
|
+
config: { ...args.config, jevModel: 'laya-rl-agent' },
|
|
32
|
+
overrides: { endpoint: args.config.jevLayaUrl || DEFAULT_LAYA_URL, model: 'laya-rl-agent' },
|
|
33
|
+
};
|
|
34
|
+
const [primary, laya] = await Promise.all([jevAsk(args), jevAsk(layaArgs)]);
|
|
35
|
+
if (primary.ok)
|
|
36
|
+
return { ...primary, source: 'primary', layaResult: laya };
|
|
37
|
+
if (laya.ok) {
|
|
38
|
+
return { ok: true, answers: laya.answers, usage: laya.usage, latencyMs: laya.latencyMs, source: 'laya', layaResult: laya };
|
|
39
|
+
}
|
|
40
|
+
return { ...primary, source: 'primary', layaResult: laya };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=dual.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dual.js","sourceRoot":"","sources":["../../src/jev/dual.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpC,+EAA+E;AAC/E,MAAM,CAAC,MAAM,gBAAgB,GAAG,oCAAoC,CAAA;AAwBpE,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAiB;IAChD,IAAI,IAAI,CAAC,MAAM,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;QACzC,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,IAAkB,CAAC,CAAA;QAC1C,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;IACtD,CAAC;IACD,MAAM,QAAQ,GAAe;QAC3B,GAAG,IAAI;QACP,MAAM,EAAE,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE;QACrD,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE;KAC5F,CAAA;IACD,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAkB,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IACzF,IAAI,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;IAC1E,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;QACZ,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;IAC5H,CAAC;IACD,OAAO,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;AAC5D,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound secret gate (design 2026-09-25 §3.3) — ported verbatim from
|
|
3
|
+
* dsh-topics-memory `src/jev/gate.ts`, a self-contained port of the
|
|
4
|
+
* dsh-jev-mcp `server.mjs` mechanism. The COMPLETE serialized request body
|
|
5
|
+
* (model, state, questions — byte-identical to what goes on the wire) is
|
|
6
|
+
* scanned for credential patterns BEFORE anything leaves the process; a hit
|
|
7
|
+
* aborts the call with a structured descriptor that never echoes the secret.
|
|
8
|
+
*
|
|
9
|
+
* SECURITY INVARIANT: hit descriptors carry static metadata only (pattern
|
|
10
|
+
* name, offset, ≤10 printable chars before the hit) — never the matched
|
|
11
|
+
* text, and never text at or after the hit point.
|
|
12
|
+
*
|
|
13
|
+
* @module jev/gate
|
|
14
|
+
*/
|
|
15
|
+
export interface SecretGateHit {
|
|
16
|
+
pattern: string;
|
|
17
|
+
offset: number;
|
|
18
|
+
context: string;
|
|
19
|
+
}
|
|
20
|
+
/** Minimal logger surface (both sinks optional — bare harnesses have none);
|
|
21
|
+
* the gate only ever warns, and a broken logger must not break the gate. */
|
|
22
|
+
export interface GateLogger {
|
|
23
|
+
warn?: (m: string) => void;
|
|
24
|
+
}
|
|
25
|
+
export declare const BUILTIN_PATTERNS: ReadonlyArray<{
|
|
26
|
+
name: string;
|
|
27
|
+
re: RegExp;
|
|
28
|
+
}>;
|
|
29
|
+
/** Literal secrets from the external list file (JEV_SECRET_FILE semantics:
|
|
30
|
+
* one entry per line, ≥8 chars, `#` comments skipped, matched at FULL
|
|
31
|
+
* length — a >512-char line warns but is never truncated, or everything
|
|
32
|
+
* past char 512 would silently escape the gate). */
|
|
33
|
+
export declare function loadExternalSecrets(file: string, log?: GateLogger): string[];
|
|
34
|
+
export declare function currentSecrets(file: string, log?: GateLogger): string[];
|
|
35
|
+
export declare function reloadSecrets(file: string, log?: GateLogger): string[];
|
|
36
|
+
/**
|
|
37
|
+
* Redacted context: at most 10 printable chars BEFORE the hit plus an
|
|
38
|
+
* explicit [REDACTED:<pattern>] marker. Content at or after the hit point is
|
|
39
|
+
* NEVER shown — when a prefix regex under-matches, the secret tail sits
|
|
40
|
+
* right there. Belt and suspenders: if the matched text or any known
|
|
41
|
+
* external secret still appears in the context, the whole context collapses
|
|
42
|
+
* to '[REDACTED]'.
|
|
43
|
+
*/
|
|
44
|
+
export declare function describeHit(pattern: string, offset: number, matched: string, payload: string, externalSecrets?: readonly string[]): SecretGateHit;
|
|
45
|
+
/**
|
|
46
|
+
* Scan the serialized outbound payload for secrets.
|
|
47
|
+
* @param payload JSON.stringify({model, state, questions}) — the exact wire body
|
|
48
|
+
* @param externalSecrets literal secrets from the external list (cached via currentSecrets)
|
|
49
|
+
* @param externalSource label for hit descriptors (the list file path)
|
|
50
|
+
*/
|
|
51
|
+
export declare function scanForSecrets(payload: string, externalSecrets?: readonly string[], externalSource?: string): SecretGateHit | null;
|