-
Notifications
You must be signed in to change notification settings - Fork 519
Expand file tree
/
Copy pathprovider.go
More file actions
406 lines (354 loc) · 11 KB
/
Copy pathprovider.go
File metadata and controls
406 lines (354 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
package parser
import (
"context"
"errors"
"time"
)
const (
ProviderFeatureFingerprint = "fingerprint"
ProviderFeatureParse = "parse"
)
// ErrUnsupportedProviderFeature identifies optional provider behavior that is
// intentionally absent. Callers use errors.Is to distinguish this from I/O or
// parse failures.
var ErrUnsupportedProviderFeature = errors.New("unsupported provider feature")
// UnsupportedProviderFeatureError wraps ErrUnsupportedProviderFeature with the
// provider and feature names that produced it.
type UnsupportedProviderFeatureError struct {
Provider AgentType
Feature string
}
func (err UnsupportedProviderFeatureError) Error() string {
if err.Provider == "" {
return "unsupported provider feature " + err.Feature
}
return string(err.Provider) + ": unsupported provider feature " + err.Feature
}
func (err UnsupportedProviderFeatureError) Unwrap() error {
return ErrUnsupportedProviderFeature
}
// ProviderFactory is the registry surface for creating config-bound provider
// instances.
type ProviderFactory interface {
Definition() AgentDef
Capabilities() Capabilities
NewProvider(ProviderConfig) Provider
}
// ProviderConfig is copied into a provider instance at construction time.
type ProviderConfig struct {
Roots []string
Machine string
}
// Clone returns an independent config snapshot.
func (cfg ProviderConfig) Clone() ProviderConfig {
cfg.Roots = cfg.RootsCopy()
return cfg
}
// RootsCopy returns an independent roots slice.
func (cfg ProviderConfig) RootsCopy() []string {
return append([]string(nil), cfg.Roots...)
}
// Provider is the target parser/source facade. Providers own source shape and
// return normalized parser results for the sync engine to persist.
type Provider interface {
Definition() AgentDef
Capabilities() Capabilities
Discover(context.Context) ([]SourceRef, error)
WatchPlan(context.Context) (WatchPlan, error)
SourcesForChangedPath(context.Context, ChangedPathRequest) ([]SourceRef, error)
FindSource(context.Context, FindSourceRequest) (SourceRef, bool, error)
Fingerprint(context.Context, SourceRef) (SourceFingerprint, error)
// Parse returns a normalized outcome for one logical source. A non-nil
// error is a whole-source failure, including context cancellation; callers
// must ignore the returned ParseOutcome. Partial multi-session success is
// represented by a nil error with successful Results plus SourceErrors for
// isolated per-session failures.
Parse(context.Context, ParseRequest) (ParseOutcome, error)
ParseIncremental(
context.Context,
IncrementalRequest,
) (IncrementalOutcome, IncrementalStatus, error)
}
// ProviderBase is embedded by concrete providers to make optional source
// methods callable with zero-value no-op behavior.
type ProviderBase struct {
Def AgentDef
Caps Capabilities
Config ProviderConfig
}
func (b ProviderBase) Definition() AgentDef {
return cloneAgentDef(b.Def)
}
func (b ProviderBase) Capabilities() Capabilities {
return b.Caps
}
func (b ProviderBase) Discover(context.Context) ([]SourceRef, error) {
return nil, nil
}
func (b ProviderBase) WatchPlan(context.Context) (WatchPlan, error) {
return WatchPlan{}, nil
}
func (b ProviderBase) SourcesForChangedPath(
context.Context,
ChangedPathRequest,
) ([]SourceRef, error) {
return nil, nil
}
func (b ProviderBase) FindSource(
context.Context,
FindSourceRequest,
) (SourceRef, bool, error) {
return SourceRef{}, false, nil
}
func (b ProviderBase) Fingerprint(
context.Context,
SourceRef,
) (SourceFingerprint, error) {
return SourceFingerprint{}, b.unsupported(ProviderFeatureFingerprint)
}
func (b ProviderBase) ParseIncremental(
context.Context,
IncrementalRequest,
) (IncrementalOutcome, IncrementalStatus, error) {
return IncrementalOutcome{}, IncrementalUnsupported, nil
}
func (b ProviderBase) unsupported(feature string) error {
return UnsupportedProviderFeatureError{
Provider: b.Def.Type,
Feature: feature,
}
}
// SourceRef is the engine-visible handle for provider-owned source data.
type SourceRef struct {
// Provider identifies the provider that created this source and must match
// the provider instance used for subsequent operations.
Provider AgentType
// Key is stable within the provider across process restarts. It is suitable
// for dedupe and diagnostics, but not necessarily for DB freshness checks.
Key string
// DisplayPath is human-readable and may be a virtual path.
DisplayPath string
// FingerprintKey is the persisted lookup key for skip-cache and parser data
// version checks. Migrated providers should keep it compatible with legacy
// file_path values whenever practical.
FingerprintKey string
// ProjectHint is advisory metadata for UI grouping and may be empty.
ProjectHint string
// Opaque is provider-owned in-memory state. The engine must not persist,
// compare, inspect, or log it, and providers must not require it for lookup
// from persisted rows.
Opaque any
}
// WatchPlan describes provider-owned filesystem watch roots.
type WatchPlan struct {
Roots []WatchRoot
}
// WatchRoot is one filesystem root the engine should watch.
type WatchRoot struct {
Path string
Recursive bool
IncludeGlobs []string
ExcludeGlobs []string
DebounceKey string
}
// ChangedPathRequest is passed back to providers for authoritative changed-path
// classification.
type ChangedPathRequest struct {
Path string
EventKind string
WatchRoot string
// StoredSourcePaths are optional provider-persisted source paths already
// known to the caller for this watch root. Providers that model a shared
// physical file as virtual per-session sources use these to emit tombstone
// sources when a DB row or DB file has disappeared and can no longer be
// rediscovered from current metadata.
StoredSourcePaths []string
}
// FindSourceRequest contains persisted source hints for provider-owned lookup.
type FindSourceRequest struct {
RawSessionID string
FullSessionID string
StoredFilePath string
FingerprintKey string
RequireFreshSource bool
PreferStoredSource bool
}
// SourceFingerprint is the provider-normalized source freshness identity.
type SourceFingerprint struct {
Key string
Size int64
MTimeNS int64
Inode uint64
Device uint64
Hash string
}
// ParseRequest is the full-parse provider input.
type ParseRequest struct {
Source SourceRef
Fingerprint SourceFingerprint
Machine string
ForceParse bool
}
// ParseOutcome is the full-parse provider output. It is meaningful only when
// Provider.Parse returns a nil error.
type ParseOutcome struct {
Results []ParseResultOutcome
ExcludedSessionIDs []string
SourceErrors []SourceError
ResultSetComplete bool
ForceReplace bool
SkipReason SkipReason
}
// ParseResultOutcome pairs a normalized parse result with per-session retry and
// data-version state.
type ParseResultOutcome struct {
Result ParseResult
DataVersion DataVersionState
RetryReason string
}
// SourceError reports a per-session parse failure from a multi-session source.
// Providers use the Parse error return instead when a failure cannot be
// isolated to a persisted full session ID.
type SourceError struct {
SourceKey string
DisplayPath string
SessionID string
Err error
Retryable bool
}
// DataVersionState describes whether a parsed result is current for this parser
// data version.
type DataVersionState uint8
const (
DataVersionUnspecified DataVersionState = iota
DataVersionCurrent
DataVersionNeedsRetry
)
// SkipReason explains provider-level intentional skips.
type SkipReason uint8
const (
SkipNone SkipReason = iota
SkipNoSession
SkipUnsupportedSource
SkipNonInteractive
SkipShadowedBySidecar
)
// IncrementalRequest is the append-only parse input.
type IncrementalRequest struct {
Source SourceRef
Fingerprint SourceFingerprint
SessionID string
Offset int64
StartOrdinal int
Machine string
}
// IncrementalOutcome is the append-only parse output.
type IncrementalOutcome struct {
SessionID string
Messages []ParsedMessage
EndedAt time.Time
ConsumedBytes int64
MessageCount int
UserMessageCount int
TotalOutputTokens int
PeakContextTokens int
HasTotalOutputTokens bool
HasPeakContextTokens bool
ForceReplace bool
}
// IncrementalStatus describes how an incremental parse attempt should proceed.
type IncrementalStatus uint8
const (
IncrementalUnsupported IncrementalStatus = iota
IncrementalNoNewData
IncrementalApplied
IncrementalNeedsFullParse
)
type legacyProviderFactory struct {
def AgentDef
}
func (f legacyProviderFactory) Definition() AgentDef {
return cloneAgentDef(f.def)
}
func (f legacyProviderFactory) Capabilities() Capabilities {
return Capabilities{}
}
func (f legacyProviderFactory) NewProvider(cfg ProviderConfig) Provider {
return &legacyProvider{
ProviderBase: ProviderBase{
Def: cloneAgentDef(f.def),
Config: cfg.Clone(),
},
}
}
type legacyProvider struct {
ProviderBase
}
func (p *legacyProvider) Parse(context.Context, ParseRequest) (ParseOutcome, error) {
return ParseOutcome{}, p.unsupported(ProviderFeatureParse)
}
// ProviderFactories returns one provider factory for every registered agent.
func ProviderFactories() []ProviderFactory {
factories := make([]ProviderFactory, 0, len(Registry))
for _, def := range Registry {
factories = append(factories, providerFactoryForDef(def))
}
return factories
}
func providerFactoryForDef(def AgentDef) ProviderFactory {
def = cloneAgentDef(def)
switch def.Type {
case AgentAmp:
return newAmpProviderFactory(def)
case AgentCommandCode:
return newCommandCodeProviderFactory(def)
case AgentCortex:
return newCortexProviderFactory(def)
case AgentDeepSeekTUI:
return newDeepSeekTUIProviderFactory(def)
case AgentIflow:
return newIflowProviderFactory(def)
case AgentGptme:
return newGptmeProviderFactory(def)
case AgentKimi:
return newKimiProviderFactory(def)
case AgentOpenClaw:
return newOpenClawProviderFactory(def)
case AgentOMP, AgentPi:
return newPiProviderFactory(def)
case AgentQClaw:
return newQClawProviderFactory(def)
case AgentQwen:
return newQwenProviderFactory(def)
case AgentQwenPaw:
return newQwenPawProviderFactory(def)
case AgentWorkBuddy:
return newWorkBuddyProviderFactory(def)
case AgentZencoder:
return newZencoderProviderFactory(def)
default:
return legacyProviderFactory{def: def}
}
}
// ProviderFactoryByType returns the factory for an agent type.
func ProviderFactoryByType(t AgentType) (ProviderFactory, bool) {
for _, factory := range ProviderFactories() {
if factory.Definition().Type == t {
return factory, true
}
}
return nil, false
}
// NewProvider constructs a config-bound provider for an agent type.
func NewProvider(t AgentType, cfg ProviderConfig) (Provider, bool) {
factory, ok := ProviderFactoryByType(t)
if !ok {
return nil, false
}
return factory.NewProvider(cfg), true
}
func cloneAgentDef(def AgentDef) AgentDef {
def.DefaultDirs = append([]string(nil), def.DefaultDirs...)
def.WatchSubdirs = append([]string(nil), def.WatchSubdirs...)
return def
}