1package agent
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "os"
8
9 "github.com/bnema/zut/packages/core"
10 "github.com/bnema/zut/packages/provider"
11)
12
13// openOrCreateSessionState distinguishes creation from restoration even when
14// the restored transcript is empty. Fresh runs may preload pinned skills.
15func openOrCreateSessionState(ctx context.Context, args Args, r Resolved, ag *core.Agent, version string) (*core.Session, bool, error) {
16 if args.NoSess {
17 ag.SessionID()
18 return nil, true, nil
19 }
20 root := agentSessionsRoot(ZutHome(), args)
21 // Explicit paths and UUIDs may identify empty sessions. Don't delete them
22 // before deciding whether this is a restoration.
23 if args.ResumeSessionID == "" && args.Session == "" {
24 core.PruneEmptySessions(root, args.CWD)
25 }
26 var (
27 sess *core.Session
28 messages []provider.Message
29 err error
30 fresh bool
31 )
32 switch {
33 case args.Session != "":
34 sess, messages, err = core.OpenSession(args.Session)
35 if errors.Is(err, os.ErrNotExist) {
36 sess, err = core.NewSessionAtPath(args.Session, args.CWD, r.Provider, r.Model, version)
37 messages, fresh = nil, true
38 }
39 case args.Continue:
40 if path := core.LatestSession(root, args.CWD); path != "" {
41 sess, messages, err = core.OpenSession(path)
42 }
43 case args.Resume:
44 var path string
45 if args.ResumeSessionID != "" {
46 path, err = core.FindManagedSessionByID(ctx, ZutHome(), args.ResumeSessionID)
47 if err != nil {
48 return nil, false, err
49 }
50 if path == "" {
51 return nil, false, fmt.Errorf("session %q not found", args.ResumeSessionID)
52 }
53 } else {
54 path, err = pickSession(root, args.CWD)
55 if err != nil {
56 return nil, false, err
57 }
58 }
59 if path != "" {
60 sess, messages, err = core.OpenSession(path)
61 if err == nil && args.ResumeSessionID != "" && sess.ID != args.ResumeSessionID {
62 return nil, false, errors.Join(fmt.Errorf("session %q changed during resume", args.ResumeSessionID), sess.Close())
63 }
64 }
65 }
66 if err != nil {
67 return nil, false, err
68 }
69 if sess == nil {
70 sess, err = core.NewSession(root, args.CWD, r.Provider, r.Model, version)
71 if err != nil {
72 return nil, false, err
73 }
74 fresh = true
75 }
76 if err := ag.BindSessionID(sess.Meta.ID); err != nil {
77 return nil, false, errors.Join(err, sess.Close())
78 }
79 ag.SetSessionTimeContext(sess.Meta.Started, sess.Meta.Timezone, sess.Meta.TimezoneOffset)
80 if !fresh {
81 ag.SetMessages(messages)
82 if cumulative, last, err := core.SessionUsageDetail(sess.Path); err == nil {
83 ag.SeedCost(cumulative)
84 ag.SeedLastTurnUsage(last)
85 }
86 }
87 return sess, fresh, nil
88}