package app import ( "agent-system-backend/internal/execenv" "agent-system-backend/internal/store" "agent-system-backend/internal/tools" core "agent-system-core" ) // envStore 把 store 的行类型适配成 execenv.Store。 type envStore struct{ *store.Store } func (s envStore) InsertExecEnvRequest(r execenv.Request) error { return s.Store.InsertExecEnvRequest(store.ExecEnvRequest{ ID: r.ID, ConversationID: r.ConversationID, Owner: r.Owner, Spec: r.Spec, Reason: r.Reason, Status: r.Status, LayerID: r.LayerID, Detail: r.Detail, Approver: r.Approver, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt, }) } func (s envStore) GetExecEnvRequest(id string) (execenv.Request, error) { r, err := s.Store.GetExecEnvRequest(id) if err != nil { return execenv.Request{}, err } return execenv.Request{ ID: r.ID, ConversationID: r.ConversationID, Owner: r.Owner, Spec: r.Spec, Reason: r.Reason, Status: r.Status, LayerID: r.LayerID, Detail: r.Detail, Approver: r.Approver, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt, }, nil } func (s envStore) ListExecEnvRequests(conversationID string, limit int) ([]execenv.Request, error) { rows, err := s.Store.ListExecEnvRequests(conversationID, limit) if err != nil { return nil, err } out := make([]execenv.Request, 0, len(rows)) for _, r := range rows { out = append(out, execenv.Request{ ID: r.ID, ConversationID: r.ConversationID, Owner: r.Owner, Spec: r.Spec, Reason: r.Reason, Status: r.Status, LayerID: r.LayerID, Detail: r.Detail, Approver: r.Approver, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt, }) } return out, nil } func (s envStore) UpdateExecEnvRequest(id, status, layerID, detail, approver string) error { return s.Store.UpdateExecEnvRequest(id, status, layerID, detail, approver) } func (s envStore) GetExecEnvBinding(conversationID string) (execenv.Binding, error) { b, err := s.Store.GetExecEnvBinding(conversationID) if err != nil { return execenv.Binding{}, err } return execenv.Binding{ ConversationID: b.ConversationID, BaseID: b.BaseID, Extensions: b.Layers, Isolated: b.Isolated, UpdatedAt: b.UpdatedAt, }, nil } func (s envStore) PutExecEnvBinding(b execenv.Binding) error { return s.Store.PutExecEnvBinding(store.ExecEnvBinding{ ConversationID: b.ConversationID, BaseID: b.BaseID, Layers: b.Extensions, Isolated: b.Isolated, UpdatedAt: b.UpdatedAt, }) } func (s envStore) ListExecEnvBindings() ([]execenv.Binding, error) { rows, err := s.Store.ListExecEnvBindings() if err != nil { return nil, err } out := make([]execenv.Binding, 0, len(rows)) for _, b := range rows { out = append(out, execenv.Binding{ ConversationID: b.ConversationID, BaseID: b.BaseID, Extensions: b.Layers, Isolated: b.Isolated, UpdatedAt: b.UpdatedAt, }) } return out, nil } func (s envStore) InsertExecEnvAudit(owner, action, actor, target, outcome, entry string) error { return s.Store.InsertExecEnvAudit(owner, action, actor, target, outcome, entry) } // wireExecEnv 打开环境根,把基础层交给执行器,并接上申请代理。 // 两者都空时不改变现状(解释器仍固定为 /usr/bin/python3,避免 linuxbrew 路径)。 func wireExecEnv(st *store.Store, opt *Options) (*execenv.Broker, error) { if opt.ExecEnvRoot == "" { return nil, nil } root, err := execenv.Open(opt.ExecEnvRoot) if err != nil { return nil, err } if bin, ok := root.PythonBin(); ok { tools.SetExecPython(bin, root.PythonPrefix()) } tools.SetExecShimDir(root.BinDir()) if opt.ExecDepsDir == "" { if id, err := root.CurrentBase(); err != nil { return nil, err } else if id != "" { opt.ExecDepsDir = root.LayerDir(id) } } py, _ := root.PythonBin() if py == "" { py = "/usr/bin/python3" } broker := execenv.NewBroker(root, envStore{st}, execenv.Pip{Python: py}) tools.SetEnvBroker(broker) tools.SetExecLayers(broker.LayersFor) tools.SetEnvApproval(func(owner, conv, requestID, detail string) error { card := &core.HITLCard{ ID: core.ID("hitl_" + requestID), RunID: "execenv", ConversationID: core.ID(conv), Question: "批准为这个会话安装额外的 Python 包?", Kind: core.HITLKindChoice, Detail: "exec-env:" + requestID + "\n" + detail, Choices: []core.HITLChoice{ {ID: "approve", Label: "批准", Description: detail, Impact: "只挂到这个会话,不改变基础层", Recommended: true}, {ID: "isolate", Label: "批准并使用隔离层", Description: "与基础层版本冲突时用这份独立闭包", Impact: "这个会话不再叠基础层"}, {ID: "reject", Label: "拒绝", Description: "不安装", Impact: "会话保持现状"}, }, } return st.SaveHITLCard(owner, card) }) return broker, nil }