Compare commits

..
Author SHA1 Message Date
ji ruo 524f8bc632 fix: 隐藏筛选与请求标签滚动条 2026-10-30 09:11:05 +08:00
8 changed files with 24 additions and 64 deletions

No files matched your search

-1
View File
@@ -12,7 +12,6 @@ import (
// Missing host/setting means off. Rechecked on every write for running sessions. // Missing host/setting means off. Rechecked on every write for running sessions.
var IntelligenceEnabled func() bool var IntelligenceEnabled func() bool
var IntelligenceRecord func(context.Context, int64, int64, string, db.IntelligenceInput) (int64, error) var IntelligenceRecord func(context.Context, int64, int64, string, db.IntelligenceInput) (int64, error)
var CredentialRecorded func()
func intelligenceEnabled() bool { return IntelligenceEnabled != nil && IntelligenceEnabled() } func intelligenceEnabled() bool { return IntelligenceEnabled != nil && IntelligenceEnabled() }
func intelligenceSchema() map[string]any { func intelligenceSchema() map[string]any {
+16 -17
View File
@@ -39,23 +39,22 @@ type CredentialTraffic struct {
Accounts []int `json:"accounts"` Accounts []int `json:"accounts"`
} }
type CredentialAnalysis struct { type CredentialAnalysis struct {
ConversationID int64 `json:"conversation_id,omitempty"` ForceExtract bool `json:"force_extract,omitempty"`
ForceExtract bool `json:"force_extract,omitempty"` TrafficNote string `json:"traffic_note,omitempty"`
TrafficNote string `json:"traffic_note,omitempty"` State string `json:"state"`
State string `json:"state"` Lease string `json:"lease,omitempty"`
Lease string `json:"lease,omitempty"` LeaseUntil time.Time `json:"lease_until,omitempty"`
LeaseUntil time.Time `json:"lease_until,omitempty"` SourceHash string `json:"source_hash,omitempty"`
SourceHash string `json:"source_hash,omitempty"` Method string `json:"method,omitempty"`
Method string `json:"method,omitempty"` Accounts []CredentialAccount `json:"accounts"`
Accounts []CredentialAccount `json:"accounts"` Traffic []CredentialTraffic `json:"traffic"`
Traffic []CredentialTraffic `json:"traffic"` TrafficState string `json:"traffic_state"`
TrafficState string `json:"traffic_state"` Note string `json:"note,omitempty"`
Note string `json:"note,omitempty"` Error string `json:"error,omitempty"`
Error string `json:"error,omitempty"` Model string `json:"model,omitempty"`
Model string `json:"model,omitempty"` InputTokens int `json:"input_tokens"`
InputTokens int `json:"input_tokens"` OutputTokens int `json:"output_tokens"`
OutputTokens int `json:"output_tokens"` ProcessedAt *time.Time `json:"processed_at,omitempty"`
ProcessedAt *time.Time `json:"processed_at,omitempty"`
} }
func CredentialSourceHash(content string) string { func CredentialSourceHash(content string) string {
-7
View File
@@ -233,13 +233,6 @@ func wireTools(pg *db.DB, domainReg map[string]actool.CoreTool) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
if item.Kind == "credential" {
// The durable database row is the source of truth; this wake-up only
// reduces latency. The recovery ticker handles missed notifications.
if agent.CredentialRecorded != nil {
agent.CredentialRecorded()
}
}
return item.ID, nil return item.ID, nil
} }
// Seed the built-in domain tools (first-insert only; DO NOTHING preserves edits). // Seed the built-in domain tools (first-insert only; DO NOTHING preserves edits).
+1 -25
View File
@@ -2,7 +2,6 @@ package server
import ( import (
"context" "context"
"encoding/json"
"io" "io"
"log" "log"
"net/http" "net/http"
@@ -42,16 +41,13 @@ func (s *Server) seedCredentialProcessor() {
_ = s.m.pg.SetBool(flag, true) _ = s.m.pg.SetBool(flag, true)
} }
func (s *Server) runCredentialProcessor() { func (s *Server) runCredentialProcessor() {
// Events wake the worker immediately. The slow ticker is only a recovery ticker := time.NewTicker(3 * time.Second)
// scan for rows created before a notification or after a process restart.
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop() defer ticker.Stop()
for { for {
select { select {
case <-s.ctx.Done(): case <-s.ctx.Done():
return return
case <-ticker.C: case <-ticker.C:
case <-s.credentialWake:
} }
if !s.m.pg.GetBool(db.SettingTaskIntelligence, false) || !s.m.pg.GetBool(db.SettingCredentialAgent, false) { if !s.m.pg.GetBool(db.SettingTaskIntelligence, false) || !s.m.pg.GetBool(db.SettingCredentialAgent, false) {
continue continue
@@ -97,14 +93,6 @@ func (s *Server) processCredentialJob(parent context.Context, item *db.Intellige
} }
}() }()
result := *item.Credentials result := *item.Credentials
conversationID := result.ConversationID
if conversationID == 0 {
if c, err := s.m.pg.CreateConversation(db.CredentialAgentKey, "凭据整理 · "+firstLine(item.Title, 42), nil); err == nil {
conversationID = c.ID
result.ConversationID = c.ID
_, _ = s.m.pg.AppendConvActivity(c.ID, db.Activity{Worker: db.CredentialAgentKey, Kind: "user", Summary: "收到凭据上报,开始整理", Detail: item.Content})
}
}
needsExtract := result.ForceExtract || result.SourceHash != db.CredentialSourceHash(item.Content) || result.Method == "" || (result.Error != "" && len(result.Accounts) == 0) needsExtract := result.ForceExtract || result.SourceHash != db.CredentialSourceHash(item.Content) || result.Method == "" || (result.Error != "" && len(result.Accounts) == 0)
result.State = "ready" result.State = "ready"
result.ForceExtract = false result.ForceExtract = false
@@ -197,20 +185,8 @@ func (s *Server) processCredentialJob(parent context.Context, item *db.Intellige
if err := s.m.pg.FinishCredentialJob(finish, item, result); err != nil { if err := s.m.pg.FinishCredentialJob(finish, item, result); err != nil {
log.Printf("[credentials] save record #%d: %v", item.ID, err) log.Printf("[credentials] save record #%d: %v", item.ID, err)
} }
if conversationID > 0 {
detail, _ := json.Marshal(result)
kind, summary := "result", "凭据整理完成"
isError := result.State == "error"
if isError {
kind, summary = "text", "凭据整理失败:"+result.Error
}
_, _ = s.m.pg.AppendConvActivity(conversationID, db.Activity{Worker: db.CredentialAgentKey, Kind: kind, IsError: isError, Summary: summary, Detail: string(detail), InputTokens: intPtr(result.InputTokens), OutputTokens: intPtr(result.OutputTokens)})
_ = s.m.pg.TouchConversation(conversationID)
}
} }
func intPtr(v int) *int { return &v }
var credentialURLs = regexp.MustCompile(`https?://[^\s<>"',;))]+`) var credentialURLs = regexp.MustCompile(`https?://[^\s<>"',;))]+`)
var credentialTrafficIDs = regexp.MustCompile(`\b[0-9]{8,}-[0-9]{3,}\b`) var credentialTrafficIDs = regexp.MustCompile(`\b[0-9]{8,}-[0-9]{3,}\b`)
+4 -11
View File
@@ -125,10 +125,9 @@ type Server struct {
// Cold task archives run through one persistent FIFO worker. The buffered wake // Cold task archives run through one persistent FIFO worker. The buffered wake
// channel coalesces enqueue bursts; the database remains the source of truth. // channel coalesces enqueue bursts; the database remains the source of truth.
archiveWake chan struct{} archiveWake chan struct{}
archiveWG sync.WaitGroup archiveWG sync.WaitGroup
credentialWake chan struct{} side *sideQuestionState
side *sideQuestionState
} }
// provEntry is a cached provider + its config for one LLM profile id. // provEntry is a cached provider + its config for one LLM profile id.
@@ -162,7 +161,7 @@ func New(ctx context.Context, m *Manager, skillDir string, dataDir string, keyDi
triggerActive: map[string]int{}, triggerCfg: map[string]triggerBehavior{}, triggerActive: map[string]int{}, triggerCfg: map[string]triggerBehavior{},
profChatAgents: map[int64]*agent.ChatAgent{}, profChatAgents: map[int64]*agent.ChatAgent{},
provByProfile: map[int64]*provEntry{}, llmHealth: newLLMHealthRegistry(m.pg), provByProfile: map[int64]*provEntry{}, llmHealth: newLLMHealthRegistry(m.pg),
taskAgents: map[string]*taskAgentBundle{}, archiveWake: make(chan struct{}, 1), credentialWake: make(chan struct{}, 1)} taskAgents: map[string]*taskAgentBundle{}, archiveWake: make(chan struct{}, 1)}
s.initSideQuestions() s.initSideQuestions()
if m.pg != nil { if m.pg != nil {
if err := s.basicAuth.load(m.pg); err != nil { if err := s.basicAuth.load(m.pg); err != nil {
@@ -283,12 +282,6 @@ func New(ctx context.Context, m *Manager, skillDir string, dataDir string, keyDi
go s.reconcileConcurrency() go s.reconcileConcurrency()
s.startTaskArchiveWorker() s.startTaskArchiveWorker()
if m.pg != nil { if m.pg != nil {
agent.CredentialRecorded = func() {
select {
case s.credentialWake <- struct{}{}:
default:
}
}
s.seedCredentialProcessor() s.seedCredentialProcessor()
go s.runCredentialProcessor() go s.runCredentialProcessor()
} }
@@ -923,7 +923,7 @@ export default function FindingsPage() {
})} })}
</div> </div>
<div className="flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-1"> <div className="scrollbar-none flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-1">
<InputGroup className="w-72 shrink-0"> <InputGroup className="w-72 shrink-0">
<InputGroupInput <InputGroupInput
type="search" type="search"
@@ -112,7 +112,7 @@ export function RepeaterWorkspace() {
</div> </div>
</div> </div>
<div className="flex min-h-10 min-w-0 items-center rounded-md border bg-muted/20 p-1"> <div className="flex min-h-10 min-w-0 items-center rounded-md border bg-muted/20 p-1">
<div className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto"> <div className="scrollbar-none flex min-w-0 flex-1 items-center gap-1 overflow-x-auto">
{workspace.tabs.map((tab) => { {workspace.tabs.map((tab) => {
const selected = tab.id === workspace.activeTabId; const selected = tab.id === workspace.activeTabId;
return ( return (
+1 -1
View File
@@ -14,7 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
data-slot="input-group" data-slot="input-group"
role="group" role="group"
className={cn( className={cn(
"group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5", "group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-1 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/30 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
className className
)} )}
{...props} {...props}