wogiflow 2.29.5 → 2.29.7
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/docs/claude-code-compatibility.md +2 -1
- package/.workflow/templates/claude-md.hbs +18 -34
- package/.workflow/templates/partials/methodology-rules.hbs +96 -105
- package/README.md +1 -1
- package/lib/wogi-claude +34 -3
- package/lib/wogi-claude-expect.exp +30 -5
- package/package.json +2 -2
- package/scripts/flow-defer-auth.js +103 -0
- package/scripts/flow-utils.js +52 -0
- package/scripts/hooks/core/deferral-classifier.js +129 -0
- package/scripts/hooks/core/deferral-gate.js +379 -0
- package/scripts/hooks/core/pre-tool-orchestrator.js +58 -0
- package/scripts/hooks/core/research-evidence-gate.js +11 -1
- package/scripts/hooks/core/research-required-classifier.js +205 -0
- package/scripts/hooks/core/research-required-gate.js +235 -0
- package/scripts/hooks/core/session-context.js +21 -0
- package/scripts/hooks/core/task-boundary-reset.js +132 -1
- package/scripts/hooks/entry/claude-code/stop.js +26 -0
- package/scripts/hooks/entry/claude-code/user-prompt-submit.js +39 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wogiflow",
|
|
3
|
-
"version": "2.29.
|
|
3
|
+
"version": "2.29.7",
|
|
4
4
|
"description": "AI-powered development workflow management system with multi-model support",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"flow": "./scripts/flow",
|
|
13
|
-
"test": "NODE_ENV=test node --test tests/auto-compact-prompt.test.js tests/flow-paths.test.js tests/flow-io.test.js tests/flow-config-loader.test.js tests/flow-damage-control.test.js tests/flow-output.test.js tests/flow-constants.test.js tests/flow-session-state.test.js tests/flow-hooks-integration.test.js tests/flow-utils.test.js tests/flow-security.test.js tests/flow-memory-db.test.js tests/flow-durable-session.test.js tests/flow-skill-matcher.test.js tests/flow-bridge.test.js tests/flow-proactive-compact.test.js tests/flow-cascade-completion.test.js tests/flow-capture-gate.test.js tests/flow-correction-detector-hybrid.test.js tests/flow-promote.test.js tests/flow-archive-runs.test.js tests/flow-memory.test.js tests/flow-hooks-pre-tool-helpers.test.js tests/flow-hooks-bugfix-scope-gate.test.js tests/flow-hooks-routing-gate.test.js tests/flow-hooks-phase-read-gate.test.js tests/flow-hooks-commit-log-gate.test.js tests/flow-hooks-deploy-gate.test.js tests/flow-hooks-todowrite-gate.test.js tests/flow-hooks-git-safety-gate.test.js tests/flow-hooks-scope-mutation-gate.test.js tests/flow-hooks-strike-gate.test.js tests/flow-hooks-component-check.test.js tests/flow-hooks-scope-gate.test.js tests/flow-hooks-implementation-gate.test.js tests/flow-hooks-research-gate.test.js tests/flow-hooks-loop-check.test.js tests/flow-hooks-manager-boundary-gate.test.js tests/flow-hooks-phase-gate.test.js tests/flow-hooks-pre-tool-orchestrator.test.js tests/flow-hooks-observation-capture.test.js tests/flow-hooks-task-gate.test.js tests/flow-durable-session-suspension.test.js tests/flow-health-mcp-scopes.test.js tests/flow-lean-config.test.js tests/flow-workspace-autopickup.test.js tests/flow-worker-boundary-gate.test.js tests/flow-worker-question-classifier.test.js tests/flow-completion-truth-gate-contradictions.test.js tests/flow-structure-sensor.test.js tests/flow-workspace-dispatch-tracking.test.js tests/workspace-ipc-sqlite.test.js tests/workspace-ipc-multi-worker.test.js tests/flow-story-gates.test.js tests/flow-workspace-restart-handoff.test.js tests/flow-wogi-claude-wrapper.test.js tests/flow-wave1-integrations.test.js tests/flow-wave2-integrations.test.js tests/flow-wave3-integrations.test.js tests/flow-commit-claims-gate.test.js tests/auto-review.test.js tests/gate-telemetry-surface.test.js tests/agents-md-alias.test.js tests/flow-skill-manage.test.js tests/fuzzy-patch.test.js tests/mode-schema.test.js tests/flow-feature-dossier.test.js tests/flow-autonomous-mode.test.js tests/flow-epic-cascade.test.js tests/flow-workspace-summary.test.js tests/flow-hooks-research-evidence-gate.test.js tests/flow-worker-mcp-strip.test.js tests/flow-orchestrate-corrections.test.js tests/flow-source-fidelity.test.js tests/flow-hooks-long-input-enforcement.test.js tests/workspace-channel-tracking.test.js tests/flow-hooks-deletion-log.test.js && NODE_ENV=test node tests/run-quality-gates.test.js",
|
|
13
|
+
"test": "NODE_ENV=test node --test tests/auto-compact-prompt.test.js tests/flow-paths.test.js tests/flow-io.test.js tests/flow-config-loader.test.js tests/flow-damage-control.test.js tests/flow-output.test.js tests/flow-constants.test.js tests/flow-session-state.test.js tests/flow-hooks-integration.test.js tests/flow-utils.test.js tests/flow-security.test.js tests/flow-memory-db.test.js tests/flow-durable-session.test.js tests/flow-skill-matcher.test.js tests/flow-bridge.test.js tests/flow-proactive-compact.test.js tests/flow-cascade-completion.test.js tests/flow-capture-gate.test.js tests/flow-correction-detector-hybrid.test.js tests/flow-promote.test.js tests/flow-archive-runs.test.js tests/flow-memory.test.js tests/flow-hooks-pre-tool-helpers.test.js tests/flow-hooks-bugfix-scope-gate.test.js tests/flow-hooks-routing-gate.test.js tests/flow-hooks-phase-read-gate.test.js tests/flow-hooks-commit-log-gate.test.js tests/flow-hooks-deploy-gate.test.js tests/flow-hooks-todowrite-gate.test.js tests/flow-hooks-git-safety-gate.test.js tests/flow-hooks-scope-mutation-gate.test.js tests/flow-hooks-strike-gate.test.js tests/flow-hooks-component-check.test.js tests/flow-hooks-scope-gate.test.js tests/flow-hooks-implementation-gate.test.js tests/flow-hooks-research-gate.test.js tests/flow-hooks-loop-check.test.js tests/flow-hooks-manager-boundary-gate.test.js tests/flow-hooks-phase-gate.test.js tests/flow-hooks-pre-tool-orchestrator.test.js tests/flow-hooks-observation-capture.test.js tests/flow-hooks-task-gate.test.js tests/flow-durable-session-suspension.test.js tests/flow-health-mcp-scopes.test.js tests/flow-lean-config.test.js tests/flow-workspace-autopickup.test.js tests/flow-worker-boundary-gate.test.js tests/flow-worker-question-classifier.test.js tests/flow-completion-truth-gate-contradictions.test.js tests/flow-structure-sensor.test.js tests/flow-workspace-dispatch-tracking.test.js tests/workspace-ipc-sqlite.test.js tests/workspace-ipc-multi-worker.test.js tests/flow-story-gates.test.js tests/flow-workspace-restart-handoff.test.js tests/flow-wogi-claude-wrapper.test.js tests/flow-wave1-integrations.test.js tests/flow-wave2-integrations.test.js tests/flow-wave3-integrations.test.js tests/flow-commit-claims-gate.test.js tests/auto-review.test.js tests/gate-telemetry-surface.test.js tests/agents-md-alias.test.js tests/flow-skill-manage.test.js tests/fuzzy-patch.test.js tests/mode-schema.test.js tests/flow-feature-dossier.test.js tests/flow-autonomous-mode.test.js tests/flow-epic-cascade.test.js tests/flow-workspace-summary.test.js tests/flow-hooks-research-evidence-gate.test.js tests/flow-worker-mcp-strip.test.js tests/flow-orchestrate-corrections.test.js tests/flow-source-fidelity.test.js tests/flow-hooks-long-input-enforcement.test.js tests/workspace-channel-tracking.test.js tests/flow-hooks-deletion-log.test.js tests/flow-task-boundary-reset.test.js tests/flow-deferral-gate.test.js tests/flow-research-required-gate.test.js && NODE_ENV=test node tests/run-quality-gates.test.js",
|
|
14
14
|
"test:syntax": "find scripts/ lib/ -name '*.js' -not -path '*/node_modules/*' -exec node --check {} +",
|
|
15
15
|
"lint": "eslint scripts/ lib/ tests/",
|
|
16
16
|
"lint:ci": "eslint scripts/ lib/ tests/ --max-warnings 0",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow — Deferral Authorization CLI (wf-f9912af6)
|
|
5
|
+
*
|
|
6
|
+
* Explicit user-authorization helper for the deferral gate. Used when the AI
|
|
7
|
+
* needs to record that the user picked a defer-style menu option in
|
|
8
|
+
* /wogi-review (e.g., "Create tasks for all - fix later in batches").
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* flow defer-auth grant --scope=all --reason="<verbatim user phrase>"
|
|
12
|
+
* flow defer-auth grant --findings=F5,F6,F7 --reason="..."
|
|
13
|
+
* flow defer-auth clear
|
|
14
|
+
* flow defer-auth status
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const gate = require('./hooks/core/deferral-gate');
|
|
19
|
+
|
|
20
|
+
function parseArgs(argv) {
|
|
21
|
+
const args = {};
|
|
22
|
+
for (const a of argv) {
|
|
23
|
+
if (a.startsWith('--')) {
|
|
24
|
+
const eq = a.indexOf('=');
|
|
25
|
+
if (eq === -1) {
|
|
26
|
+
args[a.slice(2)] = true;
|
|
27
|
+
} else {
|
|
28
|
+
args[a.slice(2, eq)] = a.slice(eq + 1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return args;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function cmdGrant(args) {
|
|
36
|
+
let scope = 'all';
|
|
37
|
+
if (args.findings) {
|
|
38
|
+
scope = String(args.findings)
|
|
39
|
+
.split(',')
|
|
40
|
+
.map(s => s.trim())
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
if (scope.length === 0) {
|
|
43
|
+
console.error('grant: --findings must be a non-empty comma-separated list');
|
|
44
|
+
process.exit(2);
|
|
45
|
+
}
|
|
46
|
+
} else if (args.scope === 'all' || args.scope === undefined) {
|
|
47
|
+
scope = 'all';
|
|
48
|
+
} else {
|
|
49
|
+
scope = String(args.scope);
|
|
50
|
+
}
|
|
51
|
+
const reason = args.reason ? String(args.reason) : 'cli-grant';
|
|
52
|
+
const ttlSec = args['ttl-sec'] ? parseInt(args['ttl-sec'], 10) : undefined;
|
|
53
|
+
|
|
54
|
+
const payload = gate.writeAuth({
|
|
55
|
+
scope,
|
|
56
|
+
source: reason,
|
|
57
|
+
grantedBy: 'explicit-cli',
|
|
58
|
+
ttlSec
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (!payload) {
|
|
62
|
+
console.error('grant: failed to write authorization marker');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
console.log(JSON.stringify({ status: 'granted', ...payload }, null, 2));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function cmdClear() {
|
|
69
|
+
gate.clearAuth();
|
|
70
|
+
gate.clearNoDeferPin();
|
|
71
|
+
console.log(JSON.stringify({ status: 'cleared' }, null, 2));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function cmdStatus() {
|
|
75
|
+
const auth = gate.loadAuth();
|
|
76
|
+
const pin = gate.loadNoDeferPin();
|
|
77
|
+
console.log(JSON.stringify({
|
|
78
|
+
authorization: auth || null,
|
|
79
|
+
noDeferPin: pin || null,
|
|
80
|
+
authPath: gate.getAuthPath(),
|
|
81
|
+
pinPath: gate.getNoDeferPinPath()
|
|
82
|
+
}, null, 2));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function usage() {
|
|
86
|
+
console.log('Usage: flow defer-auth <grant|clear|status> [--scope=all|<id>] [--findings=F1,F2] [--reason="..."] [--ttl-sec=600]');
|
|
87
|
+
process.exit(2);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function main() {
|
|
91
|
+
const [, , subcommand, ...rest] = process.argv;
|
|
92
|
+
const args = parseArgs(rest);
|
|
93
|
+
switch (subcommand) {
|
|
94
|
+
case 'grant': return cmdGrant(args);
|
|
95
|
+
case 'clear': return cmdClear();
|
|
96
|
+
case 'status': return cmdStatus();
|
|
97
|
+
default: return usage();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (require.main === module) main();
|
|
102
|
+
|
|
103
|
+
module.exports = { parseArgs, cmdGrant, cmdClear, cmdStatus };
|
package/scripts/flow-utils.js
CHANGED
|
@@ -336,6 +336,7 @@ function saveReadyData(data) {
|
|
|
336
336
|
const toSave = { ...data, lastUpdated: new Date().toISOString() };
|
|
337
337
|
const result = writeJson(PATHS.ready, toSave);
|
|
338
338
|
invalidateReadyDataCache(); // Invalidate AFTER write completes to avoid stale cache race
|
|
339
|
+
maybeArmTaskBoundaryRestart(previousData, toSave);
|
|
339
340
|
return result;
|
|
340
341
|
}
|
|
341
342
|
|
|
@@ -359,10 +360,61 @@ async function saveReadyDataAsync(data) {
|
|
|
359
360
|
const toSave = { ...data, lastUpdated: new Date().toISOString() };
|
|
360
361
|
const result = writeJson(PATHS.ready, toSave);
|
|
361
362
|
invalidateReadyDataCache(); // Invalidate AFTER write completes
|
|
363
|
+
maybeArmTaskBoundaryRestart(previousData, toSave);
|
|
362
364
|
return result;
|
|
363
365
|
});
|
|
364
366
|
}
|
|
365
367
|
|
|
368
|
+
/**
|
|
369
|
+
* wf-ee4e343b — Phase 1 chokepoint for task-boundary auto-restart.
|
|
370
|
+
*
|
|
371
|
+
* Why this exists: Phase 1 marker writes were previously split across three
|
|
372
|
+
* disjoint paths (`flow done`, `task-completed.js` hook, Stop-hook fallback
|
|
373
|
+
* with a 5-min freshness window). The Stop-hook fallback misses real-world
|
|
374
|
+
* timing (user takes >5min to type next message → fallback rejects) and the
|
|
375
|
+
* other two paths are not always called. By detecting "new entry in
|
|
376
|
+
* recentlyCompleted" right here in saveReadyData — the actual chokepoint
|
|
377
|
+
* every completion goes through — we arm the marker at the moment of
|
|
378
|
+
* completion regardless of who completed the task.
|
|
379
|
+
*
|
|
380
|
+
* Gated on WOGI_WRAPPER_PID so test/CLI/non-wrapper invocations don't
|
|
381
|
+
* write spurious markers. Lazy-required to avoid circular dependency
|
|
382
|
+
* (task-boundary-reset.js → flow-utils.js).
|
|
383
|
+
*/
|
|
384
|
+
function maybeArmTaskBoundaryRestart(previousData, savedData) {
|
|
385
|
+
try {
|
|
386
|
+
if (!process.env.WOGI_WRAPPER_PID) return;
|
|
387
|
+
// First-save guard (F2): when ready.json doesn't yet exist, previousData
|
|
388
|
+
// is null. If savedData arrives pre-populated (fresh install seeded from
|
|
389
|
+
// backup, init script bootstrapping recentlyCompleted, etc.) we MUST NOT
|
|
390
|
+
// arm a restart marker — there's no completion event, just an initial
|
|
391
|
+
// state snapshot. Real completions always have a previousData to diff
|
|
392
|
+
// against because saveReadyData is the only writer.
|
|
393
|
+
if (!previousData) return;
|
|
394
|
+
// F7 fix (wf-ee4e343b cleanup): F2 was asymmetric — readJson returns {}
|
|
395
|
+
// (truthy) on corrupt JSON or missing top-level keys, so the !previousData
|
|
396
|
+
// guard would not catch that case. A corrupt ready.json that recovers as
|
|
397
|
+
// {} followed by a save with populated recentlyCompleted would still
|
|
398
|
+
// false-positive. Require previousData.recentlyCompleted to be an actual
|
|
399
|
+
// array for the diff to be meaningful — anything else is "we don't know
|
|
400
|
+
// the prior state," which is structurally identical to first-save and
|
|
401
|
+
// must NOT arm.
|
|
402
|
+
if (!Array.isArray(previousData.recentlyCompleted)) return;
|
|
403
|
+
const prevTop = previousData.recentlyCompleted[0];
|
|
404
|
+
const curTop = savedData?.recentlyCompleted?.[0];
|
|
405
|
+
if (!curTop || !curTop.id) return;
|
|
406
|
+
if (prevTop && prevTop.id === curTop.id) return; // no new completion
|
|
407
|
+
const { markRestartPending } = require('./hooks/core/task-boundary-reset');
|
|
408
|
+
markRestartPending({
|
|
409
|
+
taskId: curTop.id,
|
|
410
|
+
taskTitle: curTop.title,
|
|
411
|
+
source: 'saveReadyData'
|
|
412
|
+
});
|
|
413
|
+
} catch (_err) {
|
|
414
|
+
// Fail-open — never let an observability/marker write break ready.json save
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
366
418
|
/**
|
|
367
419
|
* Archive overflow completed tasks to a log file (v3.2)
|
|
368
420
|
* When recentlyCompleted exceeds 10 items, archive the overflow
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow — Deferral Intent Classifier (wf-f9912af6)
|
|
5
|
+
*
|
|
6
|
+
* Regex-based detector for explicit user deferral intent in UserPromptSubmit
|
|
7
|
+
* messages. Cheap (no Haiku call), deterministic, runs every prompt.
|
|
8
|
+
*
|
|
9
|
+
* NEGATIVE intent takes precedence over POSITIVE — if the user says both
|
|
10
|
+
* "fix everything" and "skip Y" in the same message, we assume they want
|
|
11
|
+
* everything fixed (the defer-everything pattern is the dangerous one this
|
|
12
|
+
* gate exists to stop).
|
|
13
|
+
*
|
|
14
|
+
* Negative match → write `no-defer-pin.json` (HARD block, overrides any auth)
|
|
15
|
+
* Positive match → write `deferral-authorization.json` (allows specific scope)
|
|
16
|
+
* Neither → no-op
|
|
17
|
+
*
|
|
18
|
+
* Fail-open: any error in classification falls through silently.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// Negative phrases (HIGH PRIORITY — clear auth, write no-defer pin)
|
|
22
|
+
const NEGATIVE_PATTERNS = [
|
|
23
|
+
/\bfix\s+(everything|all\s+of\s+(them|it)|all\s+findings?)\b/i,
|
|
24
|
+
/\bno\s+deferr?als?\b/i,
|
|
25
|
+
/\b(don'?t|do\s+not)\s+defer\b/i,
|
|
26
|
+
/\bi\s+don'?t\s+(want|like)\s+(tech\s*-?\s*debt|technical\s*-?\s*debt|deferr?al)/i,
|
|
27
|
+
/\bnever\s+defer\b/i,
|
|
28
|
+
/\balways\s+fix\s+(what'?s\s+broken|what\s+needs?\s+fixing)/i,
|
|
29
|
+
/\bnothing\s+(should\s+be|gets)\s+deferr?ed\b/i,
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// Positive phrases (MEDIUM PRIORITY — write auth marker)
|
|
33
|
+
// We're conservative: require defer/skip phrasing to be coupled with finding
|
|
34
|
+
// context (this/that/those/it/option N/F\d+/severity word) to avoid catching
|
|
35
|
+
// unrelated mentions like "let's defer the meeting".
|
|
36
|
+
const POSITIVE_PATTERNS = [
|
|
37
|
+
// "defer X" / "skip X" with a referent
|
|
38
|
+
/\b(defer|skip|ignore|drop)\s+(this|that|those|it|them|f\d+|finding\s+\w+)\b/i,
|
|
39
|
+
/\bleave\s+(this|that|those|f\d+|.*?)\s+(for\s+)?later\b/i,
|
|
40
|
+
|
|
41
|
+
// /wogi-review menu options that mean defer
|
|
42
|
+
/\boption\s*[24]\b/i, // option 2 = "fix critical only"; option 4 = "create tasks for all (defer)"
|
|
43
|
+
/\bcreate\s+tasks?\s+for\s+(all|the\s+rest|remaining)\b/i,
|
|
44
|
+
|
|
45
|
+
// Severity-scoped deferrals
|
|
46
|
+
/\bfix\s+(only\s+)?(critical|high)\s*(\s*\/\s*high)?\s+only\b/i,
|
|
47
|
+
/\bfix\s+(critical|high)\s+(only|first)\b/i,
|
|
48
|
+
/\bskip\s+(low|medium|low\s*\/\s*medium)\b/i,
|
|
49
|
+
|
|
50
|
+
// Ship-as-is style
|
|
51
|
+
/\bship\s+(it\s+)?as\s*-?\s*is\b/i,
|
|
52
|
+
/\bgood\s+enough\s+(as\s*-?\s*is|for\s+now)\b/i,
|
|
53
|
+
/\bcall\s+it\s+(done|good)\b/i,
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Classify a user prompt for deferral intent.
|
|
58
|
+
*
|
|
59
|
+
* @param {string} prompt - the user's UserPromptSubmit text
|
|
60
|
+
* @returns {{ intent: 'negative'|'positive'|'none', match?: string, scope?: string|string[] }}
|
|
61
|
+
*/
|
|
62
|
+
function classifyDeferralIntent(prompt) {
|
|
63
|
+
if (!prompt || typeof prompt !== 'string') return { intent: 'none' };
|
|
64
|
+
|
|
65
|
+
// Negative first — overrides positive
|
|
66
|
+
for (const rx of NEGATIVE_PATTERNS) {
|
|
67
|
+
const m = prompt.match(rx);
|
|
68
|
+
if (m) return { intent: 'negative', match: m[0] };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Positive
|
|
72
|
+
for (const rx of POSITIVE_PATTERNS) {
|
|
73
|
+
const m = prompt.match(rx);
|
|
74
|
+
if (m) {
|
|
75
|
+
// Try to extract scope — look for F\d+ ids in the prompt
|
|
76
|
+
const findingIds = Array.from(prompt.matchAll(/\bF\d+\b/g)).map(x => x[0]);
|
|
77
|
+
return {
|
|
78
|
+
intent: 'positive',
|
|
79
|
+
match: m[0],
|
|
80
|
+
scope: findingIds.length > 0 ? findingIds : 'all'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { intent: 'none' };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Apply classification result to the gate's state files. Wired into
|
|
90
|
+
* UserPromptSubmit. Fail-open throughout.
|
|
91
|
+
*/
|
|
92
|
+
function applyClassification(prompt, config) {
|
|
93
|
+
try {
|
|
94
|
+
if (config?.deferralGate?.classifyUserPrompts === false) return { applied: false, reason: 'classifier-disabled' };
|
|
95
|
+
|
|
96
|
+
const result = classifyDeferralIntent(prompt);
|
|
97
|
+
if (result.intent === 'none') return { applied: false, reason: 'no-match' };
|
|
98
|
+
|
|
99
|
+
// Lazy-require to avoid load-order coupling
|
|
100
|
+
const gate = require('./deferral-gate');
|
|
101
|
+
|
|
102
|
+
if (result.intent === 'negative') {
|
|
103
|
+
gate.writeNoDeferPin({ source: result.match });
|
|
104
|
+
return { applied: true, intent: 'negative', match: result.match };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (result.intent === 'positive') {
|
|
108
|
+
gate.writeAuth({
|
|
109
|
+
scope: result.scope,
|
|
110
|
+
source: result.match,
|
|
111
|
+
grantedBy: 'user-prompt',
|
|
112
|
+
config
|
|
113
|
+
});
|
|
114
|
+
return { applied: true, intent: 'positive', match: result.match, scope: result.scope };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { applied: false, reason: 'unhandled-intent' };
|
|
118
|
+
} catch (err) {
|
|
119
|
+
if (process.env.DEBUG) console.error(`[deferral-classifier] applyClassification error (fail-open): ${err.message}`);
|
|
120
|
+
return { applied: false, reason: `error: ${err.message}` };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
classifyDeferralIntent,
|
|
126
|
+
applyClassification,
|
|
127
|
+
NEGATIVE_PATTERNS,
|
|
128
|
+
POSITIVE_PATTERNS
|
|
129
|
+
};
|