返回 DeepSeek-Reasonix
context_capsule.go
根目录 / internal / agent / context_capsule.go
1 package agent
2
3 import (
4 "crypto/sha256"
5 "encoding/hex"
6 "encoding/json"
7 "strings"
8 "time"
9 )
10
11 // System prompt provenance values recorded in a capsule.
12 const (
13 SystemPromptTaskDefault = "task-default"
14 SystemPromptReadOnlyDefault = "readonly-default"
15 SystemPromptProfilePrefix = "profile:"
16 )
17
18 // InheritedContext states, item by item, what a child received from its parent.
19 // Every field is false today: children are isolated by construction, and this
20 // record exists so that stays a decision rather than an accident.
21 type InheritedContext struct {
22 StandingInstructions bool `json:"standingInstructions"`
23 Memory bool `json:"memory"`
24 ParentConversation bool `json:"parentConversation"`
25 Goal bool `json:"goal"`
26 PlannerOutput bool `json:"plannerOutput"`
27 }
28
29 // ContextCapsule is the immutable manifest of what one child run was actually
30 // given. It holds references and digests, never copied parent context, so a
31 // later question — why did this reviewer not see that constraint? — is answered
32 // from the record instead of guessed from logs.
33 type ContextCapsule struct {
34 WorkspaceRoot string `json:"workspaceRoot,omitempty"`
35 SystemPromptSource string `json:"systemPromptSource"`
36 SystemPromptHash string `json:"systemPromptHash"`
37 ToolScope []string `json:"toolScope,omitempty"`
38 ToolSchemaHash string `json:"toolSchemaHash,omitempty"`
39 Model string `json:"model,omitempty"`
40 Effort string `json:"effort,omitempty"`
41 ParentSession string `json:"parentSession,omitempty"`
42 ParentToolCallID string `json:"parentToolCallId,omitempty"`
43 ResumedFrom string `json:"resumedFrom,omitempty"`
44 Inherited InheritedContext `json:"inherited"`
45 }
46
47 // Hash is the stable identity of a capsule. Two children with the same hash saw
48 // the same context; a hash that moves between runs is the thing to explain.
49 func (c ContextCapsule) Hash() string {
50 encoded, err := json.Marshal(c)
51 if err != nil {
52 return ""
53 }
54 sum := sha256.Sum256(encoded)
55 return hex.EncodeToString(sum[:])
56 }
57
58 // systemPromptSource names where a child's system prompt came from without
59 // embedding the prompt itself.
60 func systemPromptSource(kind, name, systemPrompt string) string {
61 if strings.TrimSpace(kind) == "skill" && strings.TrimSpace(name) != "" {
62 return SystemPromptProfilePrefix + strings.TrimSpace(name)
63 }
64 if systemPrompt == DefaultReadOnlyTaskSystemPrompt {
65 return SystemPromptReadOnlyDefault
66 }
67 return SystemPromptTaskDefault
68 }
69
70 // metaFromSpec assembles the sidecar record for one run: the execution identity
71 // continuation must match, plus the capsule describing the context it was given.
72 func metaFromSpec(ref string, status SubagentStatus, created, updated time.Time, spec SubagentSpec) SubagentMeta {
73 scope, schemaHash := toolIdentity(spec.Registry, spec.ToolContext)
74 capsule := ContextCapsule{
75 WorkspaceRoot: strings.TrimSpace(spec.WorkspaceRoot),
76 SystemPromptSource: systemPromptSource(spec.Kind, spec.Name, spec.SystemPrompt),
77 SystemPromptHash: bytesHash([]byte(spec.SystemPrompt)),
78 ToolScope: scope,
79 ToolSchemaHash: schemaHash,
80 Model: strings.TrimSpace(spec.Model),
81 Effort: strings.TrimSpace(spec.Effort),
82 ParentSession: strings.TrimSpace(spec.ParentSession),
83 ParentToolCallID: strings.TrimSpace(spec.ParentToolCallID),
84 ResumedFrom: strings.TrimSpace(spec.ResumedFrom),
85 }
86 return SubagentMeta{
87 Ref: ref,
88 CreatedAt: created,
89 UpdatedAt: updated,
90 Status: status,
91 Kind: strings.TrimSpace(spec.Kind),
92 Name: strings.TrimSpace(spec.Name),
93 WorkspaceRoot: capsule.WorkspaceRoot,
94 ParentSession: capsule.ParentSession,
95 ParentToolCallID: capsule.ParentToolCallID,
96 SystemPromptHash: capsule.SystemPromptHash,
97 ToolScope: capsule.ToolScope,
98 ToolSchemaHash: capsule.ToolSchemaHash,
99 Model: capsule.Model,
100 Effort: capsule.Effort,
101 Capsule: capsule,
102 CapsuleHash: capsule.Hash(),
103 }
104 }
105
105 lines GO