f99010fae1
Desktop Artifacts / Desktop Build (Linux) (push) Waiting to run
Desktop Artifacts / Desktop Build (Windows) (push) Waiting to run
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Waiting to run
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
215 lines
6.0 KiB
Go
215 lines
6.0 KiB
Go
package parser
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
var _ Provider = (*commandCodeProvider)(nil)
|
|
|
|
type commandCodeProviderFactory struct {
|
|
def AgentDef
|
|
}
|
|
|
|
func newCommandCodeProviderFactory(def AgentDef) ProviderFactory {
|
|
return commandCodeProviderFactory{def: cloneAgentDef(def)}
|
|
}
|
|
|
|
func (f commandCodeProviderFactory) Definition() AgentDef {
|
|
return cloneAgentDef(f.def)
|
|
}
|
|
|
|
func (f commandCodeProviderFactory) Capabilities() Capabilities {
|
|
return commandCodeProviderCapabilities()
|
|
}
|
|
|
|
func (f commandCodeProviderFactory) NewProvider(cfg ProviderConfig) Provider {
|
|
cfg = cfg.Clone()
|
|
return &commandCodeProvider{
|
|
ProviderBase: ProviderBase{
|
|
Def: cloneAgentDef(f.def),
|
|
Caps: commandCodeProviderCapabilities(),
|
|
Config: cfg,
|
|
},
|
|
sources: newCommandCodeSourceSet(cfg.Roots),
|
|
}
|
|
}
|
|
|
|
type commandCodeProvider struct {
|
|
ProviderBase
|
|
sources DirectoryJSONLSourceSet
|
|
}
|
|
|
|
func (p *commandCodeProvider) Discover(ctx context.Context) ([]SourceRef, error) {
|
|
return p.sources.Discover(ctx)
|
|
}
|
|
|
|
func (p *commandCodeProvider) WatchPlan(ctx context.Context) (WatchPlan, error) {
|
|
return p.sources.WatchPlan(ctx)
|
|
}
|
|
|
|
func (p *commandCodeProvider) SourcesForChangedPath(
|
|
ctx context.Context,
|
|
req ChangedPathRequest,
|
|
) ([]SourceRef, error) {
|
|
return p.sources.SourcesForChangedPath(ctx, req)
|
|
}
|
|
|
|
func (p *commandCodeProvider) FindSource(
|
|
ctx context.Context,
|
|
req FindSourceRequest,
|
|
) (SourceRef, bool, error) {
|
|
return p.sources.FindSource(ctx, ProviderFindRequestWithRawSessionID(p.Def, req))
|
|
}
|
|
|
|
func (p *commandCodeProvider) Fingerprint(
|
|
ctx context.Context,
|
|
source SourceRef,
|
|
) (SourceFingerprint, error) {
|
|
return p.sources.Fingerprint(ctx, source)
|
|
}
|
|
|
|
func (p *commandCodeProvider) Parse(
|
|
ctx context.Context,
|
|
req ParseRequest,
|
|
) (ParseOutcome, error) {
|
|
if err := ctx.Err(); err != nil {
|
|
return ParseOutcome{}, err
|
|
}
|
|
path, ok, err := p.sources.pathFromSource(ctx, req.Source)
|
|
if err != nil {
|
|
return ParseOutcome{}, err
|
|
}
|
|
if !ok {
|
|
return ParseOutcome{}, fmt.Errorf("commandcode source path unavailable")
|
|
}
|
|
machine := firstNonEmptyJSONLString(req.Machine, p.Config.Machine)
|
|
sess, msgs, err := p.parseSession(path, machine)
|
|
if err != nil {
|
|
return ParseOutcome{}, err
|
|
}
|
|
if sess == nil {
|
|
return ParseOutcome{
|
|
ResultSetComplete: true,
|
|
SkipReason: SkipNoSession,
|
|
}, nil
|
|
}
|
|
// Use the shared fingerprint hash (which folds the .meta.json companion via
|
|
// WithCompanionFiles + WithContentHashing) rather than recomputing a bespoke
|
|
// transcript-only hash, so file_hash stays consistent with the fingerprint.
|
|
if req.Fingerprint.Hash != "" {
|
|
sess.File.Hash = req.Fingerprint.Hash
|
|
}
|
|
// Mirror the legacy effective-info behavior: the transcript's
|
|
// freshness identity (size and mtime) includes the .meta.json
|
|
// companion so a title-only rename triggers a reparse.
|
|
if size, mtime, ok := commandCodeEffectiveFileInfo(path); ok {
|
|
sess.File.Size = size
|
|
sess.File.Mtime = mtime
|
|
}
|
|
return ParseOutcome{
|
|
Results: []ParseResultOutcome{{
|
|
Result: ParseResult{
|
|
Session: *sess,
|
|
Messages: msgs,
|
|
},
|
|
DataVersion: DataVersionCurrent,
|
|
}},
|
|
ResultSetComplete: true,
|
|
}, nil
|
|
}
|
|
|
|
func newCommandCodeSourceSet(roots []string) DirectoryJSONLSourceSet {
|
|
return NewDirectoryJSONLSourceSet(AgentCommandCode, roots,
|
|
WithSymlinkFollowing(),
|
|
WithIncludePath(isCommandCodeSourcePath),
|
|
WithProjectHint(func(root, path string) string { return "" }),
|
|
WithSessionIDFromPath(commandCodeSessionIDFromPath),
|
|
// Command Code's .meta.json sidecar participates in source freshness, so
|
|
// fold it into the shared fingerprint (size, mtime, and content hash) via
|
|
// the framework companion hook instead of a bespoke Fingerprint.
|
|
// WithContentHashing preserves the legacy per-agent file_hash, which a
|
|
// resync would otherwise clear to NULL.
|
|
WithCompanionFiles(func(transcriptPath string) []string {
|
|
return []string{commandCodeMetaCompanionPath(transcriptPath)}
|
|
}),
|
|
WithContentHashing(),
|
|
)
|
|
}
|
|
|
|
func isCommandCodeSourcePath(root, path string) bool {
|
|
name := filepath.Base(path)
|
|
if !strings.HasSuffix(name, ".jsonl") ||
|
|
strings.HasSuffix(name, ".checkpoints.jsonl") ||
|
|
strings.HasSuffix(name, ".prompts.jsonl") {
|
|
return false
|
|
}
|
|
return IsValidSessionID(strings.TrimSuffix(name, ".jsonl"))
|
|
}
|
|
|
|
func commandCodeSessionIDFromPath(root, path string) string {
|
|
name := filepath.Base(path)
|
|
if !isCommandCodeSourcePath(root, path) {
|
|
return ""
|
|
}
|
|
return strings.TrimSuffix(name, ".jsonl")
|
|
}
|
|
|
|
func commandCodeMetaCompanionPath(path string) string {
|
|
return strings.TrimSuffix(path, ".jsonl") + ".meta.json"
|
|
}
|
|
|
|
// commandCodeEffectiveFileInfo returns the combined size and mtime of the
|
|
// transcript and its optional .meta.json companion. The bool is false only
|
|
// when the transcript itself cannot be stat'd.
|
|
func commandCodeEffectiveFileInfo(path string) (int64, int64, bool) {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return 0, 0, false
|
|
}
|
|
size := info.Size()
|
|
mtime := info.ModTime().UnixNano()
|
|
if metaInfo, ok, err := commandCodeCompanionInfo(
|
|
commandCodeMetaCompanionPath(path),
|
|
); err == nil && ok && metaInfo != nil {
|
|
size += metaInfo.Size()
|
|
if metaMtime := metaInfo.ModTime().UnixNano(); metaMtime > mtime {
|
|
mtime = metaMtime
|
|
}
|
|
}
|
|
return size, mtime, true
|
|
}
|
|
|
|
func commandCodeCompanionInfo(path string) (os.FileInfo, bool, error) {
|
|
info, err := os.Stat(path)
|
|
if os.IsNotExist(err) {
|
|
return nil, false, nil
|
|
}
|
|
if err != nil {
|
|
return nil, false, fmt.Errorf("stat %s: %w", path, err)
|
|
}
|
|
if info.IsDir() {
|
|
return nil, false, nil
|
|
}
|
|
return info, true, nil
|
|
}
|
|
|
|
func commandCodeProviderCapabilities() Capabilities {
|
|
return Capabilities{
|
|
Source: jsonlFileProviderSourceCapabilities(),
|
|
Content: ContentCapabilities{
|
|
FirstMessage: CapabilitySupported,
|
|
SessionName: CapabilitySupported,
|
|
Cwd: CapabilitySupported,
|
|
GitBranch: CapabilitySupported,
|
|
Thinking: CapabilitySupported,
|
|
ToolCalls: CapabilitySupported,
|
|
ToolResults: CapabilitySupported,
|
|
MalformedLineCount: CapabilitySupported,
|
|
},
|
|
}
|
|
}
|