Files
Agent-System-Backend/internal/store/context_manifest.go
T
agent-exec-transport 8c410b9ea4
governance / governance (pull_request) Successful in 14s
ci / vet-and-test (pull_request) Successful in 1m30s
agent-exec transport 412eff9b36afd457500606d468b89f94/0636a7e1c0f0
2026-10-20 15:59:51 +00:00

134 lines
4.9 KiB
Go

package store
import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"agent-system-backend/pkg/extension"
)
const contextManifestAction = "model.context_manifest"
// ContextManifestScope records only that the request came from a trusted model
// guard. Raw goals and model output are never persisted in this receipt.
type ContextManifestScope struct {
Bound bool
}
// AppendContextManifest writes to the trusted receipt table, never to the
// plugin-writable generic audit log. A terminal state must follow a matching
// attempted state for the same owner, target, project and input hash.
func (s *Store) AppendContextManifest(owner, targetID string, m extension.ProjectContextManifest) error {
return s.appendContextManifest(owner, targetID, m, ContextManifestScope{})
}
func (s *Store) AppendContextManifestWithScope(owner, targetID string, m extension.ProjectContextManifest, scope ContextManifestScope) error {
if !scope.Bound {
return errors.New("trusted context manifest resolve scope is not bound")
}
return s.appendContextManifest(owner, targetID, m, scope)
}
func (s *Store) appendContextManifest(owner, targetID string, m extension.ProjectContextManifest, scope ContextManifestScope) error {
if owner == "" || targetID == "" || m.ProjectID == "" || m.InputSHA256 == "" || m.AttemptID == "" {
return errors.New("context manifest requires owner, target, project, attempt and input hash")
}
if m.Delivery != "attempted" && m.Delivery != "responded" && m.Delivery != "unknown" {
return errors.New("context manifest has invalid delivery state")
}
if err := requireRecheckableSources(m); err != nil {
return err
}
raw, err := json.Marshal(m)
if err != nil {
return err
}
tx, err := s.db.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if m.Delivery != "attempted" {
var attemptedBound int
err := tx.QueryRow(s.q(`SELECT scope_bound FROM context_manifests WHERE attempt_id=? AND delivery='attempted' AND owner=? AND target_id=? AND project_id=? AND input_sha256=?`),
m.AttemptID, owner, targetID, m.ProjectID, m.InputSHA256).Scan(&attemptedBound)
if errors.Is(err, sql.ErrNoRows) {
return errors.New("context manifest terminal receipt lacks matching attempt")
}
if err != nil {
return err
}
if (attemptedBound != 0) != scope.Bound {
return errors.New("context manifest terminal resolve scope changed")
}
var terminal int
err = tx.QueryRow(s.q(`SELECT 1 FROM context_manifests WHERE attempt_id=? AND delivery IN ('responded','unknown') LIMIT 1`), m.AttemptID).Scan(&terminal)
if err == nil {
return errors.New("context manifest attempt already terminal")
}
if !errors.Is(err, sql.ErrNoRows) {
return err
}
}
bound := 0
if scope.Bound {
bound = 1
}
_, err = tx.Exec(s.q(`INSERT INTO context_manifests(id,attempt_id,owner,target_id,project_id,input_sha256,delivery,manifest_json,created_at,scope_bound) VALUES(?,?,?,?,?,?,?,?,?,?)`),
NewID("ctxm"), m.AttemptID, owner, targetID, m.ProjectID, m.InputSHA256, m.Delivery, string(raw), nowISO(), bound)
if err != nil {
return fmt.Errorf("insert context manifest: %w", err)
}
return tx.Commit()
}
// requireRecheckableSources refuses a receipt, and therefore the model request
// it gates, when an actual input could not be reauthorized on later reuse.
func requireRecheckableSources(m extension.ProjectContextManifest) error {
for _, e := range m.Included {
if e.SourceID == "" || e.Revision == "" || e.SourceHash == "" || e.InputSHA256 == "" {
return errors.New("context manifest included source lacks stable identity")
}
}
for _, ref := range m.OfferedReferences {
if ref.ID == "" || ref.SourceID == "" || ref.Revision == "" || ref.Hash == "" {
return errors.New("context manifest offered reference lacks reauthorizable identity")
}
}
for _, e := range m.ReadEvidence {
if e.ReferenceID == "" || e.SourceID == "" || e.Revision == "" || e.SourceHash == "" || e.InputSHA256 == "" {
return errors.New("context manifest read evidence lacks reauthorizable identity")
}
}
return nil
}
// ListContextManifests reads receipts only within the authenticated owner's
// scope. The target is a run ID for ordinary work, or a conversation ID for
// direct WorkCompletionHost calls.
func (s *Store) ListContextManifests(owner, targetID string) ([]extension.ProjectContextManifest, error) {
if owner == "" || targetID == "" {
return nil, errors.New("context manifest query requires owner and target")
}
rows, err := s.db.Query(s.q(`SELECT manifest_json FROM context_manifests WHERE owner=? AND target_id=? ORDER BY created_at,id`), owner, targetID)
if err != nil {
return nil, err
}
defer rows.Close()
var out []extension.ProjectContextManifest
for rows.Next() {
var raw string
if err := rows.Scan(&raw); err != nil {
return nil, err
}
var m extension.ProjectContextManifest
if err := json.Unmarshal([]byte(raw), &m); err != nil {
return nil, err
}
out = append(out, m)
}
return out, rows.Err()
}