@heretek-ai/epistemic-swarm 0.7.21 → 0.7.22
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/plugin.json +1 -1
- package/README.md +1 -1
- package/bin/cli.js +3 -2
- package/extensions/pi/index.js +3 -2
- package/package.json +1 -1
- package/plugins/antigravity/plugin.json +1 -1
- package/plugins/factory/skills/factory/SKILL.md +1 -1
- package/plugins/opencode/config-io.js +598 -31
- package/plugins/opencode/index.js +56 -15
- package/plugins/opencode/tui.js +115 -11
- package/plugins/research-cache/skills/research_cache/hasher.py +33 -8
- package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
- package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
- package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
- package/runner/__pycache__/darkharvest_claims.cpython-311.pyc +0 -0
- package/runner/__pycache__/errors.cpython-311.pyc +0 -0
- package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
- package/runner/__pycache__/path_safety.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
- package/runner/__pycache__/preflight.cpython-311.pyc +0 -0
- package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
- package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
- package/runner/__pycache__/retrieval_log.cpython-311.pyc +0 -0
- package/runner/__pycache__/schema_validate.cpython-311.pyc +0 -0
- package/runner/__pycache__/schemas.cpython-311.pyc +0 -0
- package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
- package/runner/mcp_server.py +249 -81
- package/runner/refinement.py +5 -0
- package/runner/schema_validate.py +63 -1
- package/runner/schemas.py +9 -4
- package/runner/tests/__pycache__/test_antigravity_plugin.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_backends.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_bet1_spike.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claude_plugin.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_config_schema.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_contract_tooling.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_darkharvest_audit.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_dossier_contract.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_factory.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_frontier_concurrency.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_frontier_mirror_standalone.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_grounding.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_hardening_contract.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_manifest_concurrency.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_opencode_ux.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_opencode_v2_registrars.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_phase08_waiver_reopens.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_search_anomaly.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_search_provider.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_search_provider_plugin.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_swarm_config.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_sweep_regressions.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_webcache.cpython-311.pyc +0 -0
- package/runner/tests/fixtures/config_validation_cases.json +60 -0
- package/runner/tests/test_config_schema.py +119 -15
- package/runner/tests/test_hardening_contract.py +391 -0
- package/runner/tests/test_opencode_ux.py +232 -0
- package/runner/tests/test_opencode_v2_registrars.py +548 -16
- package/runner/tests/test_phase08_waiver_reopens.py +539 -0
- package/schemas/config.schema.json +4 -3
- package/scripts/__pycache__/bet1_advisory_spike.cpython-311.pyc +0 -0
- package/scripts/__pycache__/build_adapters.cpython-311.pyc +0 -0
- package/scripts/__pycache__/check_docs.cpython-311.pyc +0 -0
- package/scripts/__pycache__/divergence_experiment.cpython-311.pyc +0 -0
- package/scripts/__pycache__/gen_schemas.cpython-311.pyc +0 -0
- package/skills/epistemic_search/scripts/__pycache__/search.cpython-311.pyc +0 -0
- package/skills/factory/SKILL.md +1 -1
- package/skills/grilling/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/grilling/__pycache__/socratic_tree.cpython-311.pyc +0 -0
- package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
- package/skills/research_cache/hasher.py +33 -8
- package/skills/swarm_config/SKILL.md +6 -1
- package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
- package/skills/swarm_config/configure.py +254 -17
|
@@ -152,13 +152,13 @@ export const TOOL_CATALOG = [
|
|
|
152
152
|
show: {
|
|
153
153
|
type: 'boolean',
|
|
154
154
|
description:
|
|
155
|
-
'Inspect only; equivalent to calling with no update keys (reads are always safe).',
|
|
155
|
+
'Inspect only; equivalent to calling with no update keys (reads are always safe). When true, the call is read-only even if update keys are present (they are reported as ignored and nothing is written; the expected_hash guard, including the dual-spelling conflict check, is not evaluated).',
|
|
156
156
|
default: false,
|
|
157
157
|
},
|
|
158
158
|
expected_hash: {
|
|
159
159
|
type: 'string',
|
|
160
160
|
description:
|
|
161
|
-
'Optimistic-concurrency guard: SHA-256 (or a unique prefix of at least 8 hex chars) of the config file you read. A mismatch returns a structured stale error with a fresh snapshot and writes nothing. Alias: `expectedHash`; if both spellings are present they must be equal or the call is rejected.',
|
|
161
|
+
'Optimistic-concurrency guard: SHA-256 (or a unique prefix of at least 8 hex chars) of the config file you read. A mismatch returns a structured stale error with a fresh snapshot and writes nothing. Empty/whitespace-only spellings count as absent. Alias: `expectedHash`; if both spellings are present they must be equal after normalization (strip + lowercase) or the call is rejected.',
|
|
162
162
|
},
|
|
163
163
|
expectedHash: {
|
|
164
164
|
type: 'string',
|
|
@@ -207,8 +207,8 @@ export const TOOL_CATALOG = [
|
|
|
207
207
|
},
|
|
208
208
|
searxng_url: {
|
|
209
209
|
type: ['string', 'null'],
|
|
210
|
-
pattern: '^https?://',
|
|
211
|
-
description: 'Base URL of a self-hosted SearXNG, exported as SEARXNG_URL when set; must include an http:// or https:// scheme; null leaves the environment untouched.',
|
|
210
|
+
pattern: '^https?://[^\\s/]+\\S*$',
|
|
211
|
+
description: 'Base URL of a self-hosted SearXNG, exported as SEARXNG_URL when set; must include an http:// or https:// scheme and a host, with no whitespace anywhere; null leaves the environment untouched.',
|
|
212
212
|
},
|
|
213
213
|
license_whitelist: {
|
|
214
214
|
type: 'array',
|
|
@@ -227,7 +227,8 @@ export const TOOL_CATALOG = [
|
|
|
227
227
|
domain_pack: {
|
|
228
228
|
type: ['string', 'null'],
|
|
229
229
|
minLength: 1,
|
|
230
|
-
|
|
230
|
+
pattern: '^\\S(.*\\S)?$',
|
|
231
|
+
description: 'Regulated Domain Pack id (config/domain_packs/) or null for the legacy constitution; the empty string, whitespace-only strings, and ids with leading/trailing whitespace are rejected.',
|
|
231
232
|
},
|
|
232
233
|
verify: {
|
|
233
234
|
type: 'object',
|
|
@@ -440,13 +441,18 @@ export const TOOL_CATALOG = [
|
|
|
440
441
|
{
|
|
441
442
|
name: 'iumbtems_factory',
|
|
442
443
|
description:
|
|
443
|
-
'Drive factory run state: init / phase-add / qa-record / expansion / stop. State goes to <project>/.factory and <project>/.roadmap; no helper-script path needed.',
|
|
444
|
+
'Drive factory run state: init / phase-add / qa-record / expansion / stop / gate. State goes to <project>/.factory and <project>/.roadmap; no helper-script path needed.',
|
|
444
445
|
input: {
|
|
445
446
|
type: 'object',
|
|
446
447
|
properties: {
|
|
447
448
|
command: {
|
|
448
449
|
type: 'string',
|
|
449
|
-
enum: ['init', 'phase-add', 'qa-record', 'expansion', 'stop'],
|
|
450
|
+
enum: ['init', 'phase-add', 'qa-record', 'expansion', 'stop', 'gate'],
|
|
451
|
+
},
|
|
452
|
+
action: {
|
|
453
|
+
type: 'string',
|
|
454
|
+
enum: ['open', 'settle', 'approve', 'waive', 'escalate', 'count'],
|
|
455
|
+
description: 'Gate action (with command: gate)',
|
|
450
456
|
},
|
|
451
457
|
run: { type: 'string', description: 'Factory run name' },
|
|
452
458
|
phase: { type: 'string', description: 'Phase id (e.g. 01-auth)' },
|
|
@@ -2227,13 +2233,16 @@ function rpcFail(context, type, message, data) {
|
|
|
2227
2233
|
const SETTINGS_CONTROL_ARGS = new Set(['base_dir', 'dir', 'show', 'expected_hash', 'expectedHash']);
|
|
2228
2234
|
|
|
2229
2235
|
/**
|
|
2230
|
-
* Leak signatures that must never reach the RPC transport (phase-03 R7/R9
|
|
2231
|
-
* tracebacks, `File "…", line N` frames, interpreter failure
|
|
2232
|
-
* absolute filesystem paths of ANY root — POSIX
|
|
2233
|
-
* `/srv/…`, `/Users/…`, `/app/…`),
|
|
2234
|
-
*
|
|
2235
|
-
*
|
|
2236
|
-
*
|
|
2236
|
+
* Leak signatures that must never reach the RPC transport (phase-03 R7/R9,
|
|
2237
|
+
* phase-08 R1): tracebacks, `File "…", line N` frames, interpreter failure
|
|
2238
|
+
* lines, `file://` URIs, and absolute filesystem paths of ANY root — POSIX
|
|
2239
|
+
* multi-segment (e.g. `/workspace/…`, `/srv/…`, `/Users/…`, `/app/…`),
|
|
2240
|
+
* single-segment system roots (`/etc`, `/tmp`, `/lib`, `/lib64`, `/boot`,
|
|
2241
|
+
* `/snap`, macOS `/System`, `/Library`, `/Volumes`, `/Applications`, …),
|
|
2242
|
+
* Windows drive
|
|
2243
|
+
* (`C:\Users\…`), and UNC (`\\server\share`). Structured validation problems
|
|
2244
|
+
* (e.g. `mode: value … not in enum …`, type/range violations carrying
|
|
2245
|
+
* ordinary values) match none of these and pass through intact.
|
|
2237
2246
|
*/
|
|
2238
2247
|
const SETTINGS_ERROR_LEAK_PATTERNS = [
|
|
2239
2248
|
// Tracebacks and Python/Node interpreter failure lines.
|
|
@@ -2241,7 +2250,39 @@ const SETTINGS_ERROR_LEAK_PATTERNS = [
|
|
|
2241
2250
|
/File\s+["'][^"']+["']\s*,\s*line\s+\d+/i,
|
|
2242
2251
|
/\b(?:ModuleNotFoundError|ImportError|SyntaxError|NameError|OSError|Errno \d+)\b/,
|
|
2243
2252
|
// Absolute POSIX path of any root: a bounded leading slash + >=2 segments.
|
|
2244
|
-
|
|
2253
|
+
// Phase-08 R1 (W9) REWORK-2 L2: prefix class gains `[`, `{`, `-` so
|
|
2254
|
+
// bracket/brace/dash-delimited absolutes (`[/etc]`, `{/etc}`, `-/etc`,
|
|
2255
|
+
// `x-/lib64,y`) redact. Canonical values contain no `/`-root tokens
|
|
2256
|
+
// (proven by the canonical-value corpus passing verbatim), so `-`-prefixed
|
|
2257
|
+
// path tokens are disclosures, not user echoes.
|
|
2258
|
+
/(?:^|[\s"'(=:,\[{-])\/(?:[A-Za-z0-9._-]+\/)+[A-Za-z0-9._-]+/,
|
|
2259
|
+
// Phase-08 R1 (W9) REWORK-1: single-segment system-root absolutes
|
|
2260
|
+
// (`/etc`, `/tmp`, plus `/lib`, `/lib64`, `/boot`, `/snap`, macOS `/System`,
|
|
2261
|
+
// `/Library`, `/Volumes`, `/Applications`).
|
|
2262
|
+
// The >=2-segment arm above lets these through, but a bare system root in
|
|
2263
|
+
// a failure item is a local-filesystem disclosure (interpreter stderr
|
|
2264
|
+
// echoes absolute paths of any depth). The root allowlist keeps ordinary
|
|
2265
|
+
// user echoes intact: single-segment non-system values (`/v1`, `/nope`)
|
|
2266
|
+
// still pass through, and sanitization stays per-item (R9).
|
|
2267
|
+
// RESIDUAL (documented): truly exotic single-segment roots outside the
|
|
2268
|
+
// mainstream Linux + macOS set (e.g. `/net`, `/nfs`, future mount points)
|
|
2269
|
+
// still pass through by design to avoid over-redacting user echoes.
|
|
2270
|
+
// TRIGGER (consumed for this class): qa-b repro `["error at /lib nope"]`
|
|
2271
|
+
// passthrough + inspection-named `/lib64`, `/boot`, `/snap`, `/System`,
|
|
2272
|
+
// `/Library`, `/Volumes`, `/Applications` are now covered. Revisit only
|
|
2273
|
+
// with a NEW real leak repro naming the still-uncovered root.
|
|
2274
|
+
// Phase-08 R1 (W9) REWORK-2 L1: suffix lookahead gains `.!?]}` so
|
|
2275
|
+
// bare-root + sentence punctuation (`/lib.`, `/etc.`, `/lib!`, `/tmp?`,
|
|
2276
|
+
// `/lib]`, `/lib}`) redacts. The >=2-segment arm is untouched (its trailing
|
|
2277
|
+
// `[A-Za-z0-9._-]+` already consumes `/etc/foo.`). L2: prefix class gains
|
|
2278
|
+
// `[`, `{`, `-` (same `-` safety proof as above) so `[/etc]`, `[/lib]`,
|
|
2279
|
+
// `[/lib64]`, `{/etc}`, `-/etc`, `x-/lib64,y` redact.
|
|
2280
|
+
/(?:^|[\s"'(=:,\[{-])\/(?:etc|var|tmp|srv|app|home|root|usr|opt|bin|sbin|proc|sys|dev|run|mnt|media|private|Users|workspace|lib64|lib|boot|snap|System|Library|Volumes|Applications)(?=\/|$|[\s"'<>|,;:().!?\]}])/,
|
|
2281
|
+
// Phase-08 R1 (W9): `file://` URIs always disclose local filesystem
|
|
2282
|
+
// layout, and no canonical config value is a file:// URI (`searxng_url`
|
|
2283
|
+
// requires an http(s) scheme) — so a file:// URI in a failure item is
|
|
2284
|
+
// never a legitimate user echo. Fail closed.
|
|
2285
|
+
/file:\/\/[^\s"'<>|]+/i,
|
|
2245
2286
|
// Windows drive-letter absolute path (`C:\Users\…`) or UNC share (`\\srv\share`).
|
|
2246
2287
|
/\b[A-Za-z]:[\\/](?:[^\\/\s"'<>|]+[\\/])*[^\\/\s"'<>|]+/,
|
|
2247
2288
|
/\\\\[^\\/\s"'<>|]+[\\/][^\\/\s"'<>|]+/,
|
package/plugins/opencode/tui.js
CHANGED
|
@@ -504,9 +504,98 @@ const WIZARD_KEEP_VALUE = '__iumbtems_keep__';
|
|
|
504
504
|
|
|
505
505
|
function wizardToast(host, title, message) {
|
|
506
506
|
try {
|
|
507
|
-
host?.ui?.toast
|
|
507
|
+
const toast = host?.ui?.toast;
|
|
508
|
+
const show = toast?.show;
|
|
509
|
+
if (typeof show !== 'function') return false;
|
|
510
|
+
show.call(toast, { title, message, variant: 'info', duration: 6000 });
|
|
511
|
+
return true;
|
|
508
512
|
} catch {
|
|
509
|
-
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// ---------------------------------------------------------------------------
|
|
518
|
+
// Phase 07 (H8): keyed advisory dedup window for the repeating R4 advisories.
|
|
519
|
+
//
|
|
520
|
+
// Editing an env-shadowed or deprecated row fires its advisory on EVERY edit,
|
|
521
|
+
// so a multi-edit wizard session spams one toast per row visit. `warnOnce`
|
|
522
|
+
// collapses repeats of the SAME advisory key inside a TTL window: the first
|
|
523
|
+
// shows, repeats within the window are suppressed (counted), expiry re-shows
|
|
524
|
+
// (annotated with the suppressed count), and distinct keys stay independent.
|
|
525
|
+
//
|
|
526
|
+
// SAFETY: only the two repeating R4 advisories route here. Degraded-path,
|
|
527
|
+
// malformed-config, validation-error, stale, and save-failure toasts call
|
|
528
|
+
// `wizardToast` directly and are NEVER deduped (missed-action safety).
|
|
529
|
+
// Durations are preserved: wizard advisories stay 6000ms via `wizardToast`
|
|
530
|
+
// (the 4000ms keymap usage toast is untouched).
|
|
531
|
+
// ---------------------------------------------------------------------------
|
|
532
|
+
|
|
533
|
+
/** Dedup window for repeating R4 advisories, in ms. Exported for tests. */
|
|
534
|
+
export const ADVISORY_DEDUP_MS = 30000;
|
|
535
|
+
|
|
536
|
+
/** key -> { ts, suppressed }: last show time + repeats collapsed since. */
|
|
537
|
+
const advisorySeen = new Map();
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Show a keyed advisory toast at most once per `ttlMs` window.
|
|
541
|
+
* Returns true when a toast was actually delivered, false when collapsed
|
|
542
|
+
* or when delivery failed (failed deliveries never arm the window).
|
|
543
|
+
* `opts.now` injects the clock (tests); `opts.ttlMs` overrides the window.
|
|
544
|
+
* Session scope: each `openSettingsWizard` call resets the window (fresh
|
|
545
|
+
* wizard session always warns at least once); repeats within one session
|
|
546
|
+
* still collapse. Never throws.
|
|
547
|
+
*/
|
|
548
|
+
export function warnOnce(host, key, title, message, opts = {}) {
|
|
549
|
+
try {
|
|
550
|
+
const ttl =
|
|
551
|
+
opts && opts.ttlMs !== undefined && opts.ttlMs !== null
|
|
552
|
+
? Number(opts.ttlMs)
|
|
553
|
+
: ADVISORY_DEDUP_MS;
|
|
554
|
+
const now =
|
|
555
|
+
opts && opts.now !== undefined && opts.now !== null
|
|
556
|
+
? Number(opts.now)
|
|
557
|
+
: Date.now();
|
|
558
|
+
const entry = advisorySeen.get(key);
|
|
559
|
+
if (entry && Number.isFinite(ttl) && ttl > 0) {
|
|
560
|
+
const dt = now - entry.ts;
|
|
561
|
+
if (Number.isFinite(dt) && dt >= 0 && dt < ttl) {
|
|
562
|
+
entry.suppressed += 1;
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
// dt < 0 (clock moved backwards) or dt >= ttl (expiry) fall through
|
|
566
|
+
// to reshow — a backwards clock must never suppress indefinitely.
|
|
567
|
+
}
|
|
568
|
+
let text = message;
|
|
569
|
+
if (entry && entry.suppressed > 0) {
|
|
570
|
+
text += ` (repeated ${entry.suppressed + 1}× since last notice)`;
|
|
571
|
+
}
|
|
572
|
+
// Arm the window ONLY on actual delivery: a failed toast (missing/broken
|
|
573
|
+
// host) leaves state untouched so the next real warning still delivers.
|
|
574
|
+
const delivered = wizardToast(host, title, text);
|
|
575
|
+
if (!delivered) return false;
|
|
576
|
+
advisorySeen.set(key, { ts: now, suppressed: 0 });
|
|
577
|
+
return true;
|
|
578
|
+
} catch {
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/** Test hook: forget all dedup state. Never throws. */
|
|
584
|
+
export function _resetAdvisoryDedup() {
|
|
585
|
+
try {
|
|
586
|
+
advisorySeen.clear();
|
|
587
|
+
} catch {
|
|
588
|
+
/* never throw */
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/** Test hook: suppressed-repeat count pending for `key`. Never throws. */
|
|
593
|
+
export function _advisorySuppressed(key) {
|
|
594
|
+
try {
|
|
595
|
+
const entry = advisorySeen.get(key);
|
|
596
|
+
return entry ? entry.suppressed : 0;
|
|
597
|
+
} catch {
|
|
598
|
+
return 0;
|
|
510
599
|
}
|
|
511
600
|
}
|
|
512
601
|
|
|
@@ -715,23 +804,29 @@ async function editRow(dialog, host, draft, row) {
|
|
|
715
804
|
try {
|
|
716
805
|
const sub = row.schema || {};
|
|
717
806
|
// R4 advisories (never block the edit; the write still proceeds):
|
|
718
|
-
// an env-shadowed value
|
|
719
|
-
//
|
|
807
|
+
// an env-shadowed value is not effective while the override is set, and
|
|
808
|
+
// a deprecated key is ignored at runtime. Both route through the keyed
|
|
809
|
+
// dedup window (per-key repeats collapse); every other toast stays
|
|
810
|
+
// un-deduped so no error or degraded-path notice is ever swallowed.
|
|
720
811
|
if (row.source === 'env-override') {
|
|
721
|
-
|
|
812
|
+
warnOnce(
|
|
722
813
|
host,
|
|
814
|
+
`env:${row.key}`,
|
|
723
815
|
'Environment override',
|
|
724
|
-
`The environment shadows '${row.key}' —
|
|
725
|
-
'
|
|
726
|
-
'
|
|
816
|
+
`The environment shadows '${row.key}' — the file value is not ` +
|
|
817
|
+
'effective while the override is set and has no runtime effect ' +
|
|
818
|
+
'until the override is unset. The edited value is still stored ' +
|
|
819
|
+
'on save (cancel writes nothing).'
|
|
727
820
|
);
|
|
728
821
|
}
|
|
729
822
|
if (row.deprecated) {
|
|
730
|
-
|
|
823
|
+
warnOnce(
|
|
731
824
|
host,
|
|
825
|
+
`deprecated:${row.key}`,
|
|
732
826
|
'Deprecated field',
|
|
733
|
-
`'${row.key}' is deprecated and
|
|
734
|
-
'
|
|
827
|
+
`'${row.key}' is deprecated and ignored — it has no runtime ` +
|
|
828
|
+
'effect. The edited value is still stored on save for reference, ' +
|
|
829
|
+
'but changes nothing (cancel writes nothing).'
|
|
735
830
|
);
|
|
736
831
|
}
|
|
737
832
|
const types = Array.isArray(sub.type) ? sub.type : [sub.type];
|
|
@@ -999,6 +1094,15 @@ async function editAgentsRow(dialog, host, draft, row) {
|
|
|
999
1094
|
*/
|
|
1000
1095
|
export async function openSettingsWizard(host, root, opts = {}) {
|
|
1001
1096
|
try {
|
|
1097
|
+
// R1 session scope: a fresh wizard session always warns at least once.
|
|
1098
|
+
// The keyed R4 window collapses repeats WITHIN one session only; reset
|
|
1099
|
+
// the boundary here so a prior session (same key, <30s ago) never leaks
|
|
1100
|
+
// into the new session. Never throws.
|
|
1101
|
+
try {
|
|
1102
|
+
advisorySeen.clear();
|
|
1103
|
+
} catch {
|
|
1104
|
+
/* dedup reset is best-effort */
|
|
1105
|
+
}
|
|
1002
1106
|
const dialog = dialogOf(host);
|
|
1003
1107
|
const baseDir = researchDirOf(host, root);
|
|
1004
1108
|
const env = (opts && opts.env) || process.env;
|
|
@@ -17,6 +17,35 @@ from typing import Dict, Any, Optional, Tuple
|
|
|
17
17
|
DEFAULT_RESEARCH_DIR = ".research"
|
|
18
18
|
BASE_DIR_HELP = "Base .research directory"
|
|
19
19
|
|
|
20
|
+
#: Historical decision boundary for the fuzzy quote fallback (config
|
|
21
|
+
#: `verify.min_fuzzy_confidence`). This is the default whenever no threshold
|
|
22
|
+
#: is configured or the configured value is malformed.
|
|
23
|
+
DEFAULT_MIN_FUZZY_CONFIDENCE = 0.88
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def coerce_min_fuzzy_confidence(value: Any) -> float:
|
|
27
|
+
"""Coerce a `verify.min_fuzzy_confidence` candidate into [0, 1].
|
|
28
|
+
|
|
29
|
+
SINGLE-ENFORCEMENT-POINT CONTRACT (Phase 05 H5): this function is the one
|
|
30
|
+
definition of the coercion rule — string values are parsed with `float()`
|
|
31
|
+
(so a hand-edited `"0.95"` behaves like `0.95`), the result is clamped
|
|
32
|
+
into [0, 1], and anything malformed (None, non-numeric strings, NaN)
|
|
33
|
+
falls back to `DEFAULT_MIN_FUZZY_CONFIDENCE` (0.88, the strict
|
|
34
|
+
historical boundary). `SourceHasher.verify_quote` enforces it at call
|
|
35
|
+
time; every other layer (MCP `iumbtems_verify_quote`, the auditor, the
|
|
36
|
+
swarm runner) forwards its configured value untouched and must NOT
|
|
37
|
+
re-implement this rule. The persisted schema stays strict
|
|
38
|
+
(`type: number`): coercion exists only for hand-edited files and direct
|
|
39
|
+
library callers, never as an excuse to loosen validation.
|
|
40
|
+
"""
|
|
41
|
+
try:
|
|
42
|
+
threshold = float(value)
|
|
43
|
+
except (TypeError, ValueError):
|
|
44
|
+
return DEFAULT_MIN_FUZZY_CONFIDENCE
|
|
45
|
+
if threshold != threshold: # NaN: float("nan") parses but matches nothing
|
|
46
|
+
return DEFAULT_MIN_FUZZY_CONFIDENCE
|
|
47
|
+
return max(0.0, min(1.0, threshold))
|
|
48
|
+
|
|
20
49
|
|
|
21
50
|
def _log_retrieval(base_dir, kind: str, **fields) -> None:
|
|
22
51
|
"""Best-effort retrieval telemetry (see runner/retrieval_log.py)."""
|
|
@@ -146,15 +175,11 @@ class SourceHasher:
|
|
|
146
175
|
the historical decision boundary; callers that pass a value opt into a
|
|
147
176
|
stricter/looser match. Exact and normalized matches are unaffected.
|
|
148
177
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
178
|
+
Coercion is NOT done here inline: `coerce_min_fuzzy_confidence` (the
|
|
179
|
+
single enforcement point — see its contract) parses, clamps, and
|
|
180
|
+
defaults the threshold.
|
|
152
181
|
"""
|
|
153
|
-
|
|
154
|
-
threshold = float(min_fuzzy_confidence)
|
|
155
|
-
except (TypeError, ValueError):
|
|
156
|
-
threshold = 0.88
|
|
157
|
-
threshold = max(0.0, min(1.0, threshold))
|
|
182
|
+
threshold = coerce_min_fuzzy_confidence(min_fuzzy_confidence)
|
|
158
183
|
|
|
159
184
|
source_text = self.get_source_content(content_hash)
|
|
160
185
|
if not source_text:
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|