返回 DeepSeek-Reasonix
services.go
根目录 / internal / agent / services.go
1 package agent
2
3 import (
4 "strings"
5 "sync"
6
7 "reasonix/internal/checkpoint"
8 "reasonix/internal/diff"
9 "reasonix/internal/event"
10 "reasonix/internal/extension/dispatch"
11 "reasonix/internal/jobs"
12 "reasonix/internal/mcpinteraction"
13 "reasonix/internal/memory"
14 "reasonix/internal/provider"
15 "reasonix/internal/sandbox"
16 "reasonix/internal/sessiontemp"
17 "reasonix/internal/tool"
18 "reasonix/internal/workspacelease"
19 )
20
21 // agentServices are the collaborators an Agent talks to, separated from the
22 // state it remembers. This is not a lifetime: the controller rebinds most of
23 // these between turns through the Set* seams, and fork wraps prov mid-run. A
24 // nil field is a capability the host did not wire, which each reader handles.
25 type agentServices struct {
26 prov provider.Provider
27 tools *tool.Registry
28 // pricing turns provider usage into money for the task budget.
29 pricing *provider.Pricing
30 quoteContext *event.QuoteContext
31 // sink receives the turn's typed event stream. Frontends decide how to
32 // render it; never nil because New defaults it to event.Discard.
33 sink event.Sink
34 // warnState rate-limits recovery retries across sessions and processes by an
35 // opaque provider-configuration fingerprint (#7059). The legacy type and file
36 // names preserve the on-disk v2 contract. nil keeps in-memory gating only.
37 warnState *missingReasoningWarnState
38 // gate is the per-call permission gate for both standard and Plan
39 // workflows. Runtime approval-mode switches replace it while a turn may be
40 // executing, so every read/write goes through gateSnapshot/setGate.
41 gateMu sync.RWMutex
42 gate Gate
43 // extensions is the frozen Extension Protocol v2 dispatcher for this
44 // controller generation; nil means every intercept point passes through
45 // byte-identically. See extensions.go.
46 extensions *dispatch.Dispatcher
47 // planTrust is retained for legacy controller wiring. The main Plan
48 // execution path no longer consults it.
49 planTrust PlanModeReadOnlyTrustGate
50 // sandboxEscape can ask the user whether one shell command may rerun
51 // unconfined after the OS sandbox failed to start.
52 sandboxEscape sandbox.EscapeApprover
53 // permissionPreset returns the host-authoritative execution preset for each
54 // call. Keeping it host-only avoids tool-schema and prompt-prefix churn.
55 permissionPreset func() string
56 // configWrite can ask the user whether a file tool may write a
57 // Reasonix-managed config file outside the workspace roots.
58 configWrite tool.ConfigWriteApprover
59 // writeRoots is the session-scoped writable directory manager.
60 writeRoots *sandbox.WritableRootSet
61 // writeAccess authorizes extra writable directories. nil skips expansion
62 // except for a fail-closed missing-dir check when writeRoots is set.
63 writeAccess WriteAccessGate
64 // writeAccessExpandable is false for sub-agents: they inherit roots but
65 // cannot request new directories.
66 writeAccessExpandable bool
67 workspaceRoot string
68 sessionTemp *sessiontemp.Manager
69 homeDir string
70 stateRoot string
71 // hooks fires PreToolUse / PostToolUse shell hooks around each tool call.
72 hooks ToolHooks
73 // asker lets the `ask` tool put questions to the user; nil in headless runs.
74 asker Asker
75 // interactionBroker carries MCP server-initiated elicitations to the user
76 // for tool calls whose ctx reaches the SDK elicitation handler; nil in
77 // headless runs, where requests cancel instead of guessing.
78 interactionBroker mcpinteraction.Broker
79 // preEdit is the seam the checkpoint store uses to snapshot pre-edit
80 // content. Only non-ReadOnly tool.Previewer tools fire it, so bash — whose
81 // targets are unknowable — is never tracked. Prefer mutationObserver.
82 preEdit func(diff.Change)
83 // mutationObserver is the host-side unified file mutation observer: it
84 // captures preimages before tools run and fingerprints after, regardless of
85 // outcome. Never changes provider-visible schemas or prompts.
86 mutationObserver *checkpoint.MutationObserver
87 // jobs is the session's background-job manager, stamped onto each tool
88 // call's context so the background tools can reach it. nil degrades
89 // gracefully.
90 jobs *jobs.Manager
91 // writeScheduler coordinates parent-agent writes against background
92 // subagent write claims. Set on the parent executor only.
93 writeScheduler *SubagentScheduler
94 // workspaceLease is shared by every writer-capable agent in one Delivery
95 // session, acquired lazily on the first mutation and held through the final
96 // participating run so verification stays isolated.
97 workspaceLease *workspacelease.Owner
98 // memQueue lets the remember/forget tools fold a turn-tail note about a
99 // just-made memory change into the next turn, so it applies this session
100 // without touching the cache-stable prefix.
101 memQueue memory.Queue
102 // sessionCheckpointer flushes the accepted event prefix immediately before
103 // model and top-level tool side effects.
104 sessionCheckpointer SessionCheckpointer
105 }
106
107 func (s *agentServices) gateSnapshot() Gate {
108 s.gateMu.RLock()
109 defer s.gateMu.RUnlock()
110 return s.gate
111 }
112
113 func (s *agentServices) setGate(g Gate) {
114 s.gateMu.Lock()
115 s.gate = g
116 s.gateMu.Unlock()
117 }
118
119 // newAgentServices binds the collaborators New resolved. It exists so New stays
120 // under the function-size limit and so adding a collaborator touches one place.
121 func newAgentServices(
122 prov provider.Provider, tools *tool.Registry, sink event.Sink, gate Gate,
123 planTrust PlanModeReadOnlyTrustGate, sandboxEscape sandbox.EscapeApprover,
124 configWrite tool.ConfigWriteApprover, hooks ToolHooks, opts Options,
125 ) agentServices {
126 return agentServices{
127 prov: prov,
128 tools: tools,
129 pricing: opts.Pricing,
130 quoteContext: opts.QuoteContext,
131 sink: sink,
132 gate: gate,
133 extensions: opts.Extensions,
134 planTrust: planTrust,
135 sandboxEscape: sandboxEscape,
136 configWrite: configWrite,
137 hooks: hooks,
138 jobs: opts.Jobs,
139 memQueue: opts.MemoryQueue,
140 sessionCheckpointer: opts.SessionCheckpointer,
141 writeScheduler: opts.WriteScheduler,
142 workspaceLease: opts.WorkspaceLease,
143 warnState: missingReasoningWarnStateFor(opts.MissingReasoningWarnStateDir),
144 mutationObserver: opts.MutationObserver,
145 writeRoots: opts.WriteRoots,
146 writeAccess: opts.WriteAccessGate,
147 writeAccessExpandable: opts.SubagentDepth == 0 && !opts.DisableWriteAccessExpand,
148 workspaceRoot: strings.TrimSpace(opts.WriteWorkspaceRoot),
149 sessionTemp: opts.SessionTemp,
150 homeDir: strings.TrimSpace(opts.HomeDir),
151 stateRoot: strings.TrimSpace(opts.StateRoot),
152 }
153 }
154
154 lines GO