返回 DeepSeek-Reasonix
identity_switch.go
根目录 / internal / serve / identity_switch.go
1 package serve
2
3 import (
4 "context"
5 "log/slog"
6 "reasonix/internal/control"
7 "reasonix/internal/session"
8 )
9
10 // sessionAPIRef reads the immutable v3 identity off any session API without
11 // requiring the concrete controller type.
12 func sessionAPIRef(ctrl control.SessionAPI) (session.SessionRef, bool) {
13 identity, ok := ctrl.(interface {
14 SessionRef() (session.SessionRef, bool)
15 })
16 if !ok {
17 return session.SessionRef{}, false
18 }
19 return identity.SessionRef()
20 }
21
22 // busySwitchIdentity backgrounds a busy exclusive-session controller and
23 // brings the target identity session to the foreground — the identity-route
24 // counterpart of busyDetach. The replacement controller opens the target
25 // through the shared session service; the detached registry (identity-keyed)
26 // keeps the running turn observable until it is re-attached. It fails with
27 // errIdentityServiceUnavailable when this host cannot build an
28 // identity-capable replacement; the caller then keeps the historical refusal.
29 func (s *Server) busySwitchIdentity(ctx context.Context, cur *control.Controller, ref session.SessionRef) error {
30 if s.tagFor(cur) == nil {
31 return errSessionTagUnavailable
32 }
33 next, tag, err := s.buildTaggedMode(ctx, currentModelRef(cur), false, false)
34 if err != nil {
35 return err
36 }
37 if next.SessionService() == nil {
38 s.closeTaggedController(next)
39 return errIdentityServiceUnavailable
40 }
41 if _, err := next.OpenSession(ctx, ref); err != nil {
42 s.closeTaggedController(next)
43 return err
44 }
45 if bound, ok := next.SessionRef(); ok {
46 tag.PrimeIdentity("", bound.SessionID)
47 }
48 next.EnableInteractiveApproval()
49 next.SetOnSessionRecovered(s.sessionRecoveryHandler(next, s.leases))
50 if !s.publishControllerSwap(cur, next, "") {
51 s.closeTaggedController(next)
52 return errReplacedDuringBind
53 }
54 var demoted *control.SessionLeaseKeeper
55 if s.leases != nil {
56 demoted = s.leases.Split()
57 }
58 if _, err := s.registerDetached(cur, demoted, nil); err != nil {
59 // bindMu prevents another foreground swap here. Roll publication back so
60 // a registry failure cannot strand a running controller.
61 _ = s.publishControllerSwap(next, cur, cur.SessionPath())
62 s.closeTaggedController(next)
63 if demoted != nil {
64 s.leases.Adopt(demoted)
65 }
66 return err
67 }
68 tag.Activate()
69 slog.Info("serve: busy identity session detached", "session", ref.SessionID)
70 return nil
71 }
72
72 lines GO