Files
ARTEX/db/constraints.go
T
RuoJi6 c0d1aa6f37 feat(intercept): 模型审查补充任务上下文与裁决说明 (#124)
审查模型改为接收版本化 JSON:任务描述/目标/操作约束、工作目录、Worker 意图、当前轮输入,以及按 tool_use_id 配对的近期工具执行记录;末尾是完整的 tool_name 与 arguments。

- 任务约束每次审查从数据库重新读取;读取失败或约束超预算转人工。
- 历史只保留唯一配对的执行事实,排除 Agent 自述、未完成请求与平台拒绝反馈。
- 裁决改为 JSON decision(allow/ask/deny) + comment,三种结果均须说明实际操作、成功后果与命中规则。
- 所有模型裁决(含自动放行)保存实际模型输入与 SHA-256 指纹,详情页可展开。
- 对话审批卡片支持展开完整请求、模型输入、裁决与执行结果。
- 修复父目录 lockfile 导致的 Turbopack 根目录误判。

官方默认拦截规则、工具范围与优先级不变。
2026-09-15 20:08:12 +08:00

104 lines
3.6 KiB
Go

package db
import (
"context"
"encoding/json"
"fmt"
"time"
)
// OperationReviewContext takes one database snapshot of the task's original
// description/goal and registered constraints. Discovered task_scope assets are
// deliberately excluded: discovery does not grant authorization.
func (s *ExplorationStore) OperationReviewContext(ctx context.Context) (description, goal string, constraints []Constraint, err error) {
var raw []byte
err = s.db.QueryRowContext(ctx, `SELECT COALESCE(e.description,''), e.goal,
COALESCE((SELECT jsonb_agg(jsonb_build_object(
'id', c.id, 'kind', c.kind, 'text', c.text, 'origin', COALESCE(c.origin,''), 'created_at', c.created_at
) ORDER BY c.id) FROM task_constraints c WHERE c.exploration_id=e.id), '[]'::jsonb)
FROM explorations e WHERE e.id=$1`, s.expID).Scan(&description, &goal, &raw)
if err == nil {
err = json.Unmarshal(raw, &constraints)
}
return
}
// Constraint is one operator-authored operation constraint for a task: kind=allow
// (permitted operations) or kind=deny (forbidden operations), free-text. Stored in
// task_constraints, keyed by exploration_id (cascades with the exploration).
type Constraint struct {
ID int64 `json:"id"`
Kind string `json:"kind"` // allow | deny
Text string `json:"text"`
Origin string `json:"origin,omitempty"` // goals | human | system
CreatedAt time.Time `json:"created_at"`
}
// ListConstraints returns this exploration's constraints, allow before deny, oldest
// first within each group (stable render order for the prompt block + UI).
func (s *ExplorationStore) ListConstraints() ([]Constraint, error) {
rows, err := s.db.Query(`
SELECT id, kind, text, COALESCE(origin,''), created_at
FROM task_constraints WHERE exploration_id=$1
ORDER BY (kind='deny'), id`, s.expID)
if err != nil {
return nil, err
}
defer rows.Close()
var out []Constraint
for rows.Next() {
var c Constraint
if err := rows.Scan(&c.ID, &c.Kind, &c.Text, &c.Origin, &c.CreatedAt); err != nil {
return nil, err
}
out = append(out, c)
}
return out, rows.Err()
}
// AddConstraint inserts one constraint (kind must be allow|deny) and returns its id.
func (s *ExplorationStore) AddConstraint(kind, text, origin string) (int64, error) {
if kind != "allow" && kind != "deny" {
return 0, fmt.Errorf("kind 必须是 allow 或 deny")
}
if origin == "" {
origin = "system"
}
var id int64
err := s.db.QueryRow(`
INSERT INTO task_constraints(exploration_id, kind, text, origin)
VALUES ($1, $2, $3, $4) RETURNING id`, s.expID, kind, text, origin).Scan(&id)
return id, err
}
// UpdateConstraint rewrites a constraint's kind + text; scoped to this exploration.
// Returns an error if no such constraint exists.
func (s *ExplorationStore) UpdateConstraint(id int64, kind, text string) error {
if kind != "allow" && kind != "deny" {
return fmt.Errorf("kind 必须是 allow 或 deny")
}
res, err := s.db.Exec(`
UPDATE task_constraints SET kind=$1, text=$2, updated_at=now()
WHERE id=$3 AND exploration_id=$4`, kind, text, id, s.expID)
if err != nil {
return err
}
if n, _ := res.RowsAffected(); n == 0 {
return fmt.Errorf("约束不存在")
}
return nil
}
// DeleteConstraint removes a constraint; scoped to this exploration. Returns an
// error if no such constraint exists.
func (s *ExplorationStore) DeleteConstraint(id int64) error {
res, err := s.db.Exec(`DELETE FROM task_constraints WHERE id=$1 AND exploration_id=$2`, id, s.expID)
if err != nil {
return err
}
if n, _ := res.RowsAffected(); n == 0 {
return fmt.Errorf("约束不存在")
}
return nil
}