agent

package
v0.3.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 7, 2026 License: AGPL-3.0 Imports: 36 Imported by: 0

Documentation

Overview

Package agent wires real LLM-driven planner and work agents (on top of the agent-core SDK) to the dual SQLite graph. See docs/ARTEX-架构设计.md §4.3 (planner) and §4.4 (work agent).

Provider configuration is read from the environment so the system runs with any Anthropic- or OpenAI-format endpoint. If no key is configured, FromEnv returns ok=false and the exploration engine stays idle (an LLM is required).

Index

Constants

View Source
const DefaultAssistantPrompt = `` /* 190-byte string literal not displayed */

DefaultAssistantPrompt is the starter/fallback body for CUSTOM conversational agents — they have no per-key in-code default. It is seeded into agent_prompts when a custom agent is created (so the editor isn't blank) and used as the render fallback in RunChat when the DB prompt is somehow missing.

View Source
const ReporterDefaultPrompt = `你是一个授权渗透测试系统里的**漏洞报告撰写 agent**。你不亲自渗透、不做利用——你的唯一职责是:为**刚刚被确认登记的某一个漏洞**撰写一份专业、可复现、面向修复的**详细报告(Markdown)**,并保存回该漏洞。

━━ 你是怎么被唤起的 ━━
每当有 worker 调用 report_finding 登记了一个漏洞,系统就会用一段【由工具调用触发】的上下文唤起你,其中包含:
- **任务 id**(task_id,见上下文"任务: #<id>")
- report_finding 的**入参**(vulnclass / severity / summary / evidence 等)
- report_finding 的**返回**:形如 "finding recorded: <id>" —— 这个 **<id> 是探索节点 ID**,是 get_task_node_detail 和 update_finding_report 使用的旧句柄。返回 JSON 中的 finding_id 则是独立漏洞记录 ID,get_finding_traffic 使用它。

先从上下文里**准确抽取 task_id、探索节点 node_id,以及 JSON 中的独立漏洞 finding_id(如有)**,不得混用两种 ID。抽取不到 node_id 就不要瞎写,说明情况即可。

━━ 工作步骤 ━━
1. **取全证据**:用 get_task_node_detail(task_id, id=<node_id>) 读该漏洞节点的**完整证据/PoC**(触发上下文里的 evidence 可能被截断)。
2. **流量证据**:如返回 JSON 包含独立 finding_id,用 get_finding_traffic 先读有序清单及 version,有绑定时再按 binding_id 分段读取请求/响应。绑定可选,空清单不阻止撰写报告:TCP 等非 HTTP 漏洞或未采集的情况,依据节点证据、命令输出和日志说明复现与影响,建议如实说明未绑定原因,不虚构请求/响应,不仅为补包重新探测。报告引用稳定证据编号及用途;仅按真实内容描述。保存报告时传入所读 version 作为 evidence_version;如版本冲突,重新读取并生成,不得直接换版本重试。
3. **还原过程**:用 list_task_worker_traces(task_id) 找到相关的 work,再用 get_task_worker_trace(task_id, intent_id[, step_ids]) 或 search_task_worker_traces(task_id, q) 看这个漏洞**是怎么被发现和验证的**(用了什么请求/命令、目标怎么响应)。必要时 get_task_graph(task_id) 看整体态势、list_task_findings(task_id) 看是否有关联漏洞。
4. **写报告**:综合以上,写一份结构化 Markdown 报告(见下方模板)。
5. **保存**:调用 **update_finding_report(finding_id=<node_id>, report=<Markdown 全文>, evidence_version=<实际读取的 version>)** 保存;未读取版本时省略 evidence_version,不得猜测。这是你的最终产物——不写进去等于没做。

━━ 报告结构(Markdown,按需裁剪,但证据/复现/修复必须有)━━
- ` + "`## 概述`" + `:一句话说清是什么漏洞、在哪、能造成什么。
- ` + "`## 影响与危害`" + `:结合业务讲清最坏后果(数据泄露/接管/RCE/横向…),给出**严重等级**判断及理由。
- ` + "`## 受影响范围`" + `:受影响的资产/接口/参数/版本。
- ` + "`## 复现步骤`" + `:**可照做复现**的分步操作(请求/命令/参数),能贴 PoC 就贴。
- ` + "`## 证据`" + `:证明漏洞真实存在的关键请求/响应片段、命令输出、回显、截图说明——用代码块贴原文。
- ` + "`## PoC`" + `:可直接运行/复用的利用代码或 payload(利用脚本、请求报文、命令行、payload 串),**通常以代码块给出完整代码**,并简述如何运行;无独立利用代码时说明"复现步骤即为 PoC"。
- ` + "`## 根因分析`" + `:为什么会有这个漏洞(缺校验/危险函数/配置错误…)。
- ` + "`## 修复建议`" + `:具体、可落地的整改措施(不是空话),可含加固与长期建议。

━━ 纪律 ━━
- **只基于真实证据**:报告里的每一条都要能从 finding 证据或 work 执行过程里找到支撑;**绝不臆造**请求、响应、CVE 或结论。证据不足的地方如实标注"未验证/需进一步确认"。
- **面向修复、可核验**:复现步骤要能照做,修复建议要能落地。
- **精炼**:不写套话废话、不复述模板本身。
- 全程**中文**。做完(已成功调用 update_finding_report)就结束,用一两句话说明你为哪个漏洞写了报告即可。`

ReporterDefaultPrompt is the seeded prompt for the "报告撰写"(reporter) custom agent — triggered when report_finding fires. It gathers the finding's full evidence + how it was found, writes a Markdown vulnerability report, and saves it via update_finding_report.

View Source
const RetesterDefaultPrompt = `` /* 1995-byte string literal not displayed */

RetesterDefaultPrompt is seeded once as an editable conversation agent.

Variables

View Source
var (
	// Task-level execution context.
	AbortPausedByUser = cause("paused_by_user", "用户暂停了任务",
		"用户通过任务控制接口(POST /api/tasks/{id}/control,action=pause)暂停了任务。本次 Planner/Worker 运行被主动取消;运行中的意图会退回 frontier(open),恢复任务后重新领取并从头执行")
	AbortPausedByOrchestrator = cause("paused_by_orchestrator", "编排 Agent 暂停了任务",
		"编排 Agent 调用了 pause_task 工具暂停本任务。本次 Planner/Worker 运行被主动取消;运行中的意图会退回 frontier(open),恢复后重新执行")
	AbortTaskDeleted = cause("task_deleted", "任务被删除",
		"任务正在删除(DELETE /api/tasks/{id}),删除屏障已取消该任务正在运行的 Planner、Worker 和主 Agent;本次运行结果不会再被使用")
	AbortPausedOnReload = cause("paused_on_reload", "后端恢复了任务的暂停状态",
		"后端启动时根据数据库中持久化的状态恢复了任务暂停。本次运行被取消;正常情况下恢复阶段没有正在运行的 Agent")
	AbortGoalMet = cause("goal_met", "规划者判定任务目标已达成",
		"规划者判定任务目标已达成并将任务置为 done,随后取消仍在运行的 Worker;这些意图会标记为 stopped,而不是失败")
	AbortSettleDrainTimeout = cause("settle_drain_timeout", "任务超时收尾的等待时间已用尽",
		"任务到达 timeout 后等待正在运行的 Worker 优雅收尾,但 90 秒 drain 宽限仍不足,因此执行硬取消;意图会标记为 exhausted,收尾阶段已经写入的事实和资产会保留")

	// Per-work context.
	AbortKilledByPlanner = cause("killed_by_planner", "规划者终止了这条意图",
		"规划者调用 kill_work 主动终止了这条意图,通常表示方向跑偏或已无继续价值;意图会标记为 stopped,不会自动重新领取")
	AbortWorkPausedByUser = cause("work_paused_by_user", "用户暂停了这条 Worker 意图",
		"用户暂停了正在运行的 Worker。本次调用被取消,意图转为 paused;已经登记的意图、事实、漏洞和活动记录全部保留,恢复后从头重新执行")
	AbortWorkCancelledByUser = cause("work_cancelled_by_user", "用户删除了这条 Worker 意图",
		"用户删除了正在运行的 Worker。本次调用被取消;Worker 退出写入区后,服务端按用户选择的删除模式处理该意图——假删除仅标记为已删除并保留全部产出,真删除会级联移除该意图及仅由它支撑的下游节点")
	AbortWorkFinished = cause("work_finished", "Worker 已正常结束并释放 context",
		"Worker 已正常结束,引擎在 detachWork 中释放其 context 资源。这不是运行中断;若它出现在中断消息中,说明取消与收场事件发生了竞态")
	AbortPausedRaceGuard = cause("paused_race_guard", "任务暂停期间拒绝启动新运行",
		"任务处于暂停状态时,引擎拒绝发出新的执行 context,用于防止 claim 与暂停之间的竞态导致 Worker 继续启动;已领取的意图会退回 frontier")

	// Main Agent and standalone conversation contexts.
	AbortChatStoppedByUser = cause("chat_stopped_by_user", "用户停止了本轮对话",
		"用户点击了停止,主动中止本轮主 Agent 或会话 Agent 运行。已经产生的活动记录会保留,可以继续发送下一条消息")
	AbortChatPausedWithTask = cause("chat_paused_with_task", "任务暂停并中止了主 Agent 对话",
		"用户暂停任务时,正在运行的主 Agent 对话也被同步取消。已经产生的活动记录会保留;恢复任务后不会自动重放本轮消息")
	AbortChatTurnFinished = cause("chat_turn_finished", "本轮对话已正常结束并释放 context",
		"本轮对话已正常结束,服务端正在释放该轮 context 资源。这不是运行中断;若它出现在中断消息中,说明取消与收场事件发生了竞态")

	// Process-level and per-run hard backstop.
	AbortShutdown = cause("shutdown", "后端进程正在关闭",
		"后端进程收到 SIGINT 或 SIGTERM,正在重启、更新或关闭。所有运行中的 Agent 会被取消;重启后残留的 running 意图会重置为 open 并重新执行")
	AbortRunHardTimeout = cause("run_hard_timeout", "单次运行的硬超时兜底已触发",
		"单次运行超过软墙钟预算及额外宽限,说明模型请求或某个工具长时间没有返回,导致正常的回合边界收尾无法执行。请重点检查中断前最后一个未返回的工具调用")
)
View Source
var (
	WrapupTaskTimeoutOverride      func(agentKey string) (string, bool)
	WrapupTaskTimeoutTurnsOverride func(agentKey string) (int, bool)
)

WrapupTaskTimeoutOverride / …TurnsOverride:任务超时收尾词与轮数的 DB 覆盖 (wire 到 agents.task_timeout_wrapup_prompt / _max_turns,仅 worker/planner)。

View Source
var FindingTrafficBindingEnabled func() bool

The server supplies the persisted setting. A missing setting/host is off. Consulted at assembly and again on writes so an already-running session cannot keep binding after the user switches the feature off.

View Source
var PromptOverride func(agentKey string) (string, bool)

PromptOverride, if set, returns the stored system-prompt template for an agent key and whether one exists. The server wires it to the PG agent_prompts table. When nil or no override exists, agents use their built-in default prompt — so behavior is identical until a user edits a prompt in the UI.

View Source
var ToolAugment func(ctx context.Context, agentKey string) (extra []actool.CoreTool, def DeferredInfo, cleanup func())

ToolAugment, if set, returns the EXTRA tools an agent should see beyond its built-in base set — the agent's visible skills (packed into one Skill meta-tool) and visible MCP servers (expanded to mcp__server__tool). It also returns the DeferredInfo describing how those MCP tools are deferred/gated. The server wires it to the PG agent_visibility table. cleanup releases any spawned MCP clients.

When nil, agents run with only their built-in tools — behavior is unchanged until a user assigns a skill/MCP to the agent in the UI.

View Source
var ToolResolve func(ctx context.Context, agentKey string, tools []actool.CoreTool) []actool.CoreTool

ToolResolve, if set, post-processes an agent's fully-assembled tool list against the DB tools table: it drops tools not bound to this agent (or globally disabled) and wraps the rest so the model sees the DB-overridden description/schema and 缺省入参 get injected. Tools with no matching DB row (MCP/skill/host tools like traffic) pass through untouched. nil = tools unchanged. Wired in server/assembly.go.

View Source
var WrapupMaxTurnsOverride func(agentKey string) (int, bool)

WrapupMaxTurnsOverride, if set, returns the admin-configured turn budget for the wrap-up phase of an agent and whether a positive one exists. Wired to the agents table. nil / ≤0 → the built-in per-agent default (wrapupTurnDefaults) is used.

View Source
var WrapupOverride func(agentKey string) (string, bool)

WrapupOverride, if set, returns the stored wrap-up prompt for an agent key and whether a non-empty one exists. Wired by the server to the agents table (like PromptOverride for system prompts). nil / empty → the built-in default is used.

Functions

func AbortReason

func AbortReason(ctx context.Context) (code, short, text string, ok bool)

AbortReason resolves the named cause attached to a cancelled run context.

func BuiltinPromptSeeds

func BuiltinPromptSeeds() map[string]string

BuiltinPromptSeeds returns each built-in agent's default EDITABLE prompt body keyed by agent key. The server seeds these into agent_prompts on startup (only when an agent has no prompt yet), so the DB becomes the authoritative, editable source while the same string stays as the in-code render fallback.

func DecorateTool

func DecorateTool(t actool.CoreTool, desc string, schema map[string]any) actool.CoreTool

DecorateTool wraps t so Description()/InputSchema() report the DB overrides and Call() injects scalar parameter defaults (from schema's "default" props) whenever the model omitted them. Name/Prompt/permission/scheduler flags delegate to t, so the tool's identity and handler are unchanged. Empty desc/schema fall back to t's.

func HintTrafficSchema

func HintTrafficSchema() map[string]any

HintTrafficSchema is shared by the task-local and cross-task hint tools.

func IsQuotaExhaustedMessage

func IsQuotaExhaustedMessage(message string) bool

func TaskTimeoutWrapupDefault

func TaskTimeoutWrapupDefault(agentKey string) string

TaskTimeoutWrapupDefault 返回某 agent 的任务超时内置默认收尾词(供后台占位/恢复默认)。

func TestConnection

func TestConnection(ctx context.Context, c Config) (time.Duration, string, error)

TestConnection makes a minimal real completion to verify the provider/model/ endpoint/key actually work. Returns the round-trip latency and the model's reply text.

func WithRunInfo

func WithRunInfo(ctx context.Context, ri RunInfo) context.Context

WithRunInfo attaches run attribution to ctx.

func WithTaskClock

func WithTaskClock(ctx context.Context, tc TaskClock) context.Context

WithTaskClock attaches a TaskClock to ctx for the run.

func WorkerSessionID

func WorkerSessionID(explorationID, intentID int64) string

WorkerSessionID returns the stable transcript key used by a worker intent. Worker slots are reusable, so the intent id (rather than work#N) is the session identity. Keep this helper public so the Worker message API and UI can refer to exactly the conversation that will be resumed.

func WrapupDefault

func WrapupDefault(agentKey string) string

WrapupDefault returns the built-in default wrap-up prompt for an agent key — used by the admin UI as the "restore default" value and empty-field placeholder.

func WrapupTurnsDefault

func WrapupTurnsDefault(agentKey string) int

WrapupTurnsDefault returns the built-in wrap-up turn budget for an agent key — used by the admin UI as the "0 = default N" hint.

Types

type AbortCause

type AbortCause struct {
	Code  string
	Short string
	Text  string
}

AbortCause names why an agent run's context was cancelled. Every cancellation site should attach one so the activity trace can report the real initiator.

func Causef

func Causef(code, short, format string, args ...any) *AbortCause

Causef builds a cause that includes runtime-specific detail.

func (*AbortCause) Error

func (c *AbortCause) Error() string

type ChatAgent

type ChatAgent struct {
	// contains filtered or unexported fields
}

ChatAgent is the generic, task-independent conversational runner behind the chat page. It generalizes MainAgent.Chat: any agent (built-in OR a custom one, by key) can be chatted with, multi-turn history resumed from the transcript. It is a PURE ASSISTANT — base tools are the SDK DefaultTools (Bash/Read/Write/Edit/ LS/Glob/Grep) plus whatever skills/MCP the key is made visible; NO pentest graph/task context is injected (that stays exclusive to MainAgent).

func NewChatAgent

func NewChatAgent(prov llm.Provider, model, workDir string, tx *transcript.Store, window int) *ChatAgent

func (*ChatAgent) Chat

func (c *ChatAgent) Chat(ctx context.Context, agentKey, sessionID, message string, maxTurns int, maxDuration time.Duration, webSearch bool, emit func(db.Activity)) (string, error)

Chat runs ONE turn of a conversation with the agent identified by agentKey, resuming prior history keyed by sessionID. maxTurns is the per-turn agent step budget (0 = unlimited). maxDuration is the wall-clock run budget per turn (0 = unlimited); the timer resets each time Chat is called, so a new user message always starts a fresh countdown. webSearch gates network search for THIS agent (the global backend/key still come from the chat agent's config, but each agent decides on/off). emit receives each execution step (thinking / tool_use / tool_result / text / result), tagged with the agent key as the worker lane.

func (*ChatAgent) SetGuard

func (c *ChatAgent) SetGuard(g *guard.Guard)

SetGuard attaches a guard (with user-configured intercept rules) to this chat agent. Must be called before Chat; safe to call multiple times.

func (*ChatAgent) SetMaxTokens

func (c *ChatAgent) SetMaxTokens(fn func() int)

SetMaxTokens wires a resolver for the per-reply output cap. nil/unset or 0 = send no cap and let the endpoint decide. Read per run, like nonStreaming.

func (*ChatAgent) SetNoaEnabled

func (c *ChatAgent) SetNoaEnabled(fn func() bool)

SetNoaEnabled wires a resolver deciding whether chat runs use the experimental noa context-compression mechanism. nil/unset = off (built-in compaction). Read per run so the settings toggle takes effect without rebuilding the agent.

func (*ChatAgent) SetNonStreaming

func (c *ChatAgent) SetNonStreaming(fn func() bool)

SetNonStreaming wires a resolver deciding whether chat runs use the non-streaming model path (true = non-streaming). nil/unset = streaming.

func (*ChatAgent) SetProxy

func (c *ChatAgent) SetProxy(addr, caCert string)

SetProxy points the chat agent's WebFetch/Bash at the recording proxy plus the CA cert it trusts (empty addr = direct). Kept for parity with the other agents.

func (*ChatAgent) SetWebSearch

func (c *ChatAgent) SetWebSearch(o WebSearchOpts)

SetWebSearch selects the web_search backend for the chat agent (off by default).

type Compactor

type Compactor struct {
	// contains filtered or unexported fields
}

Compactor performs background cold-node compaction for many explorations.

func NewCompactor

func NewCompactor(prov llm.Provider, model string) *Compactor

NewCompactor builds a compactor. prov/model are used for the §4 body LLM call (same model the agent runs on, per §4). A nil Compactor is a safe no-op.

func (*Compactor) OnPlannerRound

func (c *Compactor) OnPlannerRound(ctx context.Context, ts *db.ExplorationStore)

OnPlannerRound is the single entry the planner calls each wake-up. It bumps the round, maintains the cold stamps synchronously, then (if a threshold is hit and no compaction is running / cooling down) launches a background compaction that outlives this planner round.

type Config

type Config struct {
	Format  llm.Format
	BaseURL string
	APIKey  string
	Model   string
	// Proxy routes all LLM requests through the given proxy URL (http/https/socks5,
	// optionally with user:pass@ credentials). Empty means direct — it does NOT
	// fall back to the standard *_PROXY environment variables.
	Proxy string
	// RatePerSecond / RatePerMinute cap the shared request rate across ALL agents
	// using the provider (0 = that window unlimited).
	RatePerSecond float64
	RatePerMinute float64
	// ContextWindowK is the model's context window in K tokens (user-configured),
	// used to size compaction thresholds. 0 = default; see CompactionWindow.
	ContextWindowK int
	// ThinkingType 独立控制思考「开关」字段(thinking.type):
	//   "" = 不发送(默认,兼容不支持该字段的模型); "disabled" = 显式关闭;
	//   "enabled" = 开启. 与 ReasoningEffort 完全解耦——有些接口没有 thinking 字段、
	//   只靠强度参数就能激活思考,故两者可各自单独设置.
	ThinkingType string
	// ReasoningEffort 独立控制思考「强度」字段:
	//   "" = 不发送(默认); "low"/"medium"/"high"/"xhigh"/"max" = 对应强度.
	//   OpenAI 映射为顶层 reasoning_effort;Anthropic 映射为 output_config.effort.
	ReasoningEffort string
	// Stream 控制该 profile 是否使用流式(SSE)接口。true(默认)= 流式;false = 真·
	// 非流式(发 stream:false,一次性拿完整 JSON,走 Provider.Complete)。非流式可绕开
	// 某些网关糟糕的 SSE 实现(空帧、思考字段丢帧),代价是失去运行中的实时进度/实时
	// token 计数。映射为 agentcore.Options.NonStreaming = !Stream。
	Stream bool
	// MaxTokens 是单次回复的输出上限(token)。0 = 不发送该字段,由服务端默认值决定
	// (历史行为)。与 ContextWindowK 不同:后者是模型总容量,只在本地用来算压缩阈值,
	// 不出现在请求里;本值随每次请求发出。映射为 agentcore.Options.MaxTokens。
	MaxTokens int
	// MaxTokensField 选择 MaxTokens 用哪个请求字段名,仅对 format=openai 生效:
	//   "" = max_tokens(默认); "max_completion_tokens" = 新字段。
	// OpenAI 推理模型(o 系列/GPT-5)只认后者,收到 max_tokens 会直接报
	// unsupported_parameter;而多数兼容网关只认前者,故不做自动推断,交由用户按端点选。
	MaxTokensField string
	// SessionHeaderKey,非空时,让每次 LLM 请求带上一个自定义 HTTP 头,头名为该值、
	// 头值为【当前会话的 session id】(chat 会话=conv-<id>,worker=exp<x>-worker-i<intent>
	// 等,见 WorkerSessionID)。用于某些按 session-id 头做提示缓存/粘性路由的网关。
	// 空 = 不发送。值由 transcript.WithSessionID 挂在请求 context 上,由 RoundTripper
	// 读取填入,因此同一共享 provider 也能按会话发出不同的头值。
	SessionHeaderKey string
	// Retry 是该配置解析后的重试参数(profile 覆盖 → 全局策略 → 内置默认,由
	// server 侧解析)。三层的含义见 RetryConfig;零值 = 完全沿用内置默认。
	Retry RetryConfig
}

Config describes the LLM backend resolved from the environment.

func ConfigFrom

func ConfigFrom(provider, model, baseURL, apiKey, proxy string) Config

ConfigFrom builds a Config from UI-provided strings (provider defaults to anthropic; model defaults per provider). Inputs are trimmed and the base URL is normalized to the API base the provider expects (the provider appends the endpoint path itself), so a full endpoint URL is tolerated.

func FromEnv

func FromEnv() (Config, bool)

FromEnv resolves the LLM provider config:

ARTEX_LLM_PROVIDER = anthropic|openai (default: inferred from keys)
ARTEX_LLM_MODEL    = model id        (default: per provider)
ARTEX_LLM_BASE_URL = endpoint        (optional)
ARTEX_LLM_PROXY    = proxy URL        (optional; http/https/socks5)
ANTHROPIC_API_KEY / OPENAI_API_KEY         = credentials

func (Config) CompactionWindow

func (c Config) CompactionWindow() int

CompactionWindow returns the model context window in TOKENS for compaction thresholds, resolved from the user-configured size (ContextWindowK). 0/unset → a 200K default; otherwise clamped to [32K, 1M] so compaction stays effective.

func (Config) NewProvider

func (c Config) NewProvider() (llm.Provider, error)

NewProvider builds an llm.Provider from the config. When a rate is set, the limiter lives on the single provider instance — so planner + all workers + main agent (which share this provider) are bounded by one shared rate limit.

func (Config) Provider

func (c Config) Provider() string

Provider returns the short provider name ("anthropic"/"openai").

type DeferredInfo

type DeferredInfo struct {
	FindingGuidance string            // derived from the final permitted tools, including DB overrides
	Deferred        []string          // all MCP tool names (schema withheld)
	GlobalNames     []string          // MCP names to list in the system-prompt block
	Unlock          *actool.UnlockSet // shared call-gate; nil when no MCP tools
	UnlockSkill     func(skillName string)
}

DeferredInfo carries the deferred-tools wiring an agent needs to build its Options: the MCP tool names whose schemas are withheld, the subset listed in the global system-prompt block (non-skill-gated), and the shared session unlock set. UnlockSkill unlocks a named skill's MCPs — hosts call it to rebuild the unlock set from history on a resumed session (design doc C2).

func AugmentTools

func AugmentTools(ctx context.Context, agentKey string, base []actool.CoreTool) ([]actool.CoreTool, DeferredInfo, func())

AugmentTools returns base plus the agent's visible skill/MCP tools, the DeferredInfo, and a cleanup func the caller must defer (closes MCP clients). Built-in base tools are kept as-is — never filtered (内置工具留代码层,不做可见性过滤).

type EnrichTrigger

type EnrichTrigger interface {
	ResolveDomain(id int64, host string)
	ProbeSite(id int64, url string)
}

EnrichTrigger is the enrichment engine seen from the tool layer (see package enrich). Kept as an interface here to avoid coupling agent → enrich.

type FindingRecorder

type FindingRecorder interface {
	Record(context.Context, db.RecordFindingInput, []db.TrafficRef) (*db.RecordedFinding, error)
}

FindingRecorder is injected by the host; agents never synthesize or copy evidence bodies themselves. Its implementation owns the atomic write.

type GoalSpec

type GoalSpec struct {
	Text      string `json:"text"`
	VulnClass string `json:"vulnclass,omitempty"`
}

GoalSpec is one decomposed objective.

func DecomposeGoals

func DecomposeGoals(ctx context.Context, prov llm.Provider, dataDir, goalText, desc string, as *db.AssetStore, ts *db.ExplorationStore, taskID int64, emit func(db.Activity)) []GoalSpec

DecomposeGoals asks the LLM to break a pentest task goal into discrete, independently-verifiable objectives (each becomes a goal node). Returns nil if no provider is configured or the call yields nothing — the caller then falls back to a rule-based split so goal nodes always exist.

prov is supplied by the caller (rather than built here from a Config) so goal decomposition rides the SAME provider instance as the rest of the engine — it shares the rate limiter, gets recorded by llmrec, and participates in LLM failover instead of quietly bypassing all three.

desc is the task's free-text description (背景:靶标范围/flag 数量/交战说明等). It is fed alongside the goal so the decomposer no longer splits blind — the prompt still forbids inventing anything the two texts don't state.

emit, when non-nil, receives every LLM step (thinking/tool_use/result) with Worker="planner" so the round-0 goal-decomposition activity is visible in the UI.

as + taskID, when non-nil/positive, wire the add_task_scope tool so the decomposer can register the explicit asset scope it extracts from the goal.

ts is the task's exploration store: set_goals writes the decomposed goal nodes straight into it (the same managed tool the main agent uses to add goals at runtime). The returned specs are read back from the store so callers can emit per-goal activity and detect the "LLM produced nothing" case for their fallback.

func DecomposeGoalsWithProvider

func DecomposeGoalsWithProvider(ctx context.Context, prov llm.Provider, dataDir, goalText, desc string, as *db.AssetStore, ts *db.ExplorationStore, taskID int64, nonStreaming bool, maxTokens int, emit func(db.Activity)) []GoalSpec

DecomposeGoalsWithProvider is the task-runtime variant used when a task has an ordered provider chain. It preserves the same tools and write behavior while letting the caller own provider selection/failover. maxTokens is the profile's per-reply output cap (0 = send none).

type GoalsVars

type GoalsVars struct{ EngagementDescription, DataDir, Now string }

type MainAgent

type MainAgent struct {
	// contains filtered or unexported fields
}

MainAgent is the thin human-interface orchestrator (docs §4.2 / §7). The human chats with it; it observes (read tools), and steers by injecting hints (→planner) or direct high-priority intents (→frontier). It does NOT run the autonomous intent-generation loop (that is the planner's job).

func NewMainAgent

func NewMainAgent(prov llm.Provider, model, workDir string, tx *transcript.Store, window, maxTurns int) *MainAgent

func (*MainAgent) Chat

func (m *MainAgent) Chat(ctx context.Context, taskID int64, mainSeg int, as *db.AssetStore, ts *db.ExplorationStore, goal, message string, emit func(db.Activity), notify, resume func(), notifyGoal, notifyHint func([]string)) (string, error)

Chat handles one human message and returns the assistant reply. emit, if non-nil, receives each execution step (thinking / tool_use / tool_result / text / result) so the main-agent session shows its work — exactly like the worker/planner sessions — not just the final answer.

func (*MainAgent) SetCompactionWindowResolver

func (m *MainAgent) SetCompactionWindowResolver(fn func() int)

func (*MainAgent) SetFindingRecorder

func (m *MainAgent) SetFindingRecorder(r FindingRecorder)

func (*MainAgent) SetMaxTokens

func (m *MainAgent) SetMaxTokens(fn func() int)

SetMaxTokens wires a resolver for the per-reply output cap. nil/unset or 0 = send no cap and let the endpoint decide. Read per run, like nonStreaming.

func (*MainAgent) SetNoaEnabled

func (m *MainAgent) SetNoaEnabled(fn func() bool)

SetNoaEnabled wires a resolver deciding whether runs use the experimental noa context-compression mechanism. nil/unset = off (built-in compaction). Read per run so the settings toggle takes effect without rebuilding the agent.

func (*MainAgent) SetNonStreaming

func (m *MainAgent) SetNonStreaming(fn func() bool)

SetNonStreaming wires a resolver deciding whether runs use the non-streaming model path (true = non-streaming). nil/unset = streaming (default).

func (*MainAgent) SetProxy

func (m *MainAgent) SetProxy(addr, caCert string)

SetProxy points the main agent's WebFetch at the recording proxy plus the CA cert it trusts to verify HTTPS through it (empty addr = direct).

func (*MainAgent) SetSteerWork

func (m *MainAgent) SetSteerWork(fn func(intentID int64, msg string) error)

SetSteerWork wires the engine callback that lets the main agent's steer_work tool inject a mid-run course-correction into a running work (nil = tool off).

func (*MainAgent) SetWebSearch

func (m *MainAgent) SetWebSearch(o WebSearchOpts)

SetWebSearch selects the web_search backend for the main agent (off by default).

type MainVars

type MainVars struct{ Goal, AssetSummary, FindingsSummary, DataDir, Now string }

type Planner

type Planner struct {
	// contains filtered or unexported fields
}

Planner is the event-driven LLM planner (docs §4.3): each time the asset or exploration graph changes (debounced), it reads the exploration route, queries assets, judges whether the task goal is met, and emits 0..N exploration intents into the frontier. It is the sole intent generator.

func NewPlanner

func NewPlanner(prov llm.Provider, model, workDir string, tx *transcript.Store, window, maxTurns int) *Planner

func (*Planner) Plan

func (p *Planner) Plan(ctx context.Context, taskID int64, as *db.AssetStore, ts *db.ExplorationStore, goal string, triggers []TriggerEvent, emit func(db.Activity)) (met bool, reason string, err error)

Plan runs one planning round. emit, if non-nil, receives the planner's execution steps (so users can see how it reads the situation and judges goals — the planner is the intent generator and was previously a black box). Returns whether the planner judged the goal met. triggers carries the concrete change(s) that fired this round — worker(s) done and/or finding(s) reported (may be several — the engine debounces a burst; empty for time/heartbeat wakes). They are spelled out at the top of the prompt so the planner looks first at the actual change (which intent, its output/finding).

func (*Planner) SetCompactionWindowResolver

func (p *Planner) SetCompactionWindowResolver(fn func() int)

func (*Planner) SetCompactor

func (p *Planner) SetCompactor(c *Compactor)

SetCompactor wires the cold-node compactor (cold-digest §7). Called each planner wake-up to advance the round counter, maintain cold stamps, and (off the hot path) fold cold nodes into digests. nil = feature disabled.

func (*Planner) SetConstraintInject

func (p *Planner) SetConstraintInject(fn func() bool)

SetConstraintInject wires a resolver deciding whether this task's operation constraints get injected into the planner system prompt. Read per round so the settings toggle takes effect without rebuilding the agent. nil = inject (default).

func (*Planner) SetFindingRecorder

func (p *Planner) SetFindingRecorder(r FindingRecorder)

func (*Planner) SetKillWork

func (p *Planner) SetKillWork(fn func(intentID int64) error)

SetKillWork wires the engine's per-work terminate callback so the planner's kill_work tool can stop a single running worker.

func (*Planner) SetMaxTokens

func (p *Planner) SetMaxTokens(fn func() int)

SetMaxTokens wires a resolver for the per-reply output cap. nil/unset or 0 = send no cap and let the endpoint decide. Read per run, like nonStreaming.

func (*Planner) SetNoaEnabled

func (p *Planner) SetNoaEnabled(fn func() bool)

SetNoaEnabled wires a resolver deciding whether runs use the experimental noa context-compression mechanism. nil/unset = off (built-in compaction). Read per run so the settings toggle takes effect without rebuilding the agent.

func (*Planner) SetNonStreaming

func (p *Planner) SetNonStreaming(fn func() bool)

SetNonStreaming wires a resolver deciding whether runs use the non-streaming model path (true = non-streaming). nil/unset = streaming (default).

func (*Planner) SetProxy

func (p *Planner) SetProxy(addr, caCert string)

SetProxy points the planner's WebFetch at the recording proxy plus the CA cert it trusts to verify HTTPS through it (empty addr = direct).

func (*Planner) SetSteerWork

func (p *Planner) SetSteerWork(fn func(intentID int64, msg string) error)

SetSteerWork wires the engine's per-work steering callback so the planner's steer_work tool can inject a mid-run course-correction into a running worker.

func (*Planner) SetWebSearch

func (p *Planner) SetWebSearch(o WebSearchOpts)

SetWebSearch selects the web_search backend for the planner (off by default).

type PlannerVars

type PlannerVars struct{ Goal, Scope, AssetSummary, DataDir, Now string }

Prompt-variable structs — fields mirror each agent's catalog (docs §5a) so a user template referencing a catalog variable renders; referencing anything else fails template execution and falls back to the built-in default.

type RetryConfig

type RetryConfig struct {
	// ConnectAttempts/ConnectInterval:SDK 建连重试(连接重置/超时/429/5xx,流开始前),
	// 直接映射为 llm.Config.MaxRetries / RetryInterval。默认 3 次、0.5s 起指数(封顶 8s)。
	ConnectAttempts int
	ConnectInterval time.Duration
	// EmptyAttempts/EmptyInterval:SDK 空响应重试(完成但无 content block,仅 openai
	// 格式),映射为 llm.Config.EmptyResponseRetries / EmptyResponseInterval。
	// 默认 2 次、同一条指数梯度。
	EmptyAttempts int
	EmptyInterval time.Duration
	// StreamAttempts/StreamInterval:同 provider 安全窗口重试——本项目在 SDK 之上补的
	// 一层,只在「还没向调用方交付任何输出」时重放断流/过载/流内 429。SDK 看不到它,
	// 由 server/task_llm.go 消费。默认 2 次、0.5s 起指数(封顶 4s)。
	StreamAttempts int
	StreamInterval time.Duration
}

RetryConfig 是随一个 LLM 配置走的重试参数。每层的「次数」统一语义: 0 = 用内置默认次数;负数 = 关闭该层重试;>0 = 用该值。每层的「间隔」: 0 = 用该层原本的指数退避;>0 = 改用这个固定间隔。

type RunInfo

type RunInfo struct {
	TaskID        int64  // task registry id; 0 for non-task runs (chat sessions)
	ExplorationID int64  // exploration id; 0 when unknown
	IntentID      int64  // worker's intent node; 0 for planner/mainagent/chat
	SessionID     string // chat conversation id; empty for task runs
}

RunInfo identifies WHICH run a tool call belongs to. Tool assembly only receives (ctx, agentKey) — the task/exploration ids live in the caller's arguments, not the ctx — so anything wired at assembly time (currently the Skill ledger in server/assembly.go) has no way to attribute a call to a task. Each run attaches its own RunInfo before calling AugmentTools; the wiring closure reads it once and captures it, so per-run attribution stays correct without threading parameters through the tool layer. Same pattern as TaskClock (see taskclock.go).

Zero value = attribution unknown; every consumer must treat it as optional.

func RunInfoFrom

func RunInfoFrom(ctx context.Context) RunInfo

RunInfoFrom reads the RunInfo (zero value if none attached).

type TaskClock

type TaskClock struct {
	DeadlineUnix int64 // absolute deadline (unix seconds); 0 = no task timeout
	Final        bool  // coordinator-driven FINAL planner round (task ending now)
}

TaskClock carries a task's absolute deadline into a worker/planner run so the run can clamp its own wall-clock budget to the task's remaining time and pick the right wrap-up words (per-run vs task-timeout). Attached to the run ctx by the engine. Zero value = no task-level timeout (behaves exactly as before).

type ToolSeed

type ToolSeed struct {
	Key    string         // = CoreTool.Name(),主键,不可改
	Desc   string         // 顶层描述(可在 UI 覆盖)
	Schema map[string]any // 参数 JSON-Schema(结构只读,description/default 可在 UI 改)
	Agents []string       // 默认绑定的 agent key(worker/planner/mainagent)
}

ToolSeed 是一个内置工具的可播种快照:key 即 CoreTool.Name()(与 handler 死绑, UI 只读),Desc/Schema 取自代码里的工具定义,Agents 是代码默认把它给了哪些 agent。

func BuiltinToolSeeds

func BuiltinToolSeeds() []ToolSeed

BuiltinToolSeeds 把各 agent 的内置工具集去重合并成 seed 列表:同名工具(如 list_assets 多个 agent 都有)合成一条,Agents 取并集;defaultUnbound 里的工具则强制绑定为空。

type ToolSet

type ToolSet struct {
	GoalMet bool
	Reason  string
	// contains filtered or unexported fields
}

ToolSet exposes the PG-backed dual graph (asset + exploration) to an LLM agent. One ToolSet is created per planner/worker run; per-run signals live here.

func NewToolSet

func NewToolSet(ts *db.ExplorationStore, worker string) *ToolSet

func (*ToolSet) AddHintTool

func (t *ToolSet) AddHintTool() actool.CoreTool

func (*ToolSet) AllDomainTools

func (t *ToolSet) AllDomainTools() []actool.CoreTool

AllDomainTools returns the union of all domain tools across all agent types, deduped by name (mainagent order wins). Used by the server to build a registry for injecting domain tools into agents (Auto, custom) that don't own a per-task ToolSet. The caller provides real stores; tools are callable at taskID=0 scope.

func (*ToolSet) CoverageDisabled

func (t *ToolSet) CoverageDisabled() bool

CoverageDisabled reports whether the coverage feature is off for this task.

func (*ToolSet) DropCoverageTools

func (t *ToolSet) DropCoverageTools(tools []actool.CoreTool) []actool.CoreTool

DropCoverageTools returns tools with the coverage-only ones removed when this task has the feature disabled; otherwise it returns tools unchanged.

func (*ToolSet) GetWorkerTraceTool

func (t *ToolSet) GetWorkerTraceTool() actool.CoreTool

func (*ToolSet) GraphOverviewTool

func (t *ToolSet) GraphOverviewTool() actool.CoreTool

Cross-task reuse: exported accessors returning the per-task tool logic bound to THIS ToolSet's store. Host-side orchestration tools build a ToolSet for an arbitrary task, then Call these — so cross-task reads/hint reuse the exact same logic as the in-task tools. (readTool ignores ToolContext, so Call(…,nil) is safe; add_hint is a writeTool but also doesn't deref the context here.)

func (*ToolSet) ListFindingsTool

func (t *ToolSet) ListFindingsTool() actool.CoreTool

func (*ToolSet) ListWorkerTracesTool

func (t *ToolSet) ListWorkerTracesTool() actool.CoreTool

func (*ToolSet) MainAgentTools

func (t *ToolSet) MainAgentTools() []actool.CoreTool

MainAgentTools returns the human-interface tool set.

func (*ToolSet) NodeDetailTool

func (t *ToolSet) NodeDetailTool() actool.CoreTool

func (*ToolSet) PlannerTools

func (t *ToolSet) PlannerTools() []actool.CoreTool

PlannerTools is the read + intent-generation + goal-judgement tool set.

func (*ToolSet) SearchWorkerTracesTool

func (t *ToolSet) SearchWorkerTracesTool() actool.CoreTool

func (*ToolSet) SetAssetStore

func (t *ToolSet) SetAssetStore(as *db.AssetStore, cs *db.CompanyStore)

SetAssetStore wires the asset store and company store onto this ToolSet so the insert_assets, add_company_scope, and list_assets tools are active.

func (*ToolSet) SetCoverageEnabled

func (t *ToolSet) SetCoverageEnabled(enabled bool)

SetCoverageEnabled records whether this task has the asset-coverage feature on (default enabled). Passing false makes graphOverviewData omit the coverage block and DropCoverageTools filter the two coverage-only tools out of the agent's tool list. It does NOT stop scope accumulation: insertAssets' auto-scope hook runs either way, because task_scope is the task's range boundary (the filter basis for asset queries), not merely a coverage denominator.

func (*ToolSet) SetEnrich

func (t *ToolSet) SetEnrich(e EnrichTrigger)

SetEnrich wires the async enrichment engine (DNS/HTTP auto-completion).

func (*ToolSet) SetFindingRecorder

func (t *ToolSet) SetFindingRecorder(r FindingRecorder)

func (*ToolSet) SetNotify

func (t *ToolSet) SetNotify(fn func())

SetNotify wires the planner-wake callback (see ToolSet.notify). Set by callers that hold the task handle (main-agent chat, cross-task orchestration).

func (*ToolSet) SetNotifyFinding

func (t *ToolSet) SetNotifyFinding(fn func(int64, string))

SetNotifyFinding wires the finding-wake callback (see ToolSet.notifyFinding).

func (*ToolSet) SetNotifyGoal

func (t *ToolSet) SetNotifyGoal(fn func([]string))

SetNotifyGoal wires the goal-add trigger callback (see ToolSet.notifyGoal). Set only by the main-agent chat, so runtime-added goals are announced to the planner by name.

func (*ToolSet) SetNotifyHint

func (t *ToolSet) SetNotifyHint(fn func([]string))

SetNotifyHint wires the hint-add trigger callback (see ToolSet.notifyHint). Set by the main-agent chat and cross-task orchestration, so a runtime-added hint fires a planner round announced by name instead of a bare wake.

func (*ToolSet) SetOwnerNode

func (t *ToolSet) SetOwnerNode(id int64)

SetOwnerNode sets the exploration node that writes anchor to (worker: its intent node; planner/main: the begin root). Assets created/referenced while ownerNode is set are anchored to it as lineage (not visibility).

func (*ToolSet) SetResumeTask

func (t *ToolSet) SetResumeTask(fn func())

SetResumeTask wires the task-revive callback (see ToolSet.resumeTask). Set only by the main-agent chat, so runtime-added goals can pull a finished task back to running.

func (*ToolSet) SetTaskID

func (t *ToolSet) SetTaskID(id int64)

SetTaskID sets the PG task id on this ToolSet so that report_finding can dual-write to the standalone findings table (which survives task deletion).

func (*ToolSet) WorkerTools

func (t *ToolSet) WorkerTools() []actool.CoreTool

WorkerTools returns the tool set for a work agent.

func (*ToolSet) Writes

func (t *ToolSet) Writes() WriteCounts

Writes reports what this run wrote back, split by node kind (so the engine can tell "explored but persisted nothing" apart from a completed intent, and log an honest breakdown instead of calling assets/findings "facts").

type TriggerEvent

type TriggerEvent struct {
	Kind     string
	IntentID int64
	Detail   string
	Summary  string   // Kind=="cancelled" 专用:删除前捕获的意图摘要(真删除后节点已不存在,无法再查)
	Goals    []string // Kind=="goal" 专用:本次 set_goals 新增的目标文本(1 条或多条)
	OldGoal  string   // Kind=="goal_edited" 专用:修改前的目标文本
	NewGoal  string   // Kind=="goal_edited" 专用:修改后的目标文本
	Hints    []string // Kind=="hint" 专用:本次 add_hint 新增的提示文本(1 条或多条)
}

TriggerEvent describes what concretely caused this planning round to fire, so the planner looks first at the actual change instead of re-scanning the whole overview. Kind:

"done"    — a worker finished intent IntentID (its output conclusion is fetched).
"finding" — a worker reported a finding on intent IntentID (Detail = 摘要).
"goal"    — the human (via 主 agent 的 set_goals) added one OR MORE goals in a
            single call (Goals = 本次新增的目标文本,1+ 条;set_goals 支持批量).
"goal_deleted" — the human deleted a goal from 总览的目标管理 (Detail = 被删目标文本).
"goal_edited"  — the human edited a goal from 总览的目标管理 (OldGoal→NewGoal 文本).
"cancelled" — the human deleted intent IntentID (Detail = 删除原因). The intent is
            stopped (not deleted) and the reason is attached to it as a fact.

type WebSearchOpts

type WebSearchOpts struct {
	Enabled   bool
	Backend   string
	BraveKey  string
	TavilyKey string
	Proxy     string
	// DeepSeek* 来自当前激活的 LLM 配置(仅 anthropic 格式的 DeepSeek 官方端点),
	// 不单独配置,随 LLM 配置切换而变。
	DeepSeekBaseURL string
	DeepSeekAPIKey  string
	DeepSeekModel   string
}

Worker is an LLM work agent (docs §4.4): it claims ONE intent, completes it with real tools (Bash: kali tooling through the recording proxy), writes the FACTS it found back into the graph, and stops. It does NOT generate new directions (that is the planner's job) and does NOT keep exploring toward the goal on its own. Multiple workers run concurrently as goroutines. WebSearchOpts is the web-search backend selection the server pushes into each agent (planner/worker/main). Enabled=false leaves the web_search tool off. Backend is "ddgs" (no key), "brave-free" (BraveKey required), "tavily" (TavilyKey required), or "deepseek" (DeepSeek* required, filled from the active LLM profile). It maps directly onto agentcore.Options. Proxy is a dedicated egress proxy for the search request (http/https/socks5), independent of the traffic-recording MITM proxy — set it when the search endpoint is only reachable via a VPN/SOCKS proxy. Empty = direct.

注意 deepseek 后端与其它三个的性质不同:DeepSeek 没有可直接调用的搜索接口, 搜索只存在于其 Anthropic 兼容 messages 接口内部(web_search_20250305 server tool),因此每次搜索会消耗一次模型调用,且搜索请求由 DeepSeek 服务端发出—— 不经过本机 Proxy,也不会进流量留痕。

type Worker

type Worker struct {
	// contains filtered or unexported fields
}

func NewWorker

func NewWorker(prov llm.Provider, model, workDir string, tx *transcript.Store, window, maxTurns int, extra ...actool.CoreTool) *Worker

func (*Worker) Execute

func (w *Worker) Execute(ctx context.Context, name string, taskID int64, as *db.AssetStore, ts *db.ExplorationStore, intent *db.Node, hooks harness.HookRunner, emit func(db.Activity), enr EnrichTrigger, notifyFinding func(int64, string)) (harness.TerminalReason, WriteCounts, error)

Execute runs one intent. hooks (the per-task Guard) gates every tool call; may be nil. emit, if non-nil, receives one ActivityRecord per execution step. notifyFinding, if non-nil, is called (intentID, summary) when this worker writes a finding (report_finding) so the task's planner wakes mid-flight — with context on which intent found what — instead of waiting for the worker to finish. Returns the terminal reason (so the engine can distinguish completed vs max_turns) and a per-kind breakdown of what was written back (so an intent that explored but persisted nothing isn't mistaken for done, and the engine can log facts/assets/findings separately instead of lumping them under "facts").

func (*Worker) ExecuteWithMessage

func (w *Worker) ExecuteWithMessage(ctx context.Context, name string, taskID int64, as *db.AssetStore, ts *db.ExplorationStore, intent *db.Node, hooks harness.HookRunner, emit func(db.Activity), enr EnrichTrigger, notifyFinding func(int64, string), requestID, message string) (harness.TerminalReason, WriteCounts, error)

ExecuteWithMessage runs the next turn in the same intent conversation with a human-authored message. The HTTP handler does not edit the transcript; agentcore records the message as a normal user turn when this Worker starts. This keeps Worker continuation identical to the regular agent chat flow.

func (*Worker) SetCompactionWindowResolver

func (w *Worker) SetCompactionWindowResolver(fn func() int)

func (*Worker) SetConstraintInject

func (w *Worker) SetConstraintInject(fn func() bool)

SetConstraintInject wires a resolver deciding whether this task's operation constraints get injected into the worker system prompt. nil = inject (default).

func (*Worker) SetFindingRecorder

func (w *Worker) SetFindingRecorder(r FindingRecorder)

func (*Worker) SetMaxTokens

func (w *Worker) SetMaxTokens(fn func() int)

SetMaxTokens wires a resolver for the per-reply output cap. nil/unset or 0 = send no cap and let the endpoint decide. Read per run, like nonStreaming.

func (*Worker) SetNoaEnabled

func (w *Worker) SetNoaEnabled(fn func() bool)

SetNoaEnabled wires a resolver deciding whether runs use the experimental noa context-compression mechanism. nil/unset = off (built-in compaction). Read per run so the settings toggle takes effect without rebuilding the agent.

func (*Worker) SetNonStreaming

func (w *Worker) SetNonStreaming(fn func() bool)

SetNonStreaming wires a resolver deciding whether runs use the non-streaming model path (true = non-streaming). nil/unset = streaming (default). Read per run so a profile or task-chain toggle takes effect without rebuilding.

func (*Worker) SetProxy

func (w *Worker) SetProxy(addr, caCert string)

SetProxy configures the recording proxy address that workers route target traffic through, plus the CA cert path WebFetch trusts to verify HTTPS through that MITM proxy. Empty addr disables the hint.

func (*Worker) SetRunTimeout

func (w *Worker) SetRunTimeout(run time.Duration)

SetRunTimeout configures the per-intent wall-clock budget for the main exploration (0 = unlimited). When it fires, the SDK settlement phase still runs so facts are never lost to a timeout. Safe to call before Execute.

func (*Worker) SetWebSearch

func (w *Worker) SetWebSearch(o WebSearchOpts)

SetWebSearch selects the web_search backend for this worker (off by default).

type WorkerVars

type WorkerVars struct{ ProxyAddr, WorkerName, DataDir, Now string }

type WriteCounts

type WriteCounts struct {
	Facts    int
	Assets   int
	Findings int
}

WriteCounts breaks down what a worker persisted this run, by node kind, so the engine can log an accurate "wrote back" summary instead of lumping assets and findings under "facts" (record_fact → Facts, insert_assets → Assets, report_finding → Findings; each element of a batch counts once).

func (WriteCounts) String

func (w WriteCounts) String() string

String renders the per-kind breakdown for logs, e.g. "事实1 资产25 漏洞0".

func (WriteCounts) Total

func (w WriteCounts) Total() int

Total is every node persisted this run, regardless of kind — the "explored but persisted nothing" signal (Total == 0).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL