CreateSession creates a new session from a template.
(ctx context.Context, sessionTemplate *session.Session)
| 332 | |
| 333 | // CreateSession creates a new session from a template. |
| 334 | func (sm *SessionManager) CreateSession(ctx context.Context, sessionTemplate *session.Session) (*session.Session, error) { |
| 335 | var opts []session.Opt |
| 336 | opts = append(opts, |
| 337 | session.WithMaxIterations(sessionTemplate.MaxIterations), |
| 338 | session.WithMaxConsecutiveToolCalls(sessionTemplate.MaxConsecutiveToolCalls), |
| 339 | session.WithMaxOldToolCallTokens(sessionTemplate.MaxOldToolCallTokens), |
| 340 | session.WithToolsApproved(sessionTemplate.ToolsApproved), |
| 341 | ) |
| 342 | |
| 343 | // Carry a caller-supplied title (from the POST /api/sessions request body) |
| 344 | // into the new session. When set, RunSession's needsTitle check skips the |
| 345 | // LLM title-generation call and re-emits this title instead. |
| 346 | if title := strings.TrimSpace(sessionTemplate.Title); title != "" { |
| 347 | opts = append(opts, session.WithTitle(title)) |
| 348 | } |
| 349 | |
| 350 | if wd := strings.TrimSpace(sessionTemplate.WorkingDir); wd != "" { |
| 351 | absWd, err := filepath.Abs(wd) |
| 352 | if err != nil { |
| 353 | return nil, err |
| 354 | } |
| 355 | info, err := os.Stat(absWd) |
| 356 | if err != nil { |
| 357 | return nil, err |
| 358 | } |
| 359 | if !info.IsDir() { |
| 360 | return nil, errors.New("working directory must be a directory") |
| 361 | } |
| 362 | opts = append(opts, session.WithWorkingDir(absWd)) |
| 363 | } |
| 364 | |
| 365 | if sessionTemplate.Permissions != nil { |
| 366 | opts = append(opts, session.WithPermissions(sessionTemplate.Permissions)) |
| 367 | } |
| 368 | |
| 369 | sess := session.New(opts...) |
| 370 | |
| 371 | // Copy model-related fields from the template so callers can pin a |
| 372 | // specific model when creating a session over the API. The runtime |
| 373 | // will pick these up the first time it is built for the session |
| 374 | // (see runtimeForSession). Callers that want a model to also appear |
| 375 | // in the picker history should include it in CustomModelsUsed. |
| 376 | if len(sessionTemplate.AgentModelOverrides) > 0 { |
| 377 | sess.AgentModelOverrides = maps.Clone(sessionTemplate.AgentModelOverrides) |
| 378 | } |
| 379 | if len(sessionTemplate.CustomModelsUsed) > 0 { |
| 380 | sess.CustomModelsUsed = append([]string(nil), sessionTemplate.CustomModelsUsed...) |
| 381 | } |
| 382 | |
| 383 | return sess, sm.sessionStore.AddSession(ctx, sess) |
| 384 | } |
| 385 | |
| 386 | // Sentinel errors returned by ForkSession. Matched via errors.Is by |
| 387 | // the HTTP handler to classify failures as 400 vs 500, so the messages |