open-multi-agent-kit 0.90.3 → 0.90.5
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/CHANGELOG.md +38 -0
- package/README.md +16 -14
- package/dist/config.d.ts +3 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +27 -19
- package/dist/config.js.map +1 -1
- package/dist/core/adaptorch-bridge.d.ts +250 -0
- package/dist/core/adaptorch-bridge.d.ts.map +1 -0
- package/dist/core/adaptorch-bridge.js +466 -0
- package/dist/core/adaptorch-bridge.js.map +1 -0
- package/dist/core/agent-session.d.ts +97 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +194 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bang-skill-invocation.d.ts.map +1 -1
- package/dist/core/bang-skill-invocation.js +149 -0
- package/dist/core/bang-skill-invocation.js.map +1 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +15 -2
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
- package/dist/core/context-budget-v2-planner.js +10 -2
- package/dist/core/context-budget-v2-planner.js.map +1 -1
- package/dist/core/context-budget-v2-types.d.ts +7 -1
- package/dist/core/context-budget-v2-types.d.ts.map +1 -1
- package/dist/core/context-budget-v2-types.js.map +1 -1
- package/dist/core/domain-loadouts.d.ts.map +1 -1
- package/dist/core/domain-loadouts.js +1 -1
- package/dist/core/domain-loadouts.js.map +1 -1
- package/dist/core/reasoning-router-bias.d.ts +107 -0
- package/dist/core/reasoning-router-bias.d.ts.map +1 -0
- package/dist/core/reasoning-router-bias.js +278 -0
- package/dist/core/reasoning-router-bias.js.map +1 -0
- package/dist/core/reasoning-router-resolver.d.ts +44 -0
- package/dist/core/reasoning-router-resolver.d.ts.map +1 -0
- package/dist/core/reasoning-router-resolver.js +79 -0
- package/dist/core/reasoning-router-resolver.js.map +1 -0
- package/dist/core/reasoning-router-v4-weights.d.ts +80 -0
- package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -0
- package/dist/core/reasoning-router-v4-weights.js +68 -0
- package/dist/core/reasoning-router-v4-weights.js.map +1 -0
- package/dist/core/reasoning-router-v4.d.ts +118 -0
- package/dist/core/reasoning-router-v4.d.ts.map +1 -0
- package/dist/core/reasoning-router-v4.js +585 -0
- package/dist/core/reasoning-router-v4.js.map +1 -0
- package/dist/core/router-feedback-collector.d.ts +98 -0
- package/dist/core/router-feedback-collector.d.ts.map +1 -0
- package/dist/core/router-feedback-collector.js +236 -0
- package/dist/core/router-feedback-collector.js.map +1 -0
- package/dist/core/settings-manager.d.ts +28 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +16 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +4 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/modes/interactive/components/control-panel-runtime-status.d.ts +15 -0
- package/dist/modes/interactive/components/control-panel-runtime-status.d.ts.map +1 -1
- package/dist/modes/interactive/components/control-panel-runtime-status.js +95 -5
- package/dist/modes/interactive/components/control-panel-runtime-status.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +60 -17
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/loadout-domains/README.md +8 -6
- package/docs/loadout-domains/ai-agent-ops.md +3 -2
- package/docs/loadout-domains/backend-api.md +3 -2
- package/docs/loadout-domains/data-science.md +3 -2
- package/docs/loadout-domains/devops-infra.md +2 -1
- package/docs/loadout-domains/docs-writing.md +7 -2
- package/docs/loadout-domains/frontend-ui.md +42 -11
- package/docs/loadout-domains/korean-document.md +83 -0
- package/docs/loadout-domains/mobile.md +1 -1
- package/docs/loadout-domains/qa-testing.md +4 -2
- package/docs/loadout-domains/security-audit.md +2 -1
- package/docs/loadout-domains/visual-qa.md +94 -0
- package/docs/skills.md +4 -1
- package/docs/usage.md +36 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/agent-capability-router.test.ts +123 -0
- package/examples/extensions/subagent/agent-capability-router.ts +274 -0
- package/examples/extensions/subagent/agents/planner.md +3 -0
- package/examples/extensions/subagent/agents/reviewer.md +3 -0
- package/examples/extensions/subagent/agents/scout.md +3 -0
- package/examples/extensions/subagent/agents/worker.md +2 -0
- package/examples/extensions/subagent/agents.ts +14 -0
- package/examples/extensions/subagent/capabilities.ts +368 -0
- package/examples/extensions/subagent/domain-profiles.ts +1089 -0
- package/examples/extensions/subagent/index.ts +71 -3
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
- package/dist/agents/jailbreak/index.d.ts +0 -7
- package/dist/agents/jailbreak/index.d.ts.map +0 -1
- package/dist/agents/jailbreak/index.js +0 -7
- package/dist/agents/jailbreak/index.js.map +0 -1
- package/dist/agents/jailbreak/subagent-f.d.ts +0 -44
- package/dist/agents/jailbreak/subagent-f.d.ts.map +0 -1
- package/dist/agents/jailbreak/subagent-f.js +0 -60
- package/dist/agents/jailbreak/subagent-f.js.map +0 -1
- package/dist/cli/jailbreak-args.d.ts +0 -26
- package/dist/cli/jailbreak-args.d.ts.map +0 -1
- package/dist/cli/jailbreak-args.js +0 -86
- package/dist/cli/jailbreak-args.js.map +0 -1
- package/dist/commands/jailbreak.d.ts +0 -49
- package/dist/commands/jailbreak.d.ts.map +0 -1
- package/dist/commands/jailbreak.js +0 -201
- package/dist/commands/jailbreak.js.map +0 -1
- package/dist/core/compactor.d.ts +0 -42
- package/dist/core/compactor.d.ts.map +0 -1
- package/dist/core/compactor.js +0 -128
- package/dist/core/compactor.js.map +0 -1
- package/dist/core/token-optimizer.d.ts +0 -58
- package/dist/core/token-optimizer.d.ts.map +0 -1
- package/dist/core/token-optimizer.js +0 -179
- package/dist/core/token-optimizer.js.map +0 -1
- package/dist/encoding/index.d.ts +0 -6
- package/dist/encoding/index.d.ts.map +0 -1
- package/dist/encoding/index.js +0 -6
- package/dist/encoding/index.js.map +0 -1
- package/dist/encoding/l4-encoder.d.ts +0 -96
- package/dist/encoding/l4-encoder.d.ts.map +0 -1
- package/dist/encoding/l4-encoder.js +0 -474
- package/dist/encoding/l4-encoder.js.map +0 -1
- package/dist/fuzzing/index.d.ts +0 -6
- package/dist/fuzzing/index.d.ts.map +0 -1
- package/dist/fuzzing/index.js +0 -6
- package/dist/fuzzing/index.js.map +0 -1
- package/dist/fuzzing/mutation-engine.d.ts +0 -93
- package/dist/fuzzing/mutation-engine.d.ts.map +0 -1
- package/dist/fuzzing/mutation-engine.js +0 -244
- package/dist/fuzzing/mutation-engine.js.map +0 -1
- package/dist/jailbreak-extension.d.ts +0 -48
- package/dist/jailbreak-extension.d.ts.map +0 -1
- package/dist/jailbreak-extension.js +0 -47
- package/dist/jailbreak-extension.js.map +0 -1
- package/dist/modules/index.d.ts +0 -8
- package/dist/modules/index.d.ts.map +0 -1
- package/dist/modules/index.js +0 -7
- package/dist/modules/index.js.map +0 -1
- package/dist/modules/lrl-bypass.d.ts +0 -40
- package/dist/modules/lrl-bypass.d.ts.map +0 -1
- package/dist/modules/lrl-bypass.js +0 -84
- package/dist/modules/lrl-bypass.js.map +0 -1
- package/dist/modules/lrl-deps.d.ts +0 -38
- package/dist/modules/lrl-deps.d.ts.map +0 -1
- package/dist/modules/lrl-deps.js +0 -128
- package/dist/modules/lrl-deps.js.map +0 -1
- package/dist/multiturn/easl-chain.d.ts +0 -113
- package/dist/multiturn/easl-chain.d.ts.map +0 -1
- package/dist/multiturn/easl-chain.js +0 -351
- package/dist/multiturn/easl-chain.js.map +0 -1
- package/dist/multiturn/index.d.ts +0 -6
- package/dist/multiturn/index.d.ts.map +0 -1
- package/dist/multiturn/index.js +0 -6
- package/dist/multiturn/index.js.map +0 -1
- package/dist/routing/fallback-router.d.ts +0 -123
- package/dist/routing/fallback-router.d.ts.map +0 -1
- package/dist/routing/fallback-router.js +0 -355
- package/dist/routing/fallback-router.js.map +0 -1
- package/dist/routing/index.d.ts +0 -6
- package/dist/routing/index.d.ts.map +0 -1
- package/dist/routing/index.js +0 -6
- package/dist/routing/index.js.map +0 -1
- package/dist/types/jailbreak.d.ts +0 -246
- package/dist/types/jailbreak.d.ts.map +0 -1
- package/dist/types/jailbreak.js +0 -8
- package/dist/types/jailbreak.js.map +0 -1
- package/dist/utils/jailbreak/common.d.ts +0 -63
- package/dist/utils/jailbreak/common.d.ts.map +0 -1
- package/dist/utils/jailbreak/common.js +0 -130
- package/dist/utils/jailbreak/common.js.map +0 -1
- package/dist/utils/jailbreak/index.d.ts +0 -7
- package/dist/utils/jailbreak/index.d.ts.map +0 -1
- package/dist/utils/jailbreak/index.js +0 -7
- package/dist/utils/jailbreak/index.js.map +0 -1
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reasoning-router v4 — the sole `/think auto` classifier/resolver.
|
|
3
|
+
*
|
|
4
|
+
* Deterministic by construction: same (input, weights) -> same
|
|
5
|
+
* ClassifierVerdictV4; same (verdict, availableLevels, laneType, bias, hint) ->
|
|
6
|
+
* same ThinkingLevel. No clock, randomness, I/O, model calls, network access,
|
|
7
|
+
* or state mutation anywhere in this file (ThinkingLevel is a type-only import
|
|
8
|
+
* and is erased).
|
|
9
|
+
*
|
|
10
|
+
* `classifyTaskV4` returns a confidence-bearing verdict: task class, per-class
|
|
11
|
+
* scores, runner-up, margin, confidence band, tie-break flag, fallback reason,
|
|
12
|
+
* bounded-negation audit ids, and compound-intent metadata. None of this carries
|
|
13
|
+
* prompt text — every field is a bounded enum, number, boolean, or a closed set
|
|
14
|
+
* of short diagnostic-id strings.
|
|
15
|
+
*
|
|
16
|
+
* `resolveThinkingLevelV4WithUncertainty` starts from the canonical task-class
|
|
17
|
+
* rule table, applies lane/bias/hint adjustments, and only adds non-negative
|
|
18
|
+
* confidence escalation. A prompt cannot lower its own effort by asking the
|
|
19
|
+
* router to "think less"; low confidence or fallback routing can only hold or
|
|
20
|
+
* raise the resolved level.
|
|
21
|
+
*/
|
|
22
|
+
import { DEFAULT_WEIGHTS_V4, TASK_CLASSES_V4, } from "./reasoning-router-v4-weights.js";
|
|
23
|
+
export { DEFAULT_WEIGHTS_V4, TASK_CLASSES_V4 } from "./reasoning-router-v4-weights.js";
|
|
24
|
+
import { resolveThinkingLevelCore, TASK_CLASS_THINKING_LEVELS } from "./reasoning-router-resolver.js";
|
|
25
|
+
/** Prompts shorter than this (trimmed) fall back to trivial when no real signal scores > 0. */
|
|
26
|
+
const TRIVIAL_MAX_CHARS_V4 = 40;
|
|
27
|
+
/** Prompts at/above this length with no real signal fall back to plan (long prose brief). */
|
|
28
|
+
const COMPLEX_PROSE_MIN_CHARS_V4 = 2400;
|
|
29
|
+
/** Plan-brief prompts at/above this length count as a "long brief" even without PLAN_BRIEF_PATTERN. */
|
|
30
|
+
const LONG_BRIEF_MIN_CHARS_V4 = 512;
|
|
31
|
+
/** Compound-clause split only applies within this many leading characters (short direct commands, not long prose). */
|
|
32
|
+
const COMPOUND_SPLIT_MAX_INDEX_V4 = 300;
|
|
33
|
+
/** Minimum trimmed length of a candidate second clause to be worth checking for a leading intent. */
|
|
34
|
+
const COMPOUND_SECOND_CLAUSE_MIN_CHARS_V4 = 3;
|
|
35
|
+
/** Bounded look-ahead cap when checking a second clause's leading intent (defense in depth; independent of regex cost). */
|
|
36
|
+
const COMPOUND_SECOND_CLAUSE_SCAN_CHARS_V4 = 200;
|
|
37
|
+
/** Lane fallback class, used only when every scored class is <= 0 (zero-score cascade). */
|
|
38
|
+
const LANE_FALLBACK_CLASS_V4 = {
|
|
39
|
+
planner: "plan",
|
|
40
|
+
security: "review",
|
|
41
|
+
explorer: "review",
|
|
42
|
+
coder: "code-gen",
|
|
43
|
+
reviewer: "review",
|
|
44
|
+
tester: "code-gen",
|
|
45
|
+
};
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// Whole-prompt / leading-clause patterns used by the v4 scorer.
|
|
48
|
+
// ============================================================================
|
|
49
|
+
const LOCAL_EDIT_OBJECT_PATTERN = /\b(spelling|grammar|capitalization|date\s+format|author\s+e-?mail|copyright\s+year|headline|title|tooltip|placeholder|punctuation|comma|period|semicolon|closing\s+html\s+tag|closing\s+tag|double\s+space|whitespace|indentation|table\s+alignment|typos?|one-?liner?|single\s+line|sentence|stray|trailing)\b|오타|맞춤법|띄어쓰기|문구|제목/i;
|
|
50
|
+
const LOCAL_EDIT_ACTION_OBJECT_PATTERN = /\b(update|change|swap|remove|correct|fix|add|adjust|trim)\s+(?:the\s+|a\s+|an\s+)?(?:missing\s+|stray\s+|author\s+|two\s+)?(?:e-?mail|comma|period|semicolon|tag|word|words|headline|title|copyright|spelling|grammar)\b/i;
|
|
51
|
+
const IMPLEMENTATION_OBJECT_PATTERN = /\b(error\s+handling|input\s+validation|validation|auth(?:entication)?|retry|endpoint|migration|component|function|utility|helper|module|service|database|schema|column|middleware|rate\s+limiter|cache|caching|oauth|jwt|webhook)\b|에러\s*처리|오류\s*처리|입력\s*검증|인증|재시도|엔드포인트|마이그레이션|컴포넌트|함수|유틸|헬퍼|모듈|서비스|데이터베이스|스키마|미들웨어|레이트\s*리미터|캐시|웹훅|기능/i;
|
|
52
|
+
const HARD_DIAGNOSTIC_PATTERN = /\b(stack\s*trace|traceback|panic|segfault|crash(?:es|ing|ed)?|throws?|fails?|failing|flaky|hangs?|timeout|500|EADDRINUSE|silently\s+fails?)\b|\b(?:TypeError|ReferenceError|RangeError|Error):|크래시|스택\s*트레이스|세그폴트|먹통|멈춰|타임아웃/i;
|
|
53
|
+
const BUG_OBJECT_PATTERN = /\b(null\s+pointer|null\s+deref|race(?:\s+condition)?|heap(?:\s+overflow)?|use-after-free|memory\s+leak|leaks?|deadlock|data\s+corruption|stale\s+data|off-by-one|encoding\s+bug|regression\s+(?:was\s+)?introduced|exceptions?|assertion\s+error|bugs?)\b|버그|예외|메모리\s*누수|데드락|무한\s*루프/i;
|
|
54
|
+
const NON_DIAGNOSTIC_DEBUG_CONTEXT_PATTERN = /\berror\s+(?:handling|messages?|budgets?)\b|에러\s*처리|오류\s*처리/i;
|
|
55
|
+
const GENERIC_DIAGNOSTIC_PATTERN = /\b(errors?|broken|wrong\s+results|rolls?\s+back|rollback)\b/i;
|
|
56
|
+
const PLAN_BRIEF_PATTERN = /\b(context\s+and\s+constraints|starting\s+state|target\s+state|cross-cutting|deliver:|deliverables?|roadmap|migration\s+wave|top\s+(?:ten\s+)?risks|component\s+diagram|data\s+model|architecture|go\/no-go|phased\s+(?:delivery|rollout)|bounded\s+contexts?|strangler\s+fig|event-driven|quarter-by-quarter|milestone)\b/i;
|
|
57
|
+
const OPERATIONAL_RUNBOOK_SIGNAL_PATTERNS = [
|
|
58
|
+
{ pattern: /\b(?:commit|commits|committed|committing)\b|커밋/i, critical: false },
|
|
59
|
+
{ pattern: /\b(?:push|pushed|pushing)\b|푸시|푸쉬/i, critical: true },
|
|
60
|
+
{
|
|
61
|
+
pattern: /\b(?:release|releases|tag|version\s+bump|bump\s+(?:the\s+)?version)\b|릴리즈|태그|버전/i,
|
|
62
|
+
critical: true,
|
|
63
|
+
},
|
|
64
|
+
{ pattern: /\bnpm\s+publish\b|\bpublish(?:ing|ed)?\b|퍼블리시|배포/i, critical: true },
|
|
65
|
+
{ pattern: /\b(?:CHANGELOG\.md|changelog|release\s+notes?)\b|채널로그|체인지로그/i, critical: false },
|
|
66
|
+
{ pattern: /\bREADME\.md\b|\breadme\b/i, critical: false },
|
|
67
|
+
{ pattern: /\bci\s*\/\s*cd\b|\bgithub\s+actions?\b|\bworkflow\b|깃허브\s*액션|깃헙\s*액션/i, critical: false },
|
|
68
|
+
];
|
|
69
|
+
const EXPLICIT_RELEASE_RUNBOOK_PATTERN = /\bnpm\s+publish\b|\brelease\b[^\n]{0,80}\bv?\d+\.\d+\.\d+\b|릴리즈[^\n]{0,80}\d+\.\d+\.\d+/i;
|
|
70
|
+
const REVIEW_SCOPE_PATTERN = /\b(pr|pull\s+request|diff|codebase|strategy|plan|spec|schema|design|api|coverage|security\s+posture|licensing|risks?|edge\s+cases|clarity|consistency|dependencies|third-party|ci\s+pipeline|threat\s+model|retry\s+logic|error\s+handling\s+strategy|error\s+messages?)\b/i;
|
|
71
|
+
const REFACTOR_CUE_PATTERN = /\b(refactor(?:ing|ed)?|extract|rename|deduplicate|consolidate|modularize|reorganize|simplify|clean\s*up|tidy\s+up|restructure|split\s+module|move\s+logic|untangle|merge\s+duplicate)\b|리팩토링(?!하지\s*마|하지\s*말)|리팩터링(?!하지\s*마|하지\s*말)|구조\s*개선/i;
|
|
72
|
+
const ADD_KEYWORD_PATTERN = /\badd\b/i;
|
|
73
|
+
const LOW_RISK_EDIT_ACTION_PATTERN = /^(?:correct|update|swap|remove|reword|tweak|bump|trim)\b/i;
|
|
74
|
+
/** Whole-prompt keyword-family patterns; refactor/simple-edit reuse the patterns above. */
|
|
75
|
+
const DEBUG_KEYWORD_FAMILY_PATTERN = /^(?:debug|investigate\s+why|reproduce|trace\b)|\bfix\s+this\s+(?:traceback|panic|error)\b|\btrack\s+down\b|디버깅|디버그|재현|원인\s*분석/i;
|
|
76
|
+
const REVIEW_KEYWORD_FAMILY_PATTERN = /\b(review|critique|assess|inspect|approve|lgtm|double-?check|sanity[-\s]?check|once-?over|eyeball|tell\s+me\s+(?:if|whether)[^.!?;\n]{0,80}\bcorrect|audit(?!\s+log\b))\b|리뷰|검토|점검/i;
|
|
77
|
+
const PLAN_KEYWORD_FAMILY_PATTERN = /\b(plan|design|architect|architecture|roadmap|spec(?:ification)?|strategy|decompose|milestones?|map\s+out|phases|write\s+a\s+(?:technical\s+)?spec|create\s+a\s+(?:roadmap|strategy|plan))\b|설계|로드맵|아키텍처|계획.{0,6}세워|기획/i;
|
|
78
|
+
const CODE_GEN_KEYWORD_FAMILY_PATTERN = /\b(implement|write|create|build|generate|scaffold|prototype)\b|구현|만들어\s*줘|생성|작성|추가해/i;
|
|
79
|
+
const KO_SHORT_DEBUG_SIGNAL_PATTERN_V4 = /오류|에러|실패|안\s*돼|안\s*됨|깨졌|깨짐|고장|디버깅|디버그|재현|원인\s*(?:분석|파악)?/i;
|
|
80
|
+
const KO_SHORT_SIMPLE_EDIT_SIGNAL_PATTERN_V4 = /오타|맞춤법|띄어쓰기|문구|제목/i;
|
|
81
|
+
const KO_SHORT_REVIEW_SIGNAL_PATTERN_V4 = /리뷰|검토|점검/i;
|
|
82
|
+
const KO_SHORT_PLAN_SIGNAL_PATTERN_V4 = /설계|로드맵|아키텍처|계획.{0,6}세워|기획/i;
|
|
83
|
+
const KO_SHORT_REFACTOR_SIGNAL_PATTERN_V4 = /리팩토링|리팩터링|구조\s*개선/i;
|
|
84
|
+
const KO_SHORT_CODE_GEN_SIGNAL_PATTERN_V4 = /구현|만들|생성|작성|추가|테스트|삭제|지워|지우|제거|고쳐|고치|수정|변경|바꿔|바꾸|옮겨|옮기|넣어/i;
|
|
85
|
+
/** Short-range compound-clause conjunction boundary. Only bare/compound "then"/"and also"/";" — never bare "and". */
|
|
86
|
+
const COMPOUND_SPLIT_PATTERN = /\band\s+then\b|\bthen\b|\band\s+also\b|;/i;
|
|
87
|
+
/**
|
|
88
|
+
* Bounded negation-cue vocabulary (spec 008 plan.md "bounded negation window").
|
|
89
|
+
* Deliberately excludes bare "not" (too broad; verified against the current
|
|
90
|
+
* GOLD_SET that every listed cue here is either absent or occurs AFTER, not
|
|
91
|
+
* before, a tracked keyword — see this lane's evidence file for the audit).
|
|
92
|
+
*/
|
|
93
|
+
const NEGATION_CUE_PATTERN = /\b(?:don't|do\s+not|doesn't|does\s+not|didn't|did\s+not|won't|will\s+not|shouldn't|should\s+not|wouldn't|would\s+not|can't|cannot|isn't|is\s+not|aren't|are\s+not|never|avoid|skip|without|instead\s+of|rather\s+than|no\s+need\s+(?:to|for)|no\s+longer\s+need(?:\s+(?:to|for))?)\b/i;
|
|
94
|
+
const DIRECT_NOT_OBJECT_CUE_PATTERN_V4 = /\b(?:not|no)\s+(?:a\s+|an\s+|the\s+)?$/i;
|
|
95
|
+
const DOUBLE_NEGATION_RESCUE_PATTERN_V4 = /\b(?:don't|do\s+not|doesn't|does\s+not|didn't|did\s+not|won't|will\s+not|shouldn't|should\s+not|wouldn't|would\s+not|can't|cannot)\s+(?:skip|avoid)\s+(?:the\s+|a\s+|an\s+)?$/i;
|
|
96
|
+
const DELIBERATIVE_QUESTION_RESCUE_PATTERN_V4 = /\b(?:shouldn't|should\s+not|wouldn't|would\s+not|can't|cannot)\s+(?:we|i|you)\s*$/i;
|
|
97
|
+
const POSTPOSITIONED_NEGATION_CUE_PATTERN_V4 = /^\s*(?:-\s*free\b|(?:is|are|was|were)?\s*(?:not\s+(?:needed|required|desired)|unnecessary|not\s+necessary)|(?:[은는이가을를도만]\s*)?(?:하지\s*(?:마(?:라|세요|십시오)?|말(?:고|아|라)?|않(?:아|는|고|을|게)?)|말고|금지))/i;
|
|
98
|
+
/** A closed set of clause-boundary characters; a negation cue never reaches across one of these into a prior clause. */
|
|
99
|
+
const CLAUSE_BOUNDARY_CHARS = [".", "!", "?", ";", ","];
|
|
100
|
+
function hasCodeFence(text) {
|
|
101
|
+
return text.includes("```");
|
|
102
|
+
}
|
|
103
|
+
/** Explicit hunk headers or `diff --git` count alone; bare +/- only count together. */
|
|
104
|
+
function hasDiffMarkers(text) {
|
|
105
|
+
if (/^@@[^\n]*@@/m.test(text) || /^diff --git /m.test(text))
|
|
106
|
+
return true;
|
|
107
|
+
return /^\+(?!\+)/m.test(text) && /^-(?!-)/m.test(text);
|
|
108
|
+
}
|
|
109
|
+
/** First line (after stripping one polite prefix), capped at 180 chars. */
|
|
110
|
+
function firstClause(prompt) {
|
|
111
|
+
const firstLine = prompt.replace(/^(?:please|pls|can you|could you|would you|help me|i need you to)\s+/i, "").split("\n")[0] ?? "";
|
|
112
|
+
return firstLine.slice(0, 180);
|
|
113
|
+
}
|
|
114
|
+
// --- Leading-clause intent tests. All are `^`-anchored against the LEADING
|
|
115
|
+
// clause text only, so a negation cue at the start ("don't refactor...") can
|
|
116
|
+
// never match one of these: none of the alternations include "don't"/"never"/
|
|
117
|
+
// etc, so they are negation-immune by construction.
|
|
118
|
+
function hasLeadingReviewIntent(text) {
|
|
119
|
+
return /^(?:review|audit(?!\s+log\b)|critique|inspect|assess|approve|double-?check|lgtm)\b/i.test(text);
|
|
120
|
+
}
|
|
121
|
+
function hasLeadingPlanIntent(text) {
|
|
122
|
+
return /^(?:plan|design|architect|decompose)\b|^(?:write|create)\s+(?:a\s+)?(?:technical\s+)?(?:spec|roadmap|strategy|plan)\b/i.test(text);
|
|
123
|
+
}
|
|
124
|
+
function hasLeadingRefactorIntent(text) {
|
|
125
|
+
return /^(?:refactor(?:ing|ed)?|extract|rename|deduplicate|consolidate|modularize|reorganize|simplify|restructure|untangle)\b|^clean\s+up\b|^split\s+(?:the\s+)?module\b|^move\s+logic\b/i.test(text);
|
|
126
|
+
}
|
|
127
|
+
function hasLeadingDebugAction(text) {
|
|
128
|
+
return /^(?:debug|investigate\s+why|reproduce|trace\b)|^fix\s+this\s+(?:traceback|panic|error)\b/i.test(text);
|
|
129
|
+
}
|
|
130
|
+
function hasLeadingCodeGenIntent(text) {
|
|
131
|
+
return /^(?:implement|write|create|build|generate|scaffold|prototype|add)\b/i.test(text);
|
|
132
|
+
}
|
|
133
|
+
function hasLeadingSimpleEditIntent(text) {
|
|
134
|
+
return /^(?:correct|update|swap|remove|adjust|fix|add|change|trim|reword|tweak|bump)\b/i.test(text);
|
|
135
|
+
}
|
|
136
|
+
const NO_MATCH_RESULT_V4 = { matched: false, suppressed: false };
|
|
137
|
+
function matchOperationalRunbookV4(prompt, windowChars) {
|
|
138
|
+
let distinctSignals = 0;
|
|
139
|
+
let criticalSignals = 0;
|
|
140
|
+
let sawSuppressed = false;
|
|
141
|
+
for (const { pattern, critical } of OPERATIONAL_RUNBOOK_SIGNAL_PATTERNS) {
|
|
142
|
+
const result = matchUnnegated(prompt, pattern, windowChars);
|
|
143
|
+
if (result.matched) {
|
|
144
|
+
distinctSignals += 1;
|
|
145
|
+
if (critical)
|
|
146
|
+
criticalSignals += 1;
|
|
147
|
+
}
|
|
148
|
+
if (result.suppressed)
|
|
149
|
+
sawSuppressed = true;
|
|
150
|
+
}
|
|
151
|
+
const explicitRelease = matchUnnegated(prompt, EXPLICIT_RELEASE_RUNBOOK_PATTERN, windowChars).matched;
|
|
152
|
+
const matched = explicitRelease || distinctSignals >= 3 || (criticalSignals >= 2 && distinctSignals >= 2);
|
|
153
|
+
return { matched, suppressed: !matched && sawSuppressed };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Negation-aware whole-prompt pattern scan. Finds every occurrence of `pattern`
|
|
157
|
+
* in `prompt`; for each, looks back up to
|
|
158
|
+
* `windowChars` characters (never crossing a `.,;!?` boundary into a prior
|
|
159
|
+
* clause) for a `NEGATION_CUE_PATTERN` hit, and looks forward inside the same
|
|
160
|
+
* bounded window for Hangul-only post-positioned negation cues such as
|
|
161
|
+
* "하지 말고". `matched` is true iff at least one occurrence survives
|
|
162
|
+
* un-negated. Deterministic, single pass per pattern, no shared regex state (a
|
|
163
|
+
* fresh global-flag RegExp is constructed per call).
|
|
164
|
+
*/
|
|
165
|
+
function hasPrePositionedNegationCueV4(scoped) {
|
|
166
|
+
if (!NEGATION_CUE_PATTERN.test(scoped) && !DIRECT_NOT_OBJECT_CUE_PATTERN_V4.test(scoped))
|
|
167
|
+
return false;
|
|
168
|
+
return !DOUBLE_NEGATION_RESCUE_PATTERN_V4.test(scoped) && !DELIBERATIVE_QUESTION_RESCUE_PATTERN_V4.test(scoped);
|
|
169
|
+
}
|
|
170
|
+
function hasPostPositionedNegationCueV4(prompt, matchEnd, windowChars) {
|
|
171
|
+
const scoped = prompt.slice(matchEnd, Math.min(prompt.length, matchEnd + windowChars));
|
|
172
|
+
return POSTPOSITIONED_NEGATION_CUE_PATTERN_V4.test(scoped);
|
|
173
|
+
}
|
|
174
|
+
function matchUnnegated(prompt, pattern, windowChars) {
|
|
175
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
|
|
176
|
+
const globalPattern = new RegExp(pattern.source, flags);
|
|
177
|
+
let sawMatch = false;
|
|
178
|
+
let sawUnnegated = false;
|
|
179
|
+
let match = globalPattern.exec(prompt);
|
|
180
|
+
while (match !== null) {
|
|
181
|
+
sawMatch = true;
|
|
182
|
+
const matchStart = match.index;
|
|
183
|
+
const windowStart = Math.max(0, matchStart - windowChars);
|
|
184
|
+
const windowText = prompt.slice(windowStart, matchStart);
|
|
185
|
+
let boundary = -1;
|
|
186
|
+
for (const boundaryChar of CLAUSE_BOUNDARY_CHARS) {
|
|
187
|
+
const idx = windowText.lastIndexOf(boundaryChar);
|
|
188
|
+
if (idx > boundary)
|
|
189
|
+
boundary = idx;
|
|
190
|
+
}
|
|
191
|
+
const scoped = boundary >= 0 ? windowText.slice(boundary + 1) : windowText;
|
|
192
|
+
const preNegated = hasPrePositionedNegationCueV4(scoped);
|
|
193
|
+
const postNegated = !preNegated && hasPostPositionedNegationCueV4(prompt, matchStart + match[0].length, windowChars);
|
|
194
|
+
if (!preNegated && !postNegated)
|
|
195
|
+
sawUnnegated = true;
|
|
196
|
+
if (match[0].length === 0)
|
|
197
|
+
globalPattern.lastIndex += 1;
|
|
198
|
+
match = globalPattern.exec(prompt);
|
|
199
|
+
}
|
|
200
|
+
return { matched: sawUnnegated, suppressed: sawMatch && !sawUnnegated };
|
|
201
|
+
}
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// Bounded compound-intent detection
|
|
204
|
+
// ============================================================================
|
|
205
|
+
/**
|
|
206
|
+
* Splits off a bounded second clause after a short-range conjunction
|
|
207
|
+
* ("then"/"and then"/"and also"/";"), only when the split point is within the
|
|
208
|
+
* first `COMPOUND_SPLIT_MAX_INDEX_V4` characters (short direct commands, not
|
|
209
|
+
* long prose briefs — verified this never fires inside the GOLD_SET's
|
|
210
|
+
* >=2400-char plan entries). Returns null when no qualifying split exists.
|
|
211
|
+
*/
|
|
212
|
+
function splitCompoundClauseV4(prompt) {
|
|
213
|
+
const match = COMPOUND_SPLIT_PATTERN.exec(prompt);
|
|
214
|
+
if (match === null || match.index > COMPOUND_SPLIT_MAX_INDEX_V4)
|
|
215
|
+
return null;
|
|
216
|
+
const second = prompt.slice(match.index + match[0].length).trim();
|
|
217
|
+
if (second.length < COMPOUND_SECOND_CLAUSE_MIN_CHARS_V4)
|
|
218
|
+
return null;
|
|
219
|
+
return second;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Leading-intent test for a compound prompt's SECOND clause. Reuses the same
|
|
223
|
+
* `^`-anchored leading-intent tests as the primary clause (so it is equally
|
|
224
|
+
* negation-immune), without the primary clause's diagnosticEvidence/localEdit
|
|
225
|
+
* gating (a short second-clause fragment carries no such context of its own).
|
|
226
|
+
*/
|
|
227
|
+
function secondClauseLeadingIntentV4(clause) {
|
|
228
|
+
const bounded = clause.slice(0, COMPOUND_SECOND_CLAUSE_SCAN_CHARS_V4);
|
|
229
|
+
return leadingIntentFromLeadingClauseV4(bounded);
|
|
230
|
+
}
|
|
231
|
+
function leadingIntentFromLeadingClauseV4(leading) {
|
|
232
|
+
if (hasLeadingDebugAction(leading))
|
|
233
|
+
return "debug";
|
|
234
|
+
if (hasLeadingReviewIntent(leading))
|
|
235
|
+
return "review";
|
|
236
|
+
if (hasLeadingPlanIntent(leading))
|
|
237
|
+
return "plan";
|
|
238
|
+
if (hasLeadingRefactorIntent(leading))
|
|
239
|
+
return "refactor";
|
|
240
|
+
if (hasLeadingSimpleEditIntent(leading))
|
|
241
|
+
return "simple-edit";
|
|
242
|
+
if (hasLeadingCodeGenIntent(leading))
|
|
243
|
+
return "code-gen";
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
function leadingIntentIsPostNegatedV4(leading, windowChars) {
|
|
247
|
+
const leadingIntentMatch = /^(?:debug|investigate\s+why|reproduce|trace\b|fix\s+this\s+(?:traceback|panic|error)|review|audit(?!\s+log\b)|critique|inspect|assess|approve|double-?check|lgtm|plan|design|architect|decompose|write\s+(?:a\s+)?(?:technical\s+)?(?:spec|roadmap|strategy|plan)|create\s+(?:a\s+)?(?:technical\s+)?(?:spec|roadmap|strategy|plan)|refactor(?:ing|ed)?|extract|rename|deduplicate|consolidate|modularize|reorganize|simplify|restructure|untangle|clean\s+up|split\s+(?:the\s+)?module|move\s+logic|correct|update|swap|remove|adjust|fix|add|change|trim|reword|tweak|bump|implement|build|generate|scaffold|prototype)\b/i.exec(leading);
|
|
248
|
+
return (leadingIntentMatch !== null && hasPostPositionedNegationCueV4(leading, leadingIntentMatch[0].length, windowChars));
|
|
249
|
+
}
|
|
250
|
+
function classifyShortKoreanZeroScoreTaskV4(prompt, windowChars) {
|
|
251
|
+
if (matchUnnegated(prompt, KO_SHORT_DEBUG_SIGNAL_PATTERN_V4, windowChars).matched)
|
|
252
|
+
return "debug";
|
|
253
|
+
if (matchUnnegated(prompt, KO_SHORT_SIMPLE_EDIT_SIGNAL_PATTERN_V4, windowChars).matched)
|
|
254
|
+
return "simple-edit";
|
|
255
|
+
if (matchUnnegated(prompt, KO_SHORT_REVIEW_SIGNAL_PATTERN_V4, windowChars).matched)
|
|
256
|
+
return "review";
|
|
257
|
+
if (matchUnnegated(prompt, KO_SHORT_PLAN_SIGNAL_PATTERN_V4, windowChars).matched)
|
|
258
|
+
return "plan";
|
|
259
|
+
if (matchUnnegated(prompt, KO_SHORT_REFACTOR_SIGNAL_PATTERN_V4, windowChars).matched)
|
|
260
|
+
return "refactor";
|
|
261
|
+
if (matchUnnegated(prompt, KO_SHORT_CODE_GEN_SIGNAL_PATTERN_V4, windowChars).matched)
|
|
262
|
+
return "code-gen";
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
/** Decision order for the leading intent classifier. */
|
|
266
|
+
function leadingIntentV4(input) {
|
|
267
|
+
if (input.diagnosticEvidence &&
|
|
268
|
+
(hasLeadingDebugAction(input.firstClause) || hasLeadingSimpleEditIntent(input.firstClause)))
|
|
269
|
+
return "debug";
|
|
270
|
+
if (hasLeadingReviewIntent(input.firstClause))
|
|
271
|
+
return "review";
|
|
272
|
+
if (hasLeadingPlanIntent(input.firstClause))
|
|
273
|
+
return "plan";
|
|
274
|
+
if (hasLeadingRefactorIntent(input.firstClause))
|
|
275
|
+
return "refactor";
|
|
276
|
+
if (input.localEdit && hasLeadingSimpleEditIntent(input.firstClause))
|
|
277
|
+
return "simple-edit";
|
|
278
|
+
if (!input.localEdit && hasLeadingCodeGenIntent(input.firstClause))
|
|
279
|
+
return "code-gen";
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
/** Diagnostic-evidence decision structure, parameterized over precomputed negation-aware matches. */
|
|
283
|
+
function hasDiagnosticEvidenceV4(leading, hardDiagnosticMatched, bugObjectMatched, genericDiagnosticMatched, nonDiagnosticContext) {
|
|
284
|
+
if (hasLeadingDebugAction(leading) || hardDiagnosticMatched)
|
|
285
|
+
return true;
|
|
286
|
+
const hasContextualDiagnostic = bugObjectMatched || (genericDiagnosticMatched && !nonDiagnosticContext);
|
|
287
|
+
if (!hasContextualDiagnostic)
|
|
288
|
+
return false;
|
|
289
|
+
return !hasLeadingReviewIntent(leading) && !hasLeadingPlanIntent(leading) && !hasLeadingRefactorIntent(leading);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Extracts every v4 feature from one prompt, negation-gating every
|
|
293
|
+
* whole-prompt scan exactly once and recording a `negation:<channel>` id in
|
|
294
|
+
* `suppressed` whenever a match existed but every occurrence was negated.
|
|
295
|
+
*/
|
|
296
|
+
function extractFeaturesV4(prompt, weights, suppressed) {
|
|
297
|
+
const leading = firstClause(prompt);
|
|
298
|
+
const window = weights.negationWindowChars;
|
|
299
|
+
const implementationObjectResult = matchUnnegated(prompt, IMPLEMENTATION_OBJECT_PATTERN, window);
|
|
300
|
+
if (implementationObjectResult.suppressed)
|
|
301
|
+
suppressed.push("negation:implementation-object");
|
|
302
|
+
const implementationObject = implementationObjectResult.matched;
|
|
303
|
+
const lowRiskEditAction = LOW_RISK_EDIT_ACTION_PATTERN.test(leading);
|
|
304
|
+
const localEditObjectResult = matchUnnegated(prompt, LOCAL_EDIT_OBJECT_PATTERN, window);
|
|
305
|
+
if (localEditObjectResult.suppressed)
|
|
306
|
+
suppressed.push("negation:local-edit-object");
|
|
307
|
+
const localEditActionResult = matchUnnegated(prompt, LOCAL_EDIT_ACTION_OBJECT_PATTERN, window);
|
|
308
|
+
if (localEditActionResult.suppressed)
|
|
309
|
+
suppressed.push("negation:local-edit-action-object");
|
|
310
|
+
const localEdit = !implementationObject && (lowRiskEditAction || localEditObjectResult.matched || localEditActionResult.matched);
|
|
311
|
+
const hardDiagnosticResult = matchUnnegated(prompt, HARD_DIAGNOSTIC_PATTERN, window);
|
|
312
|
+
if (hardDiagnosticResult.suppressed)
|
|
313
|
+
suppressed.push("negation:hard-diagnostic");
|
|
314
|
+
const bugObjectResult = matchUnnegated(prompt, BUG_OBJECT_PATTERN, window);
|
|
315
|
+
if (bugObjectResult.suppressed)
|
|
316
|
+
suppressed.push("negation:bug-object");
|
|
317
|
+
const genericDiagnosticResult = matchUnnegated(prompt, GENERIC_DIAGNOSTIC_PATTERN, window);
|
|
318
|
+
const nonDiagnosticContext = NON_DIAGNOSTIC_DEBUG_CONTEXT_PATTERN.test(prompt);
|
|
319
|
+
if (genericDiagnosticResult.suppressed && !nonDiagnosticContext)
|
|
320
|
+
suppressed.push("negation:generic-diagnostic");
|
|
321
|
+
const diagnosticEvidence = hasDiagnosticEvidenceV4(leading, hardDiagnosticResult.matched, bugObjectResult.matched, genericDiagnosticResult.matched, nonDiagnosticContext);
|
|
322
|
+
const reviewScopeResult = matchUnnegated(prompt, REVIEW_SCOPE_PATTERN, window);
|
|
323
|
+
if (reviewScopeResult.suppressed)
|
|
324
|
+
suppressed.push("negation:review-scope");
|
|
325
|
+
const reviewScope = hasLeadingReviewIntent(leading) && reviewScopeResult.matched;
|
|
326
|
+
const planBriefResult = matchUnnegated(prompt, PLAN_BRIEF_PATTERN, window);
|
|
327
|
+
if (planBriefResult.suppressed)
|
|
328
|
+
suppressed.push("negation:plan-brief");
|
|
329
|
+
const planBrief = hasLeadingPlanIntent(leading) && (planBriefResult.matched || prompt.length >= LONG_BRIEF_MIN_CHARS_V4);
|
|
330
|
+
const operationalRunbookResult = matchOperationalRunbookV4(prompt, window);
|
|
331
|
+
if (operationalRunbookResult.suppressed)
|
|
332
|
+
suppressed.push("negation:operational-runbook");
|
|
333
|
+
const operationalRunbook = operationalRunbookResult.matched;
|
|
334
|
+
const refactorCueResult = matchUnnegated(prompt, REFACTOR_CUE_PATTERN, window);
|
|
335
|
+
if (refactorCueResult.suppressed)
|
|
336
|
+
suppressed.push("negation:refactor-cue");
|
|
337
|
+
const refactorCue = refactorCueResult.matched;
|
|
338
|
+
const rawPrimaryIntent = leadingIntentV4({ firstClause: leading, localEdit, diagnosticEvidence });
|
|
339
|
+
const primaryIntent = rawPrimaryIntent !== null && leadingIntentIsPostNegatedV4(leading, window) ? null : rawPrimaryIntent;
|
|
340
|
+
const secondClauseText = splitCompoundClauseV4(prompt);
|
|
341
|
+
const secondClauseIntent = secondClauseText === null ? null : secondClauseLeadingIntentV4(secondClauseText);
|
|
342
|
+
const compoundIntent = secondClauseIntent !== null && secondClauseIntent !== primaryIntent;
|
|
343
|
+
const debugKeywordResult = diagnosticEvidence
|
|
344
|
+
? matchUnnegated(prompt, DEBUG_KEYWORD_FAMILY_PATTERN, window)
|
|
345
|
+
: NO_MATCH_RESULT_V4;
|
|
346
|
+
if (debugKeywordResult.suppressed)
|
|
347
|
+
suppressed.push("negation:keyword-debug");
|
|
348
|
+
const reviewKeywordResult = matchUnnegated(prompt, REVIEW_KEYWORD_FAMILY_PATTERN, window);
|
|
349
|
+
if (reviewKeywordResult.suppressed)
|
|
350
|
+
suppressed.push("negation:keyword-review");
|
|
351
|
+
const planKeywordResult = matchUnnegated(prompt, PLAN_KEYWORD_FAMILY_PATTERN, window);
|
|
352
|
+
if (planKeywordResult.suppressed)
|
|
353
|
+
suppressed.push("negation:keyword-plan");
|
|
354
|
+
const codeGenKeywordResult = matchUnnegated(prompt, CODE_GEN_KEYWORD_FAMILY_PATTERN, window);
|
|
355
|
+
if (codeGenKeywordResult.suppressed)
|
|
356
|
+
suppressed.push("negation:keyword-code-gen");
|
|
357
|
+
const addKeywordResult = localEdit ? NO_MATCH_RESULT_V4 : matchUnnegated(prompt, ADD_KEYWORD_PATTERN, window);
|
|
358
|
+
if (addKeywordResult.suppressed)
|
|
359
|
+
suppressed.push("negation:add-keyword");
|
|
360
|
+
return {
|
|
361
|
+
firstClause: leading,
|
|
362
|
+
codeFence: hasCodeFence(prompt),
|
|
363
|
+
diffHunk: hasDiffMarkers(prompt),
|
|
364
|
+
localEdit,
|
|
365
|
+
diagnosticEvidence,
|
|
366
|
+
reviewScope,
|
|
367
|
+
planBrief,
|
|
368
|
+
operationalRunbook,
|
|
369
|
+
refactorCue,
|
|
370
|
+
implementationObject,
|
|
371
|
+
leadingIntent: primaryIntent,
|
|
372
|
+
secondClauseIntent,
|
|
373
|
+
compoundIntent,
|
|
374
|
+
keywordFamilyMatch: {
|
|
375
|
+
trivial: false,
|
|
376
|
+
debug: debugKeywordResult.matched,
|
|
377
|
+
refactor: refactorCue,
|
|
378
|
+
review: reviewKeywordResult.matched,
|
|
379
|
+
plan: planKeywordResult.matched,
|
|
380
|
+
"simple-edit": localEditObjectResult.matched,
|
|
381
|
+
"code-gen": codeGenKeywordResult.matched,
|
|
382
|
+
},
|
|
383
|
+
addKeywordMatch: addKeywordResult.matched,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
// ============================================================================
|
|
387
|
+
// Scoring
|
|
388
|
+
// ============================================================================
|
|
389
|
+
function emptyScoresV4() {
|
|
390
|
+
return {
|
|
391
|
+
trivial: 0,
|
|
392
|
+
"simple-edit": 0,
|
|
393
|
+
"code-gen": 0,
|
|
394
|
+
debug: 0,
|
|
395
|
+
refactor: 0,
|
|
396
|
+
review: 0,
|
|
397
|
+
plan: 0,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function computeScoresV4(features, weights) {
|
|
401
|
+
const scores = emptyScoresV4();
|
|
402
|
+
if (features.codeFence || features.diffHunk)
|
|
403
|
+
scores["code-gen"] += weights.codeFenceOrDiff;
|
|
404
|
+
if (features.leadingIntent !== null)
|
|
405
|
+
scores[features.leadingIntent] += weights.leadingIntent;
|
|
406
|
+
if (features.secondClauseIntent !== null)
|
|
407
|
+
scores[features.secondClauseIntent] += weights.secondClauseIntent;
|
|
408
|
+
if (features.localEdit)
|
|
409
|
+
scores["simple-edit"] += weights.localEdit;
|
|
410
|
+
if (features.diagnosticEvidence)
|
|
411
|
+
scores.debug += weights.diagnosticEvidence;
|
|
412
|
+
if (features.reviewScope)
|
|
413
|
+
scores.review += weights.reviewScope;
|
|
414
|
+
if (features.planBrief)
|
|
415
|
+
scores.plan += weights.planBrief;
|
|
416
|
+
if (features.operationalRunbook)
|
|
417
|
+
scores.plan += weights.operationalRunbook;
|
|
418
|
+
if (features.refactorCue)
|
|
419
|
+
scores.refactor += weights.refactorCue;
|
|
420
|
+
if (features.implementationObject)
|
|
421
|
+
scores["code-gen"] += weights.implementationObject;
|
|
422
|
+
for (const taskClass of TASK_CLASSES_V4) {
|
|
423
|
+
if (features.keywordFamilyMatch[taskClass])
|
|
424
|
+
scores[taskClass] += weights.keywordFamily[taskClass];
|
|
425
|
+
}
|
|
426
|
+
if (features.addKeywordMatch)
|
|
427
|
+
scores["code-gen"] += weights.addKeyword;
|
|
428
|
+
return scores;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Extension signals (history / context-pressure / judge vote). Every
|
|
432
|
+
* coefficient is 0 under DEFAULT_WEIGHTS_V4 (inert until calibrated by a
|
|
433
|
+
* future governance-backed lane); the mechanism exists so
|
|
434
|
+
* `TaskClassifierInputV4`'s optional fields are meaningfully wired rather than
|
|
435
|
+
* silently accepted-and-ignored.
|
|
436
|
+
*/
|
|
437
|
+
function applyExtensionSignalsV4(scores, input, weights) {
|
|
438
|
+
const priorClass = input.history !== undefined && input.history.length > 0 ? input.history[0] : null;
|
|
439
|
+
if (priorClass !== null && weights.multiTurnPrior !== 0)
|
|
440
|
+
scores[priorClass] += weights.multiTurnPrior;
|
|
441
|
+
const judgeVote = input.judgeVote ?? null;
|
|
442
|
+
if (judgeVote !== null && weights.judgeVote !== 0)
|
|
443
|
+
scores[judgeVote] += weights.judgeVote;
|
|
444
|
+
const pressureBucket = input.pressureBucket ?? 0;
|
|
445
|
+
if (pressureBucket > 0 && weights.pressureBucket !== 0) {
|
|
446
|
+
const bump = pressureBucket * weights.pressureBucket;
|
|
447
|
+
scores.debug += bump;
|
|
448
|
+
scores.review += bump;
|
|
449
|
+
scores.plan += bump;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// ============================================================================
|
|
453
|
+
// Classifier
|
|
454
|
+
// ============================================================================
|
|
455
|
+
/**
|
|
456
|
+
* Deterministic confidence-bearing classifier (spec 008 Req 2).
|
|
457
|
+
*
|
|
458
|
+
* Pipeline: extract negation/compound-aware contextual features -> score every
|
|
459
|
+
* class from `weights` -> argmax with `TASK_CLASSES_V4` precedence tie-break,
|
|
460
|
+
* tracking the runner-up -> compute margin/confidence/confidenceBand/tieBreak
|
|
461
|
+
* from the RAW scores -> if `topScore <= 0`, replace `taskClass` (and record
|
|
462
|
+
* `fallbackReason`) via the zero-score cascade (fence/diff -> code-gen;
|
|
463
|
+
* length < 40 -> trivial; length >= 2400 -> plan; lane fallback; default
|
|
464
|
+
* code-gen) — the raw scores/margin/runnerUp/tieBreak fields still reflect the
|
|
465
|
+
* pre-cascade computation, for audit purposes.
|
|
466
|
+
*
|
|
467
|
+
* Pure: same (input, weights) -> same ClassifierVerdictV4.
|
|
468
|
+
*/
|
|
469
|
+
export function classifyTaskV4(input, weights = DEFAULT_WEIGHTS_V4) {
|
|
470
|
+
const prompt = input.prompt.trim();
|
|
471
|
+
const suppressed = [];
|
|
472
|
+
const features = extractFeaturesV4(prompt, weights, suppressed);
|
|
473
|
+
const scores = computeScoresV4(features, weights);
|
|
474
|
+
applyExtensionSignalsV4(scores, input, weights);
|
|
475
|
+
let top = TASK_CLASSES_V4[0];
|
|
476
|
+
for (const c of TASK_CLASSES_V4) {
|
|
477
|
+
if (scores[c] > scores[top])
|
|
478
|
+
top = c;
|
|
479
|
+
}
|
|
480
|
+
let runnerUp = null;
|
|
481
|
+
for (const c of TASK_CLASSES_V4) {
|
|
482
|
+
if (c === top)
|
|
483
|
+
continue;
|
|
484
|
+
if (runnerUp === null || scores[c] > scores[runnerUp])
|
|
485
|
+
runnerUp = c;
|
|
486
|
+
}
|
|
487
|
+
const topScore = scores[top];
|
|
488
|
+
const runnerUpScore = runnerUp !== null ? scores[runnerUp] : 0;
|
|
489
|
+
const margin = topScore - runnerUpScore;
|
|
490
|
+
const tieBreak = runnerUp !== null && topScore === runnerUpScore;
|
|
491
|
+
const confidence = topScore <= 0 ? 0 : Math.max(0, Math.min(1, margin / topScore));
|
|
492
|
+
const confidenceBand = confidence <= weights.lowConfidenceThreshold
|
|
493
|
+
? "low"
|
|
494
|
+
: confidence >= weights.highConfidenceThreshold
|
|
495
|
+
? "high"
|
|
496
|
+
: "medium";
|
|
497
|
+
let taskClass = top;
|
|
498
|
+
let fallbackReason = null;
|
|
499
|
+
if (topScore <= 0) {
|
|
500
|
+
if (features.codeFence || features.diffHunk) {
|
|
501
|
+
taskClass = "code-gen";
|
|
502
|
+
fallbackReason = "code-fence-or-diff";
|
|
503
|
+
}
|
|
504
|
+
else if (prompt.length < TRIVIAL_MAX_CHARS_V4) {
|
|
505
|
+
const shortKoreanTaskClass = classifyShortKoreanZeroScoreTaskV4(prompt, weights.negationWindowChars);
|
|
506
|
+
if (shortKoreanTaskClass !== null) {
|
|
507
|
+
taskClass = shortKoreanTaskClass;
|
|
508
|
+
fallbackReason = "ko-short-task-signal";
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
taskClass = "trivial";
|
|
512
|
+
fallbackReason = "trivial-length";
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
else if (prompt.length >= COMPLEX_PROSE_MIN_CHARS_V4) {
|
|
516
|
+
taskClass = "plan";
|
|
517
|
+
fallbackReason = "long-prose";
|
|
518
|
+
}
|
|
519
|
+
else if (input.laneType !== undefined) {
|
|
520
|
+
taskClass = LANE_FALLBACK_CLASS_V4[input.laneType];
|
|
521
|
+
fallbackReason = "lane-fallback";
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
taskClass = "code-gen";
|
|
525
|
+
fallbackReason = "default";
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
taskClass,
|
|
530
|
+
scores,
|
|
531
|
+
runnerUp,
|
|
532
|
+
margin,
|
|
533
|
+
confidence,
|
|
534
|
+
confidenceBand,
|
|
535
|
+
tieBreak,
|
|
536
|
+
fallbackReason,
|
|
537
|
+
suppressedFeatureIds: suppressed,
|
|
538
|
+
compoundIntent: features.compoundIntent,
|
|
539
|
+
secondClauseIntent: features.secondClauseIntent,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
// ============================================================================
|
|
543
|
+
// Resolver
|
|
544
|
+
// ============================================================================
|
|
545
|
+
/**
|
|
546
|
+
* Static rule table: task class -> recommended ThinkingLevel. Re-exported
|
|
547
|
+
* from the shared resolver core (byte-identical values; previously a
|
|
548
|
+
* duplicated literal here — see reasoning-router-resolver.ts).
|
|
549
|
+
*/
|
|
550
|
+
export const TASK_CLASS_THINKING_LEVELS_V4 = TASK_CLASS_THINKING_LEVELS;
|
|
551
|
+
/**
|
|
552
|
+
* Bounded, strictly non-negative escalation applied when the verdict's own
|
|
553
|
+
* confidence is low or a fallback (not a real signal) decided the class. This
|
|
554
|
+
* is the ONLY place confidence touches the target index, and it can only add:
|
|
555
|
+
* confidence can never lower effort (spec 008 Req 2 acceptance criterion).
|
|
556
|
+
*/
|
|
557
|
+
const LOW_CONFIDENCE_ESCALATION_STEPS_V4 = 1;
|
|
558
|
+
/**
|
|
559
|
+
* Thin auto-mode wrapper with no uncertainty adjustment: base rule table ->
|
|
560
|
+
* lane step -> clamp to availableLevels. This is v4's "confident" path and
|
|
561
|
+
* delegates to the shared resolver core with bias=0, hint=null, escalationSteps=0.
|
|
562
|
+
*/
|
|
563
|
+
export function resolveThinkingLevelV4ForAuto(taskClass, availableLevels, laneType) {
|
|
564
|
+
return resolveThinkingLevelCore(taskClass, availableLevels, laneType, 0, null, 0);
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Uncertainty-aware resolver (spec 008 Req 2 / plan.md step 5).
|
|
568
|
+
*
|
|
569
|
+
* Pipeline: base rule table for `verdict.taskClass` -> lane step -> bounded
|
|
570
|
+
* bias [-2,+2] -> optional hint fusion (±2 bounded) -> bounded
|
|
571
|
+
* non-negative confidence escalation (+1 ladder step exactly when
|
|
572
|
+
* `verdict.confidenceBand === "low"` or `verdict.fallbackReason !== null`) ->
|
|
573
|
+
* clamp to `availableLevels`.
|
|
574
|
+
*
|
|
575
|
+
* The confidence-escalation term is strictly `>= 0`: low confidence can only
|
|
576
|
+
* hold the base+lane+bias+hint target or push it one step higher, never lower
|
|
577
|
+
* it. This guarantees a low-confidence verdict never resolves BELOW what
|
|
578
|
+
* `resolveThinkingLevelV4ForAuto` would give the same class (with bias=0,
|
|
579
|
+
* hint=null) — text alone cannot talk the resolver down.
|
|
580
|
+
*/
|
|
581
|
+
export function resolveThinkingLevelV4WithUncertainty(verdict, availableLevels, laneType, bias = 0, hint = null) {
|
|
582
|
+
const escalationSteps = verdict.confidenceBand === "low" || verdict.fallbackReason !== null ? LOW_CONFIDENCE_ESCALATION_STEPS_V4 : 0;
|
|
583
|
+
return resolveThinkingLevelCore(verdict.taskClass, availableLevels, laneType, bias, hint, escalationSteps);
|
|
584
|
+
}
|
|
585
|
+
//# sourceMappingURL=reasoning-router-v4.js.map
|