Files
wehub-resource-sync f99010fae1
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
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

862 lines
24 KiB
Go

package parser
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/fs"
"log"
"os"
"path/filepath"
"strings"
)
var _ Provider = (*openCodeFormatProvider)(nil)
// A SQLite WAL begins with a 32-byte header. Bytes beyond the header are
// transaction frames, so a larger WAL can contain source changes worth
// syncing. Read-only SQLite connections may create an empty WAL and its SHM
// index simply by opening a quiet WAL-mode database; those sidecars must not
// make the watcher trigger itself.
const sqliteWALHeaderSize = int64(32)
type openCodeFormatProviderFactory struct {
def AgentDef
spec openCodeProviderSpec
}
func newOpenCodeProviderFactory(def AgentDef) ProviderFactory {
return openCodeFormatProviderFactory{
def: cloneAgentDef(def),
spec: openCodeProviderSpecForAgent(AgentOpenCode),
}
}
func newKiloProviderFactory(def AgentDef) ProviderFactory {
return openCodeFormatProviderFactory{
def: cloneAgentDef(def),
spec: openCodeProviderSpecForAgent(AgentKilo),
}
}
func newMiMoCodeProviderFactory(def AgentDef) ProviderFactory {
return openCodeFormatProviderFactory{
def: cloneAgentDef(def),
spec: openCodeProviderSpecForAgent(AgentMiMoCode),
}
}
func newIcodemateProviderFactory(def AgentDef) ProviderFactory {
return openCodeFormatProviderFactory{
def: cloneAgentDef(def),
spec: openCodeProviderSpecForAgent(AgentIcodemate),
}
}
func (f openCodeFormatProviderFactory) Definition() AgentDef {
return cloneAgentDef(f.def)
}
func (f openCodeFormatProviderFactory) Capabilities() Capabilities {
return openCodeFormatProviderCapabilities()
}
func (f openCodeFormatProviderFactory) NewProvider(cfg ProviderConfig) Provider {
cfg = cfg.Clone()
return &openCodeFormatProvider{
ProviderBase: ProviderBase{
Def: cloneAgentDef(f.def),
Caps: openCodeFormatProviderCapabilities(),
Config: cfg,
},
sources: newOpenCodeFormatSourceSet(cfg.Roots, f.spec),
}
}
type openCodeFormatProvider struct {
ProviderBase
sources openCodeFormatSourceSet
}
func (p *openCodeFormatProvider) Discover(ctx context.Context) ([]SourceRef, error) {
return p.sources.Discover(ctx)
}
func (p *openCodeFormatProvider) WatchPlan(ctx context.Context) (WatchPlan, error) {
return p.sources.WatchPlan(ctx)
}
func (p *openCodeFormatProvider) SourcesForChangedPath(
ctx context.Context,
req ChangedPathRequest,
) ([]SourceRef, error) {
return p.sources.SourcesForChangedPath(ctx, req)
}
func (p *openCodeFormatProvider) FindSource(
ctx context.Context,
req FindSourceRequest,
) (SourceRef, bool, error) {
req = ProviderFindRequestWithRawSessionID(p.Def, req)
return p.sources.FindSource(ctx, req)
}
func (p *openCodeFormatProvider) Fingerprint(
ctx context.Context,
source SourceRef,
) (SourceFingerprint, error) {
return p.sources.Fingerprint(ctx, source)
}
func (p *openCodeFormatProvider) Parse(
ctx context.Context,
req ParseRequest,
) (ParseOutcome, error) {
if err := ctx.Err(); err != nil {
return ParseOutcome{}, err
}
path, ok := p.sources.pathFromSource(req.Source)
if !ok {
return ParseOutcome{}, fmt.Errorf("%s source path unavailable", p.Def.Type)
}
machine := firstNonEmptyJSONLString(req.Machine, p.Config.Machine)
var (
sess *ParsedSession
msgs []ParsedMessage
err error
)
if dbPath, sessionID, ok := p.sources.spec.parseVirtual(path); ok {
sess, msgs, err = p.sources.spec.parseSQLite(dbPath, sessionID, machine)
} else {
sess, msgs, err = p.sources.spec.parseFile(path, machine)
}
if err != nil {
return ParseOutcome{}, err
}
if sess == nil {
return ParseOutcome{
ResultSetComplete: true,
SkipReason: SkipNoSession,
}, nil
}
if req.Fingerprint.Hash != "" {
sess.File.Hash = req.Fingerprint.Hash
}
return ParseOutcome{
Results: []ParseResultOutcome{{
Result: ParseResult{
Session: *sess,
Messages: msgs,
},
DataVersion: DataVersionCurrent,
}},
ResultSetComplete: true,
}, nil
}
// openCodeProviderSpec parameterizes the one shared OpenCode-format
// provider implementation for OpenCode and its Kilo and MiMoCode forks.
// All three reuse the same discovery, source-lookup, fingerprinting,
// and parsing code; they differ only in the per-agent SQLite filename,
// the storage/<sessionSubdir> that holds session JSON, and the agent
// label/ID prefix applied via relabel. Kilo and MiMoCode parse through
// the OpenCode storage and SQLite readers, then relabel the result onto
// their own agent and ID prefix.
type openCodeProviderSpec struct {
agent AgentType
format openCodeFormat
dbName string
listSQLite func(string) ([]OpenCodeSessionMeta, error)
sourceMtime func(string) (int64, error)
relabel func(*ParsedSession)
}
func openCodeProviderSpecForAgent(agent AgentType) openCodeProviderSpec {
switch agent {
case AgentOpenCode:
return openCodeProviderSpec{
agent: AgentOpenCode,
format: openCodeFmt,
dbName: openCodeFmt.dbName,
listSQLite: ListOpenCodeSessionMeta,
sourceMtime: OpenCodeSourceMtime,
}
case AgentKilo:
return openCodeProviderSpec{
agent: AgentKilo,
format: kiloFmt,
dbName: kiloFmt.dbName,
listSQLite: ListKiloSessionMeta,
sourceMtime: KiloSourceMtime,
relabel: relabelOpenCodeSessionAsKilo,
}
case AgentMiMoCode:
return openCodeProviderSpec{
agent: AgentMiMoCode,
format: mimoFmt,
dbName: mimoFmt.dbName,
listSQLite: ListMiMoCodeSessionMeta,
sourceMtime: MiMoCodeSourceMtime,
relabel: relabelOpenCodeSessionAsMiMoCode,
}
case AgentIcodemate:
return openCodeProviderSpec{
agent: AgentIcodemate,
format: icodemateFmt,
dbName: icodemateFmt.dbName,
listSQLite: ListIcodemateSessionMeta,
sourceMtime: IcodemateSourceMtime,
relabel: relabelOpenCodeSessionAsIcodemate,
}
default:
return openCodeProviderSpec{}
}
}
// resolve detects the OpenCode storage backend for a root.
func (spec openCodeProviderSpec) resolve(root string) OpenCodeSource {
return resolveOpenCodeFormatSource(spec.format, root)
}
// discover lists file-backed storage session JSON files under a root.
func (spec openCodeProviderSpec) discover(root string) []DiscoveredFile {
return discoverOpenCodeFormatSessions(spec.format, root)
}
// find locates a session source path (storage JSON or SQLite virtual
// path) by raw session ID under a root.
func (spec openCodeProviderSpec) find(root, sessionID string) string {
return findOpenCodeFormatSourceFile(spec.format, root, sessionID)
}
// watchRoots returns the directories that should be watched for live
// updates under a configured root.
func (spec openCodeProviderSpec) watchRoots(root string) []string {
return resolveOpenCodeFormatWatchRoots(spec.format, root)
}
// storageIDs returns the set of session IDs present as storage JSON
// under a root, used to skip duplicate SQLite metas in hybrid roots.
func (spec openCodeProviderSpec) storageIDs(root string) map[string]struct{} {
return openCodeFormatStorageSessionIDs(spec.format, root)
}
// parseVirtual splits an opencode-format SQLite virtual path
// (<dbPath>#<sessionID>) when the DB base name matches this agent.
func (spec openCodeProviderSpec) parseVirtual(
sourcePath string,
) (dbPath, sessionID string, ok bool) {
return parseOpenCodeFormatVirtualPath(spec.dbName, sourcePath)
}
// parseFile parses a file-backed storage session and relabels it onto
// this agent's ID prefix when the agent is a fork of OpenCode.
func (spec openCodeProviderSpec) parseFile(
sessionPath, machine string,
) (*ParsedSession, []ParsedMessage, error) {
sess, msgs, err := parseOpenCodeStorageFile(sessionPath, machine)
if err != nil || sess == nil {
return sess, msgs, err
}
if spec.relabel != nil {
spec.relabel(sess)
}
return sess, msgs, nil
}
// parseSQLite parses a single SQLite-backed session and relabels it
// onto this agent's ID prefix when the agent is a fork of OpenCode.
func (spec openCodeProviderSpec) parseSQLite(
dbPath, sessionID, machine string,
) (*ParsedSession, []ParsedMessage, error) {
sess, msgs, err := parseOpenCodeDBSession(dbPath, sessionID, machine)
if err != nil || sess == nil {
return sess, msgs, err
}
if spec.relabel != nil {
spec.relabel(sess)
}
return sess, msgs, nil
}
type openCodeFormatSource struct {
Root string
Path string
// MTimeNS carries the session's time_updated (already listed during
// SQLite discovery, scaled to nanoseconds) so Fingerprint does not
// reopen the shared DB once per session. Zero means unknown and makes
// Fingerprint fall back to querying the DB.
MTimeNS int64
}
type openCodeFormatSourceSet struct {
roots []string
spec openCodeProviderSpec
}
func newOpenCodeFormatSourceSet(
roots []string,
spec openCodeProviderSpec,
) openCodeFormatSourceSet {
return openCodeFormatSourceSet{
roots: cleanJSONLRoots(roots),
spec: spec,
}
}
func (s openCodeFormatSourceSet) Discover(ctx context.Context) ([]SourceRef, error) {
var sources []SourceRef
seen := make(map[string]struct{})
for _, root := range s.roots {
if err := ctx.Err(); err != nil {
return nil, err
}
src := s.spec.resolve(root)
storageIDs := map[string]struct{}{}
if src.Mode == OpenCodeSourceStorage {
for _, file := range s.spec.discover(root) {
source, ok := s.sourceRef(root, file.Path, false)
if !ok {
continue
}
source.ProjectHint = file.Project
addJSONLSource(source, &sources, seen)
}
storageIDs = s.spec.storageIDs(root)
}
if src.DBPath == "" || !IsRegularFile(src.DBPath) {
continue
}
dbSources, err := s.sqliteSources(ctx, root, src.DBPath, storageIDs)
if err != nil {
if ctx.Err() != nil {
return nil, err
}
// The SQLite DB is optional alongside filesystem storage. A
// corrupt or unreadable DB must not abort discovery of the valid
// storage-backed sessions in this root; scope the failure to the DB
// portion, matching the legacy independent discovery paths. A
// SQLite-only root has nothing to fall back to, so keep failing.
if src.Mode == OpenCodeSourceStorage {
log.Printf("sync %s: skipping unreadable %s: %v",
s.spec.agent, src.DBPath, err)
continue
}
return nil, err
}
for _, source := range dbSources {
addJSONLSource(source, &sources, seen)
}
}
sortJSONLSources(sources)
return sources, nil
}
func (s openCodeFormatSourceSet) WatchPlan(context.Context) (WatchPlan, error) {
roots := make([]WatchRoot, 0, len(s.roots))
for _, root := range s.roots {
for _, watchRoot := range s.spec.watchRoots(root) {
roots = append(roots, WatchRoot{
Path: watchRoot,
Recursive: true,
IncludeGlobs: []string{
"*.json",
s.spec.dbName,
s.spec.dbName + "-wal",
},
DebounceKey: string(s.spec.agent) + ":opencode:" + watchRoot,
})
}
}
return WatchPlan{Roots: roots}, nil
}
func (s openCodeFormatSourceSet) SourcesForChangedPath(
ctx context.Context,
req ChangedPathRequest,
) ([]SourceRef, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
pathExists := true
if _, err := os.Stat(req.Path); err != nil {
if !os.IsNotExist(err) {
return nil, nil
}
pathExists = false
}
for _, root := range s.roots {
sources, ok, err := s.sourcesForChangedPathInRoot(
ctx, root, req.Path, pathExists,
)
if err != nil || ok {
return sources, err
}
}
return nil, nil
}
func (s openCodeFormatSourceSet) FindSource(
ctx context.Context,
req FindSourceRequest,
) (SourceRef, bool, error) {
if err := ctx.Err(); err != nil {
return SourceRef{}, false, err
}
for _, path := range []string{req.StoredFilePath, req.FingerprintKey} {
if path == "" {
continue
}
for _, root := range s.roots {
if source, ok := s.sourceRef(root, path, true); ok {
return source, true, nil
}
}
}
if req.RawSessionID == "" {
return SourceRef{}, false, nil
}
for _, root := range s.roots {
path := s.spec.find(root, req.RawSessionID)
if path == "" {
continue
}
if source, ok := s.sourceRef(root, path, false); ok {
return source, true, nil
}
}
return SourceRef{}, false, nil
}
func (s openCodeFormatSourceSet) Fingerprint(
ctx context.Context,
source SourceRef,
) (SourceFingerprint, error) {
if err := ctx.Err(); err != nil {
return SourceFingerprint{}, err
}
path, ok := s.pathFromSource(source)
if !ok {
return SourceFingerprint{}, fmt.Errorf("%s source path unavailable", s.spec.agent)
}
mtime := sourceCarriedMTimeNS(source)
if mtime == 0 {
var err error
mtime, err = s.spec.sourceMtime(path)
if err != nil {
return SourceFingerprint{}, err
}
}
fingerprint := SourceFingerprint{
Key: firstNonEmptyJSONLString(source.FingerprintKey, source.Key, path),
MTimeNS: mtime,
}
if dbPath, _, ok := s.spec.parseVirtual(path); ok {
info, err := os.Stat(dbPath)
if err != nil {
return SourceFingerprint{}, fmt.Errorf("stat %s: %w", dbPath, err)
}
fingerprint.Size = info.Size()
return fingerprint, nil
}
info, err := os.Stat(path)
if err != nil {
return SourceFingerprint{}, fmt.Errorf("stat %s: %w", path, err)
}
if info.IsDir() {
return SourceFingerprint{}, fmt.Errorf("stat %s: source is a directory", path)
}
fingerprint.Size = info.Size()
// No content hash: no engine freshness gate consumes it for this
// family (see providerFingerprintHashRequiredForFreshness), and the
// authoritative storage fingerprint is computed by Parse and compared
// post-parse by dropUnchangedSharedSQLiteResults. Computing it here
// re-read and re-hashed every message and part file of the session on
// every fingerprint call — the dominant cost of syncing streaming
// storage sessions — for a value nothing read.
return fingerprint, nil
}
// sourceCarriedMTimeNS returns the discovery-listed session mtime carried on
// a SQLite-backed source, or zero when the source was built without one
// (storage sessions, FindSource lookups).
func sourceCarriedMTimeNS(source SourceRef) int64 {
switch src := source.Opaque.(type) {
case openCodeFormatSource:
return src.MTimeNS
case *openCodeFormatSource:
if src != nil {
return src.MTimeNS
}
}
return 0
}
func (s openCodeFormatSourceSet) pathFromSource(source SourceRef) (string, bool) {
switch src := source.Opaque.(type) {
case openCodeFormatSource:
return src.Path, src.Path != ""
case *openCodeFormatSource:
if src != nil && src.Path != "" {
return src.Path, true
}
}
for _, candidate := range []string{
source.DisplayPath,
source.FingerprintKey,
source.Key,
} {
for _, root := range s.roots {
if ref, ok := s.sourceRef(root, candidate, false); ok {
src := ref.Opaque.(openCodeFormatSource)
return src.Path, true
}
}
}
return "", false
}
func (s openCodeFormatSourceSet) sqliteSources(
ctx context.Context,
root string,
dbPath string,
storageIDs map[string]struct{},
) ([]SourceRef, error) {
if err := ctx.Err(); err != nil {
return nil, err
}
metas, err := s.spec.listSQLite(dbPath)
if err != nil {
return nil, err
}
sources := make([]SourceRef, 0, len(metas))
for _, meta := range metas {
if _, exists := storageIDs[meta.SessionID]; exists {
continue
}
// meta was just read from this DB, so the session row is known to
// exist. Build the SourceRef directly instead of routing through
// sourceRef, which reopens the same SQLite DB once per row via
// OpenCodeSQLiteSessionExists (O(n) opens for n sessions, and it would
// silently drop a row whose redundant probe failed).
source, ok := s.sqliteSourceRefFromMeta(root, meta)
if !ok {
continue
}
sources = append(sources, source)
}
return sources, nil
}
// sqliteSourceRefFromMeta builds a SourceRef for a session row already listed
// from the SQLite DB at root. It validates the virtual path parses and that its
// DB lives under root, but unlike sourceRef it skips the per-row
// OpenCodeSQLiteSessionExists probe because the caller read the row from that
// same DB moments earlier. The listed time_updated rides along so Fingerprint
// does not reopen the DB per session.
func (s openCodeFormatSourceSet) sqliteSourceRefFromMeta(
root string,
meta OpenCodeSessionMeta,
) (SourceRef, bool) {
root = filepath.Clean(root)
path := filepath.Clean(meta.VirtualPath)
dbPath, _, ok := s.spec.parseVirtual(path)
if !ok {
return SourceRef{}, false
}
if _, under := relUnder(root, dbPath); !under {
return SourceRef{}, false
}
ref := s.newSourceRef(root, path, "")
if src, ok := ref.Opaque.(openCodeFormatSource); ok {
src.MTimeNS = meta.FileMtime
ref.Opaque = src
}
return ref, true
}
func (s openCodeFormatSourceSet) sourcesForChangedPathInRoot(
ctx context.Context,
root string,
path string,
pathExists bool,
) ([]SourceRef, bool, error) {
rel, ok := relUnder(root, path)
if !ok {
return nil, false, nil
}
isSQLiteChange := true
switch rel {
case s.spec.dbName + "-shm":
// SHM is only SQLite's WAL index. WAL frames (or the checkpointed main
// database) carry the source changes, so SHM events are redundant.
return nil, true, nil
case s.spec.dbName + "-wal":
// A read-only connection can create an empty WAL while inspecting a
// quiet database. Ignore it, as well as WAL removal after a checkpoint;
// the corresponding main-database write is watched separately.
if !sqliteWALHasFrames(path) {
return nil, true, nil
}
case s.spec.dbName:
// The main database and WALs with transaction frames both fan out to
// the logical sessions stored in this shared SQLite container.
default:
isSQLiteChange = false
}
if isSQLiteChange {
dbPath := filepath.Join(root, s.spec.dbName)
if !IsRegularFile(dbPath) {
return nil, true, nil
}
storageIDs := map[string]struct{}{}
if s.spec.resolve(root).Mode == OpenCodeSourceStorage {
storageIDs = s.spec.storageIDs(root)
}
sources, err := s.sqliteSources(ctx, root, dbPath, storageIDs)
return sources, true, err
}
src := s.spec.resolve(root)
if src.Mode != OpenCodeSourceStorage {
return nil, false, nil
}
parts := strings.Split(rel, string(filepath.Separator))
sessionSubdir := filepath.Base(src.SessionRoot)
switch {
case pathExists &&
len(parts) == 4 &&
parts[0] == "storage" &&
parts[1] == sessionSubdir &&
strings.HasSuffix(parts[3], ".json"):
source, ok := s.sourceRef(root, path, false)
if !ok {
return nil, true, nil
}
return []SourceRef{source}, true, nil
case !pathExists &&
len(parts) == 4 &&
parts[0] == "storage" &&
parts[1] == sessionSubdir &&
strings.HasSuffix(parts[3], ".json"):
source, ok := s.sourceRefFromStoragePath(root, path)
if !ok {
return nil, true, nil
}
return []SourceRef{source}, true, nil
case len(parts) == 4 &&
parts[0] == "storage" &&
parts[1] == "message" &&
strings.HasSuffix(parts[3], ".json"):
source, ok := s.sourceForRawID(root, parts[2])
if !ok {
return nil, false, nil
}
return []SourceRef{source}, true, nil
case len(parts) == 4 &&
parts[0] == "storage" &&
parts[1] == "part" &&
strings.HasSuffix(parts[3], ".json"):
sessionID := ""
if pathExists {
sessionID = readOpenCodeProviderStorageSessionID(path)
}
if sessionID == "" {
sessionID = findOpenCodeProviderStorageSessionIDByMessageID(root, parts[2])
}
if sessionID == "" {
return nil, false, nil
}
source, ok := s.sourceForRawID(root, sessionID)
if !ok {
return nil, false, nil
}
return []SourceRef{source}, true, nil
case !pathExists &&
len(parts) == 3 &&
parts[0] == "storage" &&
parts[1] == "message":
source, ok := s.sourceForRawID(root, parts[2])
if !ok {
return nil, false, nil
}
return []SourceRef{source}, true, nil
case !pathExists &&
len(parts) == 3 &&
parts[0] == "storage" &&
parts[1] == "part":
sessionID := findOpenCodeProviderStorageSessionIDByMessageID(root, parts[2])
if sessionID == "" {
return nil, false, nil
}
source, ok := s.sourceForRawID(root, sessionID)
if !ok {
return nil, false, nil
}
return []SourceRef{source}, true, nil
}
return nil, false, nil
}
func sqliteWALHasFrames(path string) bool {
info, err := os.Stat(path)
if err != nil {
// Only a missing WAL is a definitive no-op. Other stat failures fail
// open so a real update is synced instead of silently dropped; at
// worst that costs one redundant sync.
return !errors.Is(err, fs.ErrNotExist)
}
if info == nil {
return false
}
return info.Mode().IsRegular() && info.Size() > sqliteWALHeaderSize
}
func (s openCodeFormatSourceSet) sourceForRawID(root, sessionID string) (SourceRef, bool) {
path := s.spec.find(root, sessionID)
if path == "" {
return SourceRef{}, false
}
return s.sourceRef(root, path, false)
}
func (s openCodeFormatSourceSet) sourceRef(
root string,
path string,
promoteVirtual bool,
) (SourceRef, bool) {
root = filepath.Clean(root)
path = filepath.Clean(path)
if dbPath, sessionID, ok := s.spec.parseVirtual(path); ok {
if _, under := relUnder(root, dbPath); !under {
return SourceRef{}, false
}
if promoteVirtual {
if selected := s.spec.find(root, sessionID); selected != "" &&
selected != path {
return s.sourceRef(root, selected, false)
}
}
if !OpenCodeSQLiteSessionExists(dbPath, sessionID) {
return SourceRef{}, false
}
return s.newSourceRef(root, path, ""), true
}
if !s.isStorageSessionPath(root, path, true) {
return SourceRef{}, false
}
return s.sourceRefFromStoragePath(root, path)
}
func (s openCodeFormatSourceSet) sourceRefFromStoragePath(
root string,
path string,
) (SourceRef, bool) {
if !s.isStorageSessionPath(root, path, false) {
return SourceRef{}, false
}
return s.newSourceRef(root, path, openCodeSessionProject(path)), true
}
func (s openCodeFormatSourceSet) newSourceRef(
root string,
path string,
project string,
) SourceRef {
return SourceRef{
Provider: s.spec.agent,
Key: path,
DisplayPath: path,
FingerprintKey: path,
ProjectHint: project,
Opaque: openCodeFormatSource{
Root: root,
Path: path,
},
}
}
func (s openCodeFormatSourceSet) isStorageSessionPath(
root string,
path string,
requireExisting bool,
) bool {
rel, ok := relUnder(root, path)
if !ok {
return false
}
src := s.spec.resolve(root)
if src.Mode != OpenCodeSourceStorage {
return false
}
parts := strings.Split(rel, string(filepath.Separator))
return len(parts) == 4 &&
parts[0] == "storage" &&
parts[1] == filepath.Base(src.SessionRoot) &&
strings.HasSuffix(parts[3], ".json") &&
(!requireExisting || IsRegularFile(path))
}
func readOpenCodeProviderStorageSessionID(path string) string {
raw, err := os.ReadFile(path)
if err != nil {
return ""
}
var data struct {
SessionID string `json:"sessionID"`
}
if err := json.Unmarshal(raw, &data); err != nil {
return ""
}
return data.SessionID
}
func findOpenCodeProviderStorageSessionIDByMessageID(
openCodeDir, messageID string,
) string {
messageRoot := filepath.Join(openCodeDir, "storage", "message")
entries, err := os.ReadDir(messageRoot)
if err != nil {
return ""
}
for _, entry := range entries {
if !entry.IsDir() {
continue
}
path := filepath.Join(messageRoot, entry.Name(), messageID+".json")
if info, err := os.Stat(path); err == nil && !info.IsDir() {
return entry.Name()
}
}
return ""
}
func openCodeFormatProviderCapabilities() Capabilities {
return Capabilities{
Source: SourceCapabilities{
DiscoverSources: CapabilitySupported,
WatchSources: CapabilitySupported,
ClassifyChangedPath: CapabilitySupported,
FindSource: CapabilitySupported,
CompositeFingerprint: CapabilitySupported,
IncrementalAppend: CapabilityNotApplicable,
MultiSessionSource: CapabilityNotApplicable,
PerSessionErrors: CapabilityNotApplicable,
ExcludedSessions: CapabilityNotApplicable,
ForceReplaceOnParse: CapabilityNotApplicable,
},
Content: ContentCapabilities{
FirstMessage: CapabilitySupported,
Cwd: CapabilitySupported,
Relationships: CapabilitySupported,
Thinking: CapabilitySupported,
ToolCalls: CapabilitySupported,
PerMessageTokenUsage: CapabilitySupported,
Model: CapabilitySupported,
},
}
}