@@ -1,6 +1,7 @@
( function initializeUnifiedAssistant ( ) {
const STORAGE _KEY = "cognitive-agent.assistant-messages" ;
const CONVERSATION _STORAGE _KEY = "cognitive-agent.conversation-id" ;
const ACTIVE _MODE _DRAFT _STORAGE _KEY = "cognitive-agent.active-mode-draft" ;
const MAX _STORED _MESSAGES = 24 ;
const COMPOSER _MIN _HEIGHT = 64 ;
const COMPOSER _MAX _HEIGHT = 116 ;
@@ -8,6 +9,7 @@
let pendingController = null ;
let pendingMessage = null ;
let latestControlResult = null ;
let activeModeDraftContext = null ;
let initialized = false ;
const $ = ( selector , root = document ) => root . querySelector ( selector ) ;
@@ -106,6 +108,108 @@
return created ;
}
function normalizeModeName ( value ) {
return String ( value || "" ) . trim ( ) . replace ( /\s+/g , "" ) . toLowerCase ( ) ;
}
function sanitizeModeDraftContext ( value ) {
if ( ! value || typeof value !== "object" ) return null ;
const name = String ( value . name || "" ) . trim ( ) ;
const sourceRequestId = String ( value . source _request _id || "" ) . trim ( ) ;
const vehicleId = String ( value . vehicle _id || "" ) . trim ( ) ;
const draftConversationId = String ( value . conversation _id || "" ) . trim ( ) ;
if (
! name
|| name . length > 64
|| ! /^req-[a-z0-9-]{6,100}$/ . test ( sourceRequestId )
) return null ;
if ( ! vehicleId || vehicleId . length > 80 || draftConversationId !== conversationId ( ) ) return null ;
return {
name ,
source _request _id : sourceRequestId ,
vehicle _id : vehicleId ,
conversation _id : draftConversationId ,
} ;
}
function loadActiveModeDraftContext ( ) {
try {
return sanitizeModeDraftContext (
JSON . parse ( sessionStorage . getItem ( ACTIVE _MODE _DRAFT _STORAGE _KEY ) || "null" ) ,
) ;
} catch ( _error ) {
return null ;
}
}
function rememberModeDraftContext ( draft ) {
const candidate = sanitizeModeDraftContext ( {
name : draft ? . name ,
source _request _id : draft ? . source _request _id ,
vehicle _id : draft ? . vehicle _id || $ ( "#vehicleSelect" ) ? . value || null ,
conversation _id : conversationId ( ) ,
} ) ;
if ( ! candidate ) return ;
activeModeDraftContext = candidate ;
sessionStorage . setItem ( ACTIVE _MODE _DRAFT _STORAGE _KEY , JSON . stringify ( candidate ) ) ;
}
function clearActiveModeDraftContext ( sourceRequestId = null ) {
if (
sourceRequestId
&& activeModeDraftContext ? . source _request _id !== String ( sourceRequestId )
) return ;
activeModeDraftContext = null ;
sessionStorage . removeItem ( ACTIVE _MODE _DRAFT _STORAGE _KEY ) ;
}
function isModeDraftUpdateRequest ( message , context ) {
const compact = String ( message || "" ) . trim ( ) . replace ( /\s+/g , "" ) ;
if ( ! compact || ! context ? . name ) return false ;
const politePrefix = "(?:(?:请|麻烦)(?:帮我|帮忙|给我)?|帮我|帮忙|给我)?" ;
const prefixUpdateVerb = "(?:修改一下|修改|调整|更改|更新|改一下|变更)" ;
const changePredicate = "(?:修改|改成|改为|更改|更新|变更|变为|换成|调整|调节|调高|调低|调到|调成|调为|调至|设置|设为|设成|设到|设至|打开|开启|启动|关闭|关掉|关上|全关|停止|停用|切换|进入|退出|移动|前移|后退|前进|升高|降低|抬高|放低|降下|升起|调直|放倒|复位|半开|开到|关到|拉开|拉上|上锁|解锁)" ;
const modeName = "([^,。!?!?、;;: :]{1,64}?模式)" ;
const modePossessiveBoundary = "(?:中的|里的|内的|之中的|的|中|里|内)" ;
const modeMemberBoundary = ` (?: ${ modePossessiveBoundary } |[, ,:: ]) ` ;
const question = /为什么|什么|怎么|如何|介绍|解释|作用|区别|原理|哪些|多少|几(?:度|档|级|个|项)?|是否|能否|可不可以|合适吗|合理吗|会怎样|列出|告诉我|[??]|吗[。.!! ]?$/ ;
const candidates = [
{
pattern : new RegExp ( ` ^ ${ politePrefix } ${ prefixUpdateVerb } ${ modeName } ${ modeMemberBoundary } [, ,:: ]?(.*) $ ` ) ,
predicateRequired : false ,
} ,
{
pattern : new RegExp ( ` ^ ${ politePrefix } (?:把|将) ${ modeName } ${ modeMemberBoundary } [, ,:: ]?(.+) $ ` ) ,
predicateRequired : true ,
} ,
{
pattern : new RegExp ( ` ^ ${ politePrefix } ${ modeName } ${ modeMemberBoundary } [, ,:: ]?(.+) $ ` ) ,
predicateRequired : true ,
} ,
] ;
return candidates . some ( ( { pattern , predicateRequired } ) => {
const matched = compact . match ( pattern ) ;
const change = String ( matched ? . [ 2 ] || "" ) ;
return Boolean (
matched
&& normalizeModeName ( matched [ 1 ] ) === normalizeModeName ( context . name )
&& ! question . test ( change )
&& ( ! predicateRequired || new RegExp ( changePredicate ) . test ( change ) )
) ;
} ) ;
}
function requestModeDraftContext ( message ) {
const context = activeModeDraftContext || loadActiveModeDraftContext ( ) ;
if ( ! context || ! isModeDraftUpdateRequest ( message , context ) ) return null ;
activeModeDraftContext = context ;
return {
name : context . name ,
source _request _id : context . source _request _id ,
vehicle _id : context . vehicle _id ,
} ;
}
function loadStoredMessages ( ) {
try {
const stored = JSON . parse ( sessionStorage . getItem ( STORAGE _KEY ) || "[]" ) ;
@@ -475,6 +579,7 @@
status . className = "assistant-mode-draft-status success" ;
status . textContent = existingModeId ? "模式已更新" : "模式已保存" ;
card . querySelector ( ".assistant-mode-draft-actions" ) ? . setAttribute ( "hidden" , "" ) ;
clearActiveModeDraftContext ( draft . source _request _id ) ;
await window . CognitiveAgentConsole ? . refreshModeProfiles ? . ( payload . mode _profile ? . id ) ;
appendModeCreatedState ( message , payload . mode _profile , { updated : Boolean ( existingModeId ) } ) ;
persistMessages ( ) ;
@@ -506,13 +611,40 @@
}
}
function supersedeModeDraftCards ( draft ) {
const nextName = normalizeModeName ( draft ? . name ) ;
const nextRequestId = String ( draft ? . source _request _id || "" ) ;
if ( ! nextName || ! nextRequestId ) return ;
document . querySelectorAll ( "[data-assistant-mode-draft]" ) . forEach ( ( card ) => {
if (
card . dataset . assistantModeName !== nextName
|| card . dataset . assistantModeDraft === nextRequestId
) return ;
card . classList . remove ( "is-busy" ) ;
card . classList . add ( "is-superseded" ) ;
card . querySelectorAll ( "button" ) . forEach ( ( button ) => {
button . disabled = true ;
} ) ;
card . querySelector ( ".assistant-mode-draft-actions" ) ? . setAttribute ( "hidden" , "" ) ;
const priorStatus = card . querySelector ( ".assistant-mode-draft-status" ) ;
if ( priorStatus ) {
priorStatus . hidden = false ;
priorStatus . className = "assistant-mode-draft-status superseded" ;
priorStatus . textContent = "已由下方最新草稿替换" ;
}
} ) ;
}
function appendModeDraftState ( message , draft , actions ) {
if ( ! message ? . body || ! draft ? . name || ! draft ? . source _request _id ) return ;
rememberModeDraftContext ( draft ) ;
supersedeModeDraftCards ( draft ) ;
const selector = ` [data-assistant-mode-draft=" ${ CSS . escape ( String ( draft . source _request _id ) ) } "] ` ;
if ( $ ( selector , message . body ) ) return ;
const card = document . createElement ( "section" ) ;
card . className = "assistant-mode-draft" ;
card . dataset . assistantModeDraft = String ( draft . source _request _id ) ;
card . dataset . assistantModeName = normalizeModeName ( draft . name ) ;
message . article ? . classList . add ( "assistant-message-mode-draft" ) ;
const heading = document . createElement ( "header" ) ;
@@ -613,6 +745,7 @@
const controlTransport = localStorage . getItem ( "cognitive-agent.control-transport" ) || "wired_can" ;
const hardwareClientId = localStorage . getItem ( "cognitive-agent.hardware-client-id" ) ;
const vehicleId = $ ( "#vehicleSelect" ) ? . value || null ;
const modeProfileDraftContext = requestModeDraftContext ( message ) ;
return {
message ,
conversation _id : conversationId ( ) ,
@@ -620,6 +753,9 @@
execution _mode : executionMode ,
control _transport : controlTransport ,
... ( vehicleId ? { vehicle _id : vehicleId } : { } ) ,
... ( modeProfileDraftContext
? { mode _profile _draft _context : modeProfileDraftContext }
: { } ) ,
... ( executionMode === "hardware" && controlTransport !== "wireless_mqtt" && hardwareClientId
? { hardware _client _id : hardwareClientId }
: { } ) ,
@@ -681,6 +817,7 @@
event . payload . mode _profile
&& event . payload . orchestration _route === "mode_profile_create"
) {
clearActiveModeDraftContext ( ) ;
appendModeCreatedState ( message , event . payload . mode _profile ) ;
}
}
@@ -859,6 +996,7 @@
resizeComposerInput ( input ) ;
} , 300 ) ;
renderInitialMessages ( ) ;
activeModeDraftContext = loadActiveModeDraftContext ( ) ;
setComposerPending ( false ) ;
}
@@ -873,6 +1011,7 @@
const target = event . target instanceof Element ? event . target : null ;
if ( ! target ? . closest ( "#newConversation" ) ) return ;
sessionStorage . removeItem ( STORAGE _KEY ) ;
clearActiveModeDraftContext ( ) ;
sessionStorage . setItem ( CONVERSATION _STORAGE _KEY , createConversationId ( ) ) ;
} , true ) ;