diff --git a/cmd/gc/cmd_session.go b/cmd/gc/cmd_session.go index 7779c2239f..9ca69821d8 100644 --- a/cmd/gc/cmd_session.go +++ b/cmd/gc/cmd_session.go @@ -1116,11 +1116,10 @@ func cmdSessionAttach(args []string, stdout, stderr io.Writer) int { // // stderr receives projection errors (use io.Discard to ignore). // -// sessionKind mirrors the real_world_app_session_kind bead metadata: "provider" means -// the session was created from a bare provider name (not an agent template), -// so the agent-template lookup should be skipped. This matches the guard in -// the API handler (handler_session_chat.go). -func buildResumeCommand(cityPath string, cfg *config.City, info session.Info, sessionKind string, stderr io.Writer) (string, runtime.Config) { +// sessionKind is the persisted session kind when available. A provider session +// was created from a bare provider name, so agent-template lookup must be +// skipped to avoid agent/provider name collisions. +func buildResumeCommand(cityPath string, cfg *config.City, info session.Info, sessionKind string, metadata map[string]string, stderr io.Writer) (string, runtime.Config) { cmd := session.BuildResumeCommand(info) if cfg == nil { return cmd, runtime.Config{WorkDir: info.WorkDir} @@ -1134,8 +1133,25 @@ func buildResumeCommand(cityPath string, cfg *config.City, info session.Info, se // Build command with default args and settings, matching the // reconciler's template_resolve.go command construction. command := resolved.CommandString() - if defaultArgs := resolved.ResolveDefaultArgs(); len(defaultArgs) > 0 { - command = command + " " + shellquote.Join(defaultArgs) + resumeCommand := resolved.ResumeCommand + appendDefaultArgs := func() { + if defaultArgs := resolved.ResolveDefaultArgs(); len(defaultArgs) > 0 { + command = command + " " + shellquote.Join(defaultArgs) + } + } + if overrides, err := session.ParseTemplateOverrides(metadata); err == nil { + transport := strings.TrimSpace(info.Transport) + launchCommand, err := config.BuildProviderLaunchCommand(cityPath, resolved, overrides, transport) + if err == nil && strings.TrimSpace(launchCommand.Command) != "" { + command = launchCommand.Command + } else { + appendDefaultArgs() + } + if command, err := config.BuildProviderResumeCommand(resolved, overrides); err == nil && strings.TrimSpace(command) != "" { + resumeCommand = command + } + } else { + appendDefaultArgs() } // buildResumeCommand is best-effort: log projection failures and // continue so `gc session attach` still starts the agent. The strict @@ -1143,7 +1159,7 @@ func buildResumeCommand(cityPath string, cfg *config.City, info session.Info, se // creation on projection errors. providerFamily := resolvedProviderLaunchFamily(resolved) sa, saErr := ensureClaudeSettingsArgs(fsys.OSFS{}, cityPath, providerFamily, stderr) - if saErr == nil && sa != "" { + if saErr == nil && sa != "" && !storedCommandHasSettingsArg(command) { command = command + " " + sa } else if saErr != nil { // Projection failed this tick. Fall back to the last-known-good @@ -1162,7 +1178,7 @@ func buildResumeCommand(cityPath string, cfg *config.City, info session.Info, se resolvedInfo.Provider = resolved.Name resolvedInfo.ResumeFlag = resolved.ResumeFlag resolvedInfo.ResumeStyle = resolved.ResumeStyle - resolvedInfo.ResumeCommand = resolved.ResumeCommand + resolvedInfo.ResumeCommand = resumeCommand return session.BuildResumeCommand(resolvedInfo), runtime.Config{ WorkDir: info.WorkDir, ReadyPromptPrefix: resolved.ReadyPromptPrefix, @@ -1174,21 +1190,29 @@ func buildResumeCommand(cityPath string, cfg *config.City, info session.Info, se } } - // Check persisted kind to avoid agent/provider name collisions. - // If kind is "provider", skip the agent template lookup entirely. - if sessionKind != "provider" { - // Prefer the current resolved agent template/provider config over stale - // stored command text so submit/restart paths honor provider overrides. - if found, ok := resolveAgentIdentity(cfg, info.Template, ""); ok { + // Prefer the current resolved agent template/provider config over stale + // stored command text so submit/restart paths honor provider overrides. + // Use the same collision guard as the runtime resolver so provider-track + // sessions do not accidentally resolve through an agent with the same name. + found, foundAgent := resolveAgentIdentity(cfg, info.Template, "") + if session.UseAgentTemplateForProviderResolution(sessionKind, metadata, info.Provider, found.Provider, foundAgent) { + if foundAgent { if resolved, err := config.ResolveProvider(&found, &cfg.Workspace, cfg.Providers, exec.LookPath); err == nil { return buildResolved(resolved) } } } - // Fallback for provider-only sessions whose Template is a provider name. - if resolved, err := config.ResolveProvider(&config.Agent{Provider: info.Template}, &cfg.Workspace, cfg.Providers, exec.LookPath); err == nil { - return buildResolved(resolved) + // Fallback for provider-only sessions. Prefer the persisted provider so + // resumed sessions use the same schema-backed provider selected at create. + for _, providerName := range []string{info.Provider, info.Template} { + providerName = strings.TrimSpace(providerName) + if providerName == "" { + continue + } + if resolved, err := config.ResolveProvider(&config.Agent{Provider: providerName}, &cfg.Workspace, cfg.Providers, exec.LookPath); err == nil { + return buildResolved(resolved) + } } return cmd, runtime.Config{WorkDir: info.WorkDir} diff --git a/cmd/gc/cmd_session_test.go b/cmd/gc/cmd_session_test.go index ffc50897fc..fd3481043f 100644 --- a/cmd/gc/cmd_session_test.go +++ b/cmd/gc/cmd_session_test.go @@ -779,7 +779,7 @@ func TestBuildResumeCommandUsesResolvedProviderCommand(t *testing.T) { WorkDir: "/tmp/workdir", } - cmd, hints := buildResumeCommand(t.TempDir(), cfg, info, "", io.Discard) + cmd, hints := buildResumeCommand(t.TempDir(), cfg, info, "", nil, io.Discard) if got, want := cmd, "aimux run gemini -- --approval-mode yolo"; got != want { t.Fatalf("resume command = %q, want %q", got, want) } @@ -820,7 +820,7 @@ func TestBuildResumeCommandIncludesSettingsAndDefaultArgs(t *testing.T) { ResumeFlag: "--resume", } - cmd, _ := buildResumeCommand(cityDir, cfg, info, "", io.Discard) + cmd, _ := buildResumeCommand(cityDir, cfg, info, "", nil, io.Discard) // Must include --settings pointing to .gc/settings.json. wantSettings := fmt.Sprintf("--settings %q", filepath.Join(gcDir, "settings.json")) @@ -830,6 +830,9 @@ func TestBuildResumeCommandIncludesSettingsAndDefaultArgs(t *testing.T) { if !strings.Contains(cmd, wantSettings) { t.Fatalf("resume command has wrong --settings path:\n got: %s\n want: ...%s...", cmd, wantSettings) } + if got := strings.Count(cmd, "--settings"); got != 1 { + t.Fatalf("resume command has %d --settings flags, want 1:\n got: %s", got, cmd) + } // Must include --resume flag. if !strings.Contains(cmd, "--resume abc-123") { @@ -861,12 +864,15 @@ func TestBuildResumeCommandUsesBuiltinAncestorForClaudeSettings(t *testing.T) { WorkDir: "/tmp/workdir", } - cmd, _ := buildResumeCommand(cityDir, cfg, info, "", io.Discard) + cmd, _ := buildResumeCommand(cityDir, cfg, info, "", nil, io.Discard) wantSettings := fmt.Sprintf("--settings %q", filepath.Join(cityDir, ".gc", "settings.json")) if !strings.Contains(cmd, wantSettings) { t.Fatalf("wrapped Claude resume command missing settings:\n got: %s\n want: ...%s...", cmd, wantSettings) } + if got := strings.Count(cmd, "--settings"); got != 1 { + t.Fatalf("wrapped Claude resume command has %d --settings flags, want 1:\n got: %s", got, cmd) + } } func TestBuildResumeCommandIncludesWrappedCodexResumeDefaults(t *testing.T) { @@ -900,13 +906,131 @@ func TestBuildResumeCommandIncludesWrappedCodexResumeDefaults(t *testing.T) { SessionKey: "abc-123", } - cmd, _ := buildResumeCommand(cityDir, cfg, info, "", io.Discard) + cmd, _ := buildResumeCommand(cityDir, cfg, info, "", nil, io.Discard) want := "aimux run codex -- --dangerously-bypass-approvals-and-sandbox -m gpt-5.3-codex-spark resume -c model_reasoning_effort=medium abc-123" if cmd != want { t.Fatalf("resume command = %q, want %q", cmd, want) } } +func TestBuildResumeCommandAppliesTemplateOverrides(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Provider: "codex-provider"}, + }, + Providers: map[string]config.ProviderSpec{ + "codex-provider": { + Command: "codex", + ResumeFlag: "--resume", + OptionsSchema: []config.ProviderOption{ + { + Key: "permission_mode", + Choices: []config.OptionChoice{ + {Value: "default", FlagArgs: []string{"--ask-for-approval", "on-request"}}, + {Value: "plan", FlagArgs: []string{"--ask-for-approval", "never"}}, + }, + }, + }, + }, + }, + } + info := session.Info{ + Template: "worker", + Command: "codex --ask-for-approval on-request", + Provider: "codex-provider", + WorkDir: "/tmp/workdir", + SessionKey: "abc-123", + } + + cmd, _ := buildResumeCommand(t.TempDir(), cfg, info, "", map[string]string{ + "template_overrides": `{"permission_mode":"plan"}`, + }, io.Discard) + want := "codex --resume abc-123 --ask-for-approval never" + if cmd != want { + t.Fatalf("resume command = %q, want %q", cmd, want) + } +} + +func TestBuildResumeCommandAppliesTemplateOverridesToExplicitResumeCommand(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Provider: "codex-provider"}, + }, + Providers: map[string]config.ProviderSpec{ + "codex-provider": { + Command: "codex", + ResumeCommand: "codex resume {{.SessionKey}} --ask-for-approval on-request", + OptionsSchema: []config.ProviderOption{ + { + Key: "permission_mode", + Choices: []config.OptionChoice{ + {Value: "default", FlagArgs: []string{"--ask-for-approval", "on-request"}}, + {Value: "plan", FlagArgs: []string{"--ask-for-approval", "never"}}, + }, + }, + }, + }, + }, + } + info := session.Info{ + Template: "worker", + Command: "codex --ask-for-approval on-request", + Provider: "codex-provider", + WorkDir: "/tmp/workdir", + SessionKey: "abc-123", + } + + cmd, _ := buildResumeCommand(t.TempDir(), cfg, info, "", map[string]string{ + "template_overrides": `{"permission_mode":"plan"}`, + }, io.Discard) + want := "codex resume --ask-for-approval never abc-123" + if cmd != want { + t.Fatalf("resume command = %q, want %q", cmd, want) + } +} + +func TestBuildResumeCommandFallsBackToDefaultArgsWhenOverridesInvalid(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Provider: "codex-provider"}, + }, + Providers: map[string]config.ProviderSpec{ + "codex-provider": { + Command: "codex", + Args: []string{"--ask-for-approval", "on-request"}, + ResumeFlag: "--resume", + OptionsSchema: []config.ProviderOption{ + { + Key: "permission_mode", + Choices: []config.OptionChoice{ + {Value: "default", FlagArgs: []string{"--ask-for-approval", "on-request"}}, + {Value: "plan", FlagArgs: []string{"--ask-for-approval", "never"}}, + }, + }, + }, + }, + }, + } + info := session.Info{ + Template: "worker", + Command: "codex", + Provider: "codex-provider", + WorkDir: "/tmp/workdir", + SessionKey: "abc-123", + } + + cmd, _ := buildResumeCommand(t.TempDir(), cfg, info, "", map[string]string{ + "template_overrides": `{"permission_mode":"invalid"}`, + }, io.Discard) + want := "codex --resume abc-123 --ask-for-approval on-request" + if cmd != want { + t.Fatalf("resume command = %q, want %q", cmd, want) + } +} + func TestBuildResumeCommandProviderKindSkipsTemplateCollision(t *testing.T) { cfg := &config.City{ Workspace: config.Workspace{Name: "test-city"}, @@ -933,13 +1057,80 @@ func TestBuildResumeCommandProviderKindSkipsTemplateCollision(t *testing.T) { SessionKey: "abc-123", } - cmd, _ := buildResumeCommand(t.TempDir(), cfg, info, "provider", io.Discard) + cmd, _ := buildResumeCommand(t.TempDir(), cfg, info, "provider", nil, io.Discard) want := "true provider --resume abc-123" if cmd != want { t.Fatalf("resume command = %q, want %q", cmd, want) } } +func TestBuildResumeCommandManualProviderMetadataSkipsTemplateCollision(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "runner", Provider: "agent-provider"}, + }, + Providers: map[string]config.ProviderSpec{ + "runner": { + Command: "true", + Args: []string{"provider"}, + ResumeFlag: "--resume", + }, + "agent-provider": { + Command: "true", + Args: []string{"agent"}, + ResumeFlag: "--resume", + }, + }, + } + info := session.Info{ + Template: "runner", + Provider: "runner", + Command: "stale", + WorkDir: "/tmp/workdir", + SessionKey: "abc-123", + } + + cmd, _ := buildResumeCommand(t.TempDir(), cfg, info, "", map[string]string{ + "session_origin": "manual", + }, io.Discard) + want := "true provider --resume abc-123" + if cmd != want { + t.Fatalf("resume command = %q, want %q", cmd, want) + } +} + +func TestBuildResumeCommandProviderKindPrefersPersistedProvider(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "stored-provider": { + Command: "true", + Args: []string{"stored"}, + ResumeFlag: "--resume", + }, + "template-provider": { + Command: "true", + Args: []string{"template"}, + ResumeFlag: "--resume", + }, + }, + } + info := session.Info{ + Template: "template-provider", + Provider: "stored-provider", + Command: "stale", + WorkDir: "/tmp/workdir", + SessionKey: "abc-123", + } + + cmd, _ := buildResumeCommand(t.TempDir(), cfg, info, "provider", nil, io.Discard) + want := "true stored --resume abc-123" + if cmd != want { + t.Fatalf("resume command = %q, want %q", cmd, want) + } +} + func TestSessionReason_FallsThroughToProviderForSleepingAttachment(t *testing.T) { provider := runtime.NewFake() if err := provider.Start(context.Background(), "sleeping-worker", runtime.Config{Command: "echo"}); err != nil { diff --git a/cmd/gc/dashboard/web/src/generated/index.ts b/cmd/gc/dashboard/web/src/generated/index.ts index f78220d9c2..a8f7e3de14 100644 --- a/cmd/gc/dashboard/web/src/generated/index.ts +++ b/cmd/gc/dashboard/web/src/generated/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { createAgent, createBead, createConvoy, createProvider, createRig, createSession, deleteV0CityByCityNameAgentByBase, deleteV0CityByCityNameAgentByDirByBase, deleteV0CityByCityNameBeadById, deleteV0CityByCityNameConvoyById, deleteV0CityByCityNameExtmsgAdapters, deleteV0CityByCityNameExtmsgParticipants, deleteV0CityByCityNameMailById, deleteV0CityByCityNamePatchesAgentByBase, deleteV0CityByCityNamePatchesAgentByDirByBase, deleteV0CityByCityNamePatchesProviderByName, deleteV0CityByCityNamePatchesRigByName, deleteV0CityByCityNameProviderByName, deleteV0CityByCityNameRigByName, deleteV0CityByCityNameWorkflowByWorkflowId, emitEvent, ensureExtmsgGroup, getHealth, getV0Cities, getV0CityByCityName, getV0CityByCityNameAgentByBase, getV0CityByCityNameAgentByBaseOutput, getV0CityByCityNameAgentByDirByBase, getV0CityByCityNameAgentByDirByBaseOutput, getV0CityByCityNameAgents, getV0CityByCityNameBeadById, getV0CityByCityNameBeadByIdDeps, getV0CityByCityNameBeads, getV0CityByCityNameBeadsGraphByRootId, getV0CityByCityNameBeadsReady, getV0CityByCityNameConfig, getV0CityByCityNameConfigExplain, getV0CityByCityNameConfigValidate, getV0CityByCityNameConvoyById, getV0CityByCityNameConvoyByIdCheck, getV0CityByCityNameConvoys, getV0CityByCityNameEvents, getV0CityByCityNameExtmsgAdapters, getV0CityByCityNameExtmsgBindings, getV0CityByCityNameExtmsgGroups, getV0CityByCityNameExtmsgTranscript, getV0CityByCityNameFormulaByName, getV0CityByCityNameFormulas, getV0CityByCityNameFormulasByName, getV0CityByCityNameFormulasByNameRuns, getV0CityByCityNameFormulasFeed, getV0CityByCityNameHealth, getV0CityByCityNameMail, getV0CityByCityNameMailById, getV0CityByCityNameMailCount, getV0CityByCityNameMailThreadById, getV0CityByCityNameOrderByName, getV0CityByCityNameOrderHistoryByBeadId, getV0CityByCityNameOrders, getV0CityByCityNameOrdersCheck, getV0CityByCityNameOrdersFeed, getV0CityByCityNameOrdersHistory, getV0CityByCityNamePacks, getV0CityByCityNamePatchesAgentByBase, getV0CityByCityNamePatchesAgentByDirByBase, getV0CityByCityNamePatchesAgents, getV0CityByCityNamePatchesProviderByName, getV0CityByCityNamePatchesProviders, getV0CityByCityNamePatchesRigByName, getV0CityByCityNamePatchesRigs, getV0CityByCityNameProviderByName, getV0CityByCityNameProviderReadiness, getV0CityByCityNameProviders, getV0CityByCityNameProvidersPublic, getV0CityByCityNameReadiness, getV0CityByCityNameRigByName, getV0CityByCityNameRigs, getV0CityByCityNameServiceByName, getV0CityByCityNameServices, getV0CityByCityNameSessionById, getV0CityByCityNameSessionByIdAgents, getV0CityByCityNameSessionByIdAgentsByAgentId, getV0CityByCityNameSessionByIdPending, getV0CityByCityNameSessionByIdTranscript, getV0CityByCityNameSessions, getV0CityByCityNameStatus, getV0CityByCityNameWorkflowByWorkflowId, getV0Events, getV0ProviderReadiness, getV0Readiness, type Options, patchV0CityByCityName, patchV0CityByCityNameAgentByBase, patchV0CityByCityNameAgentByDirByBase, patchV0CityByCityNameBeadById, patchV0CityByCityNameProviderByName, patchV0CityByCityNameRigByName, patchV0CityByCityNameSessionById, postV0City, postV0CityByCityNameAgentByBaseByAction, postV0CityByCityNameAgentByDirByBaseByAction, postV0CityByCityNameBeadByIdAssign, postV0CityByCityNameBeadByIdClose, postV0CityByCityNameBeadByIdReopen, postV0CityByCityNameBeadByIdUpdate, postV0CityByCityNameConvoyByIdAdd, postV0CityByCityNameConvoyByIdClose, postV0CityByCityNameConvoyByIdRemove, postV0CityByCityNameExtmsgBind, postV0CityByCityNameExtmsgInbound, postV0CityByCityNameExtmsgOutbound, postV0CityByCityNameExtmsgParticipants, postV0CityByCityNameExtmsgTranscriptAck, postV0CityByCityNameExtmsgUnbind, postV0CityByCityNameFormulasByNamePreview, postV0CityByCityNameMailByIdArchive, postV0CityByCityNameMailByIdMarkUnread, postV0CityByCityNameMailByIdRead, postV0CityByCityNameOrderByNameDisable, postV0CityByCityNameOrderByNameEnable, postV0CityByCityNameRigByNameByAction, postV0CityByCityNameServiceByNameRestart, postV0CityByCityNameSessionByIdClose, postV0CityByCityNameSessionByIdKill, postV0CityByCityNameSessionByIdRename, postV0CityByCityNameSessionByIdStop, postV0CityByCityNameSessionByIdSuspend, postV0CityByCityNameSessionByIdWake, postV0CityByCityNameSling, postV0CityByCityNameUnregister, putV0CityByCityNamePatchesAgents, putV0CityByCityNamePatchesProviders, putV0CityByCityNamePatchesRigs, registerExtmsgAdapter, replyMail, respondSession, sendMail, sendSessionMessage, streamAgentOutput, streamAgentOutputQualified, streamEvents, streamSession, streamSupervisorEvents, submitSession } from './sdk.gen'; -export type { AdapterCapabilities, AdapterEventPayload, AgentCreatedOutputBody, AgentCreateInputBody, AgentMapping, AgentOutputResponse, AgentPatch, AgentPatchSetInputBody, AgentResponse, AgentUpdateInputBody, AgentUpdateQualifiedInputBody, AnnotatedAgentResponse, AnnotatedProviderResponse, AsyncAcceptedBody, AsyncAcceptedResponse, Bead, BeadAssignInputBody, BeadCreateInputBody, BeadDepsResponse, BeadEventPayload, BeadGraphResponse, BeadUpdateBody, BindingStatus, BoundEventPayload, CityCreateRequest, CityCreateSucceededPayload, CityGetResponse, CityInfo, CityLifecyclePayload, CityPatchInputBody, CityUnregisterSucceededPayload, ClientOptions, ConfigAgentResponse, ConfigExplainPatches, ConfigExplainResponse, ConfigPatchesResponse, ConfigResponse, ConfigRigResponse, ConfigValidateOutputBody, ConversationGroupParticipant, ConversationGroupRecord, ConversationKind, ConversationRef, ConversationTranscriptRecord, ConvoyAddInputBody, ConvoyCheckResponse, ConvoyCreateInputBody, ConvoyGetResponse, ConvoyProgress, ConvoyRemoveInputBody, CreateAgentData, CreateAgentError, CreateAgentErrors, CreateAgentResponse, CreateAgentResponses, CreateBeadData, CreateBeadError, CreateBeadErrors, CreateBeadResponse, CreateBeadResponses, CreateConvoyData, CreateConvoyError, CreateConvoyErrors, CreateConvoyResponse, CreateConvoyResponses, CreateProviderData, CreateProviderError, CreateProviderErrors, CreateProviderResponse, CreateProviderResponses, CreateRigData, CreateRigError, CreateRigErrors, CreateRigResponse, CreateRigResponses, CreateSessionData, CreateSessionError, CreateSessionErrors, CreateSessionResponse, CreateSessionResponses, DeleteV0CityByCityNameAgentByBaseData, DeleteV0CityByCityNameAgentByBaseError, DeleteV0CityByCityNameAgentByBaseErrors, DeleteV0CityByCityNameAgentByBaseResponse, DeleteV0CityByCityNameAgentByBaseResponses, DeleteV0CityByCityNameAgentByDirByBaseData, DeleteV0CityByCityNameAgentByDirByBaseError, DeleteV0CityByCityNameAgentByDirByBaseErrors, DeleteV0CityByCityNameAgentByDirByBaseResponse, DeleteV0CityByCityNameAgentByDirByBaseResponses, DeleteV0CityByCityNameBeadByIdData, DeleteV0CityByCityNameBeadByIdError, DeleteV0CityByCityNameBeadByIdErrors, DeleteV0CityByCityNameBeadByIdResponse, DeleteV0CityByCityNameBeadByIdResponses, DeleteV0CityByCityNameConvoyByIdData, DeleteV0CityByCityNameConvoyByIdError, DeleteV0CityByCityNameConvoyByIdErrors, DeleteV0CityByCityNameConvoyByIdResponse, DeleteV0CityByCityNameConvoyByIdResponses, DeleteV0CityByCityNameExtmsgAdaptersData, DeleteV0CityByCityNameExtmsgAdaptersError, DeleteV0CityByCityNameExtmsgAdaptersErrors, DeleteV0CityByCityNameExtmsgAdaptersResponse, DeleteV0CityByCityNameExtmsgAdaptersResponses, DeleteV0CityByCityNameExtmsgParticipantsData, DeleteV0CityByCityNameExtmsgParticipantsError, DeleteV0CityByCityNameExtmsgParticipantsErrors, DeleteV0CityByCityNameExtmsgParticipantsResponse, DeleteV0CityByCityNameExtmsgParticipantsResponses, DeleteV0CityByCityNameMailByIdData, DeleteV0CityByCityNameMailByIdError, DeleteV0CityByCityNameMailByIdErrors, DeleteV0CityByCityNameMailByIdResponse, DeleteV0CityByCityNameMailByIdResponses, DeleteV0CityByCityNamePatchesAgentByBaseData, DeleteV0CityByCityNamePatchesAgentByBaseError, DeleteV0CityByCityNamePatchesAgentByBaseErrors, DeleteV0CityByCityNamePatchesAgentByBaseResponse, DeleteV0CityByCityNamePatchesAgentByBaseResponses, DeleteV0CityByCityNamePatchesAgentByDirByBaseData, DeleteV0CityByCityNamePatchesAgentByDirByBaseError, DeleteV0CityByCityNamePatchesAgentByDirByBaseErrors, DeleteV0CityByCityNamePatchesAgentByDirByBaseResponse, DeleteV0CityByCityNamePatchesAgentByDirByBaseResponses, DeleteV0CityByCityNamePatchesProviderByNameData, DeleteV0CityByCityNamePatchesProviderByNameError, DeleteV0CityByCityNamePatchesProviderByNameErrors, DeleteV0CityByCityNamePatchesProviderByNameResponse, DeleteV0CityByCityNamePatchesProviderByNameResponses, DeleteV0CityByCityNamePatchesRigByNameData, DeleteV0CityByCityNamePatchesRigByNameError, DeleteV0CityByCityNamePatchesRigByNameErrors, DeleteV0CityByCityNamePatchesRigByNameResponse, DeleteV0CityByCityNamePatchesRigByNameResponses, DeleteV0CityByCityNameProviderByNameData, DeleteV0CityByCityNameProviderByNameError, DeleteV0CityByCityNameProviderByNameErrors, DeleteV0CityByCityNameProviderByNameResponse, DeleteV0CityByCityNameProviderByNameResponses, DeleteV0CityByCityNameRigByNameData, DeleteV0CityByCityNameRigByNameError, DeleteV0CityByCityNameRigByNameErrors, DeleteV0CityByCityNameRigByNameResponse, DeleteV0CityByCityNameRigByNameResponses, DeleteV0CityByCityNameWorkflowByWorkflowIdData, DeleteV0CityByCityNameWorkflowByWorkflowIdError, DeleteV0CityByCityNameWorkflowByWorkflowIdErrors, DeleteV0CityByCityNameWorkflowByWorkflowIdResponse, DeleteV0CityByCityNameWorkflowByWorkflowIdResponses, DeliveryContextRecord, Dep, EmitEventData, EmitEventError, EmitEventErrors, EmitEventResponse, EmitEventResponses, EnsureExtmsgGroupData, EnsureExtmsgGroupError, EnsureExtmsgGroupErrors, EnsureExtmsgGroupResponse, EnsureExtmsgGroupResponses, ErrorDetail, ErrorModel, EventEmitOutputBody, EventEmitRequest, EventPayload, EventStreamEnvelope, ExternalActor, ExternalAttachment, ExternalInboundMessage, ExtmsgAdapterInfo, ExtMsgAdapterRegisterInputBody, ExtMsgAdapterRegisterOutputBody, ExtMsgAdapterUnregisterInputBody, ExtMsgBindInputBody, ExtMsgGroupEnsureInputBody, ExtMsgInboundInputBody, ExtMsgOutboundInputBody, ExtMsgParticipantRemoveInputBody, ExtMsgParticipantUpsertInputBody, ExtMsgTranscriptAckInputBody, ExtMsgUnbindBody, ExtMsgUnbindInputBody, FanoutPolicy, FormulaDetailResponse, FormulaFeedBody, FormulaListBody, FormulaPreviewBody, FormulaPreviewEdgeResponse, FormulaPreviewNodeResponse, FormulaPreviewResponse, FormulaRecentRunResponse, FormulaRunsResponse, FormulaStepResponse, FormulaSummaryResponse, FormulaVarDefResponse, GetHealthData, GetHealthError, GetHealthErrors, GetHealthResponse, GetHealthResponses, GetV0CitiesData, GetV0CitiesError, GetV0CitiesErrors, GetV0CitiesResponse, GetV0CitiesResponses, GetV0CityByCityNameAgentByBaseData, GetV0CityByCityNameAgentByBaseError, GetV0CityByCityNameAgentByBaseErrors, GetV0CityByCityNameAgentByBaseOutputData, GetV0CityByCityNameAgentByBaseOutputError, GetV0CityByCityNameAgentByBaseOutputErrors, GetV0CityByCityNameAgentByBaseOutputResponse, GetV0CityByCityNameAgentByBaseOutputResponses, GetV0CityByCityNameAgentByBaseResponse, GetV0CityByCityNameAgentByBaseResponses, GetV0CityByCityNameAgentByDirByBaseData, GetV0CityByCityNameAgentByDirByBaseError, GetV0CityByCityNameAgentByDirByBaseErrors, GetV0CityByCityNameAgentByDirByBaseOutputData, GetV0CityByCityNameAgentByDirByBaseOutputError, GetV0CityByCityNameAgentByDirByBaseOutputErrors, GetV0CityByCityNameAgentByDirByBaseOutputResponse, GetV0CityByCityNameAgentByDirByBaseOutputResponses, GetV0CityByCityNameAgentByDirByBaseResponse, GetV0CityByCityNameAgentByDirByBaseResponses, GetV0CityByCityNameAgentsData, GetV0CityByCityNameAgentsError, GetV0CityByCityNameAgentsErrors, GetV0CityByCityNameAgentsResponse, GetV0CityByCityNameAgentsResponses, GetV0CityByCityNameBeadByIdData, GetV0CityByCityNameBeadByIdDepsData, GetV0CityByCityNameBeadByIdDepsError, GetV0CityByCityNameBeadByIdDepsErrors, GetV0CityByCityNameBeadByIdDepsResponse, GetV0CityByCityNameBeadByIdDepsResponses, GetV0CityByCityNameBeadByIdError, GetV0CityByCityNameBeadByIdErrors, GetV0CityByCityNameBeadByIdResponse, GetV0CityByCityNameBeadByIdResponses, GetV0CityByCityNameBeadsData, GetV0CityByCityNameBeadsError, GetV0CityByCityNameBeadsErrors, GetV0CityByCityNameBeadsGraphByRootIdData, GetV0CityByCityNameBeadsGraphByRootIdError, GetV0CityByCityNameBeadsGraphByRootIdErrors, GetV0CityByCityNameBeadsGraphByRootIdResponse, GetV0CityByCityNameBeadsGraphByRootIdResponses, GetV0CityByCityNameBeadsReadyData, GetV0CityByCityNameBeadsReadyError, GetV0CityByCityNameBeadsReadyErrors, GetV0CityByCityNameBeadsReadyResponse, GetV0CityByCityNameBeadsReadyResponses, GetV0CityByCityNameBeadsResponse, GetV0CityByCityNameBeadsResponses, GetV0CityByCityNameConfigData, GetV0CityByCityNameConfigError, GetV0CityByCityNameConfigErrors, GetV0CityByCityNameConfigExplainData, GetV0CityByCityNameConfigExplainError, GetV0CityByCityNameConfigExplainErrors, GetV0CityByCityNameConfigExplainResponse, GetV0CityByCityNameConfigExplainResponses, GetV0CityByCityNameConfigResponse, GetV0CityByCityNameConfigResponses, GetV0CityByCityNameConfigValidateData, GetV0CityByCityNameConfigValidateError, GetV0CityByCityNameConfigValidateErrors, GetV0CityByCityNameConfigValidateResponse, GetV0CityByCityNameConfigValidateResponses, GetV0CityByCityNameConvoyByIdCheckData, GetV0CityByCityNameConvoyByIdCheckError, GetV0CityByCityNameConvoyByIdCheckErrors, GetV0CityByCityNameConvoyByIdCheckResponse, GetV0CityByCityNameConvoyByIdCheckResponses, GetV0CityByCityNameConvoyByIdData, GetV0CityByCityNameConvoyByIdError, GetV0CityByCityNameConvoyByIdErrors, GetV0CityByCityNameConvoyByIdResponse, GetV0CityByCityNameConvoyByIdResponses, GetV0CityByCityNameConvoysData, GetV0CityByCityNameConvoysError, GetV0CityByCityNameConvoysErrors, GetV0CityByCityNameConvoysResponse, GetV0CityByCityNameConvoysResponses, GetV0CityByCityNameData, GetV0CityByCityNameError, GetV0CityByCityNameErrors, GetV0CityByCityNameEventsData, GetV0CityByCityNameEventsError, GetV0CityByCityNameEventsErrors, GetV0CityByCityNameEventsResponse, GetV0CityByCityNameEventsResponses, GetV0CityByCityNameExtmsgAdaptersData, GetV0CityByCityNameExtmsgAdaptersError, GetV0CityByCityNameExtmsgAdaptersErrors, GetV0CityByCityNameExtmsgAdaptersResponse, GetV0CityByCityNameExtmsgAdaptersResponses, GetV0CityByCityNameExtmsgBindingsData, GetV0CityByCityNameExtmsgBindingsError, GetV0CityByCityNameExtmsgBindingsErrors, GetV0CityByCityNameExtmsgBindingsResponse, GetV0CityByCityNameExtmsgBindingsResponses, GetV0CityByCityNameExtmsgGroupsData, GetV0CityByCityNameExtmsgGroupsError, GetV0CityByCityNameExtmsgGroupsErrors, GetV0CityByCityNameExtmsgGroupsResponse, GetV0CityByCityNameExtmsgGroupsResponses, GetV0CityByCityNameExtmsgTranscriptData, GetV0CityByCityNameExtmsgTranscriptError, GetV0CityByCityNameExtmsgTranscriptErrors, GetV0CityByCityNameExtmsgTranscriptResponse, GetV0CityByCityNameExtmsgTranscriptResponses, GetV0CityByCityNameFormulaByNameData, GetV0CityByCityNameFormulaByNameError, GetV0CityByCityNameFormulaByNameErrors, GetV0CityByCityNameFormulaByNameResponse, GetV0CityByCityNameFormulaByNameResponses, GetV0CityByCityNameFormulasByNameData, GetV0CityByCityNameFormulasByNameError, GetV0CityByCityNameFormulasByNameErrors, GetV0CityByCityNameFormulasByNameResponse, GetV0CityByCityNameFormulasByNameResponses, GetV0CityByCityNameFormulasByNameRunsData, GetV0CityByCityNameFormulasByNameRunsError, GetV0CityByCityNameFormulasByNameRunsErrors, GetV0CityByCityNameFormulasByNameRunsResponse, GetV0CityByCityNameFormulasByNameRunsResponses, GetV0CityByCityNameFormulasData, GetV0CityByCityNameFormulasError, GetV0CityByCityNameFormulasErrors, GetV0CityByCityNameFormulasFeedData, GetV0CityByCityNameFormulasFeedError, GetV0CityByCityNameFormulasFeedErrors, GetV0CityByCityNameFormulasFeedResponse, GetV0CityByCityNameFormulasFeedResponses, GetV0CityByCityNameFormulasResponse, GetV0CityByCityNameFormulasResponses, GetV0CityByCityNameHealthData, GetV0CityByCityNameHealthError, GetV0CityByCityNameHealthErrors, GetV0CityByCityNameHealthResponse, GetV0CityByCityNameHealthResponses, GetV0CityByCityNameMailByIdData, GetV0CityByCityNameMailByIdError, GetV0CityByCityNameMailByIdErrors, GetV0CityByCityNameMailByIdResponse, GetV0CityByCityNameMailByIdResponses, GetV0CityByCityNameMailCountData, GetV0CityByCityNameMailCountError, GetV0CityByCityNameMailCountErrors, GetV0CityByCityNameMailCountResponse, GetV0CityByCityNameMailCountResponses, GetV0CityByCityNameMailData, GetV0CityByCityNameMailError, GetV0CityByCityNameMailErrors, GetV0CityByCityNameMailResponse, GetV0CityByCityNameMailResponses, GetV0CityByCityNameMailThreadByIdData, GetV0CityByCityNameMailThreadByIdError, GetV0CityByCityNameMailThreadByIdErrors, GetV0CityByCityNameMailThreadByIdResponse, GetV0CityByCityNameMailThreadByIdResponses, GetV0CityByCityNameOrderByNameData, GetV0CityByCityNameOrderByNameError, GetV0CityByCityNameOrderByNameErrors, GetV0CityByCityNameOrderByNameResponse, GetV0CityByCityNameOrderByNameResponses, GetV0CityByCityNameOrderHistoryByBeadIdData, GetV0CityByCityNameOrderHistoryByBeadIdError, GetV0CityByCityNameOrderHistoryByBeadIdErrors, GetV0CityByCityNameOrderHistoryByBeadIdResponse, GetV0CityByCityNameOrderHistoryByBeadIdResponses, GetV0CityByCityNameOrdersCheckData, GetV0CityByCityNameOrdersCheckError, GetV0CityByCityNameOrdersCheckErrors, GetV0CityByCityNameOrdersCheckResponse, GetV0CityByCityNameOrdersCheckResponses, GetV0CityByCityNameOrdersData, GetV0CityByCityNameOrdersError, GetV0CityByCityNameOrdersErrors, GetV0CityByCityNameOrdersFeedData, GetV0CityByCityNameOrdersFeedError, GetV0CityByCityNameOrdersFeedErrors, GetV0CityByCityNameOrdersFeedResponse, GetV0CityByCityNameOrdersFeedResponses, GetV0CityByCityNameOrdersHistoryData, GetV0CityByCityNameOrdersHistoryError, GetV0CityByCityNameOrdersHistoryErrors, GetV0CityByCityNameOrdersHistoryResponse, GetV0CityByCityNameOrdersHistoryResponses, GetV0CityByCityNameOrdersResponse, GetV0CityByCityNameOrdersResponses, GetV0CityByCityNamePacksData, GetV0CityByCityNamePacksError, GetV0CityByCityNamePacksErrors, GetV0CityByCityNamePacksResponse, GetV0CityByCityNamePacksResponses, GetV0CityByCityNamePatchesAgentByBaseData, GetV0CityByCityNamePatchesAgentByBaseError, GetV0CityByCityNamePatchesAgentByBaseErrors, GetV0CityByCityNamePatchesAgentByBaseResponse, GetV0CityByCityNamePatchesAgentByBaseResponses, GetV0CityByCityNamePatchesAgentByDirByBaseData, GetV0CityByCityNamePatchesAgentByDirByBaseError, GetV0CityByCityNamePatchesAgentByDirByBaseErrors, GetV0CityByCityNamePatchesAgentByDirByBaseResponse, GetV0CityByCityNamePatchesAgentByDirByBaseResponses, GetV0CityByCityNamePatchesAgentsData, GetV0CityByCityNamePatchesAgentsError, GetV0CityByCityNamePatchesAgentsErrors, GetV0CityByCityNamePatchesAgentsResponse, GetV0CityByCityNamePatchesAgentsResponses, GetV0CityByCityNamePatchesProviderByNameData, GetV0CityByCityNamePatchesProviderByNameError, GetV0CityByCityNamePatchesProviderByNameErrors, GetV0CityByCityNamePatchesProviderByNameResponse, GetV0CityByCityNamePatchesProviderByNameResponses, GetV0CityByCityNamePatchesProvidersData, GetV0CityByCityNamePatchesProvidersError, GetV0CityByCityNamePatchesProvidersErrors, GetV0CityByCityNamePatchesProvidersResponse, GetV0CityByCityNamePatchesProvidersResponses, GetV0CityByCityNamePatchesRigByNameData, GetV0CityByCityNamePatchesRigByNameError, GetV0CityByCityNamePatchesRigByNameErrors, GetV0CityByCityNamePatchesRigByNameResponse, GetV0CityByCityNamePatchesRigByNameResponses, GetV0CityByCityNamePatchesRigsData, GetV0CityByCityNamePatchesRigsError, GetV0CityByCityNamePatchesRigsErrors, GetV0CityByCityNamePatchesRigsResponse, GetV0CityByCityNamePatchesRigsResponses, GetV0CityByCityNameProviderByNameData, GetV0CityByCityNameProviderByNameError, GetV0CityByCityNameProviderByNameErrors, GetV0CityByCityNameProviderByNameResponse, GetV0CityByCityNameProviderByNameResponses, GetV0CityByCityNameProviderReadinessData, GetV0CityByCityNameProviderReadinessError, GetV0CityByCityNameProviderReadinessErrors, GetV0CityByCityNameProviderReadinessResponse, GetV0CityByCityNameProviderReadinessResponses, GetV0CityByCityNameProvidersData, GetV0CityByCityNameProvidersError, GetV0CityByCityNameProvidersErrors, GetV0CityByCityNameProvidersPublicData, GetV0CityByCityNameProvidersPublicError, GetV0CityByCityNameProvidersPublicErrors, GetV0CityByCityNameProvidersPublicResponse, GetV0CityByCityNameProvidersPublicResponses, GetV0CityByCityNameProvidersResponse, GetV0CityByCityNameProvidersResponses, GetV0CityByCityNameReadinessData, GetV0CityByCityNameReadinessError, GetV0CityByCityNameReadinessErrors, GetV0CityByCityNameReadinessResponse, GetV0CityByCityNameReadinessResponses, GetV0CityByCityNameResponse, GetV0CityByCityNameResponses, GetV0CityByCityNameRigByNameData, GetV0CityByCityNameRigByNameError, GetV0CityByCityNameRigByNameErrors, GetV0CityByCityNameRigByNameResponse, GetV0CityByCityNameRigByNameResponses, GetV0CityByCityNameRigsData, GetV0CityByCityNameRigsError, GetV0CityByCityNameRigsErrors, GetV0CityByCityNameRigsResponse, GetV0CityByCityNameRigsResponses, GetV0CityByCityNameServiceByNameData, GetV0CityByCityNameServiceByNameError, GetV0CityByCityNameServiceByNameErrors, GetV0CityByCityNameServiceByNameResponse, GetV0CityByCityNameServiceByNameResponses, GetV0CityByCityNameServicesData, GetV0CityByCityNameServicesError, GetV0CityByCityNameServicesErrors, GetV0CityByCityNameServicesResponse, GetV0CityByCityNameServicesResponses, GetV0CityByCityNameSessionByIdAgentsByAgentIdData, GetV0CityByCityNameSessionByIdAgentsByAgentIdError, GetV0CityByCityNameSessionByIdAgentsByAgentIdErrors, GetV0CityByCityNameSessionByIdAgentsByAgentIdResponse, GetV0CityByCityNameSessionByIdAgentsByAgentIdResponses, GetV0CityByCityNameSessionByIdAgentsData, GetV0CityByCityNameSessionByIdAgentsError, GetV0CityByCityNameSessionByIdAgentsErrors, GetV0CityByCityNameSessionByIdAgentsResponse, GetV0CityByCityNameSessionByIdAgentsResponses, GetV0CityByCityNameSessionByIdData, GetV0CityByCityNameSessionByIdError, GetV0CityByCityNameSessionByIdErrors, GetV0CityByCityNameSessionByIdPendingData, GetV0CityByCityNameSessionByIdPendingError, GetV0CityByCityNameSessionByIdPendingErrors, GetV0CityByCityNameSessionByIdPendingResponse, GetV0CityByCityNameSessionByIdPendingResponses, GetV0CityByCityNameSessionByIdResponse, GetV0CityByCityNameSessionByIdResponses, GetV0CityByCityNameSessionByIdTranscriptData, GetV0CityByCityNameSessionByIdTranscriptError, GetV0CityByCityNameSessionByIdTranscriptErrors, GetV0CityByCityNameSessionByIdTranscriptResponse, GetV0CityByCityNameSessionByIdTranscriptResponses, GetV0CityByCityNameSessionsData, GetV0CityByCityNameSessionsError, GetV0CityByCityNameSessionsErrors, GetV0CityByCityNameSessionsResponse, GetV0CityByCityNameSessionsResponses, GetV0CityByCityNameStatusData, GetV0CityByCityNameStatusError, GetV0CityByCityNameStatusErrors, GetV0CityByCityNameStatusResponse, GetV0CityByCityNameStatusResponses, GetV0CityByCityNameWorkflowByWorkflowIdData, GetV0CityByCityNameWorkflowByWorkflowIdError, GetV0CityByCityNameWorkflowByWorkflowIdErrors, GetV0CityByCityNameWorkflowByWorkflowIdResponse, GetV0CityByCityNameWorkflowByWorkflowIdResponses, GetV0EventsData, GetV0EventsError, GetV0EventsErrors, GetV0EventsResponse, GetV0EventsResponses, GetV0ProviderReadinessData, GetV0ProviderReadinessError, GetV0ProviderReadinessErrors, GetV0ProviderReadinessResponse, GetV0ProviderReadinessResponses, GetV0ReadinessData, GetV0ReadinessError, GetV0ReadinessErrors, GetV0ReadinessResponse, GetV0ReadinessResponses, GitStatus, GroupCreatedEventPayload, GroupRouteDecision, HealthOutputBody, HeartbeatEvent, InboundEventPayload, InboundResult, ListBodyAgentPatch, ListBodyAgentResponse, ListBodyBead, ListBodyConversationTranscriptRecord, ListBodyExtmsgAdapterInfo, ListBodyProviderPatch, ListBodyProviderResponse, ListBodyRigPatch, ListBodyRigResponse, ListBodySessionBindingRecord, ListBodySessionResponse, ListBodyStatus, ListBodyWireEvent, LogicalNode, MailCountOutputBody, MailEventPayload, MailListBody, MailReplyInputBody, MailSendInputBody, Message, MonitorFeedItemResponse, NoPayload, OkResponseBody, OkWithIdResponseBody, OptionChoiceDto, OrderCheckListBody, OrderCheckResponse, OrderHistoryDetailResponse, OrderHistoryEntry, OrderHistoryListBody, OrderListBody, OrderResponse, OrdersFeedBody, OutboundEventPayload, OutboundResult, OutputTurn, PackListBody, PackResponse, PaginationInfo, PatchDeletedResponseBody, PatchOkResponseBody, PatchV0CityByCityNameAgentByBaseData, PatchV0CityByCityNameAgentByBaseError, PatchV0CityByCityNameAgentByBaseErrors, PatchV0CityByCityNameAgentByBaseResponse, PatchV0CityByCityNameAgentByBaseResponses, PatchV0CityByCityNameAgentByDirByBaseData, PatchV0CityByCityNameAgentByDirByBaseError, PatchV0CityByCityNameAgentByDirByBaseErrors, PatchV0CityByCityNameAgentByDirByBaseResponse, PatchV0CityByCityNameAgentByDirByBaseResponses, PatchV0CityByCityNameBeadByIdData, PatchV0CityByCityNameBeadByIdError, PatchV0CityByCityNameBeadByIdErrors, PatchV0CityByCityNameBeadByIdResponse, PatchV0CityByCityNameBeadByIdResponses, PatchV0CityByCityNameData, PatchV0CityByCityNameError, PatchV0CityByCityNameErrors, PatchV0CityByCityNameProviderByNameData, PatchV0CityByCityNameProviderByNameError, PatchV0CityByCityNameProviderByNameErrors, PatchV0CityByCityNameProviderByNameResponse, PatchV0CityByCityNameProviderByNameResponses, PatchV0CityByCityNameResponse, PatchV0CityByCityNameResponses, PatchV0CityByCityNameRigByNameData, PatchV0CityByCityNameRigByNameError, PatchV0CityByCityNameRigByNameErrors, PatchV0CityByCityNameRigByNameResponse, PatchV0CityByCityNameRigByNameResponses, PatchV0CityByCityNameSessionByIdData, PatchV0CityByCityNameSessionByIdError, PatchV0CityByCityNameSessionByIdErrors, PatchV0CityByCityNameSessionByIdResponse, PatchV0CityByCityNameSessionByIdResponses, PendingInteraction, PoolOverride, PostV0CityByCityNameAgentByBaseByActionData, PostV0CityByCityNameAgentByBaseByActionError, PostV0CityByCityNameAgentByBaseByActionErrors, PostV0CityByCityNameAgentByBaseByActionResponse, PostV0CityByCityNameAgentByBaseByActionResponses, PostV0CityByCityNameAgentByDirByBaseByActionData, PostV0CityByCityNameAgentByDirByBaseByActionError, PostV0CityByCityNameAgentByDirByBaseByActionErrors, PostV0CityByCityNameAgentByDirByBaseByActionResponse, PostV0CityByCityNameAgentByDirByBaseByActionResponses, PostV0CityByCityNameBeadByIdAssignData, PostV0CityByCityNameBeadByIdAssignError, PostV0CityByCityNameBeadByIdAssignErrors, PostV0CityByCityNameBeadByIdAssignResponse, PostV0CityByCityNameBeadByIdAssignResponses, PostV0CityByCityNameBeadByIdCloseData, PostV0CityByCityNameBeadByIdCloseError, PostV0CityByCityNameBeadByIdCloseErrors, PostV0CityByCityNameBeadByIdCloseResponse, PostV0CityByCityNameBeadByIdCloseResponses, PostV0CityByCityNameBeadByIdReopenData, PostV0CityByCityNameBeadByIdReopenError, PostV0CityByCityNameBeadByIdReopenErrors, PostV0CityByCityNameBeadByIdReopenResponse, PostV0CityByCityNameBeadByIdReopenResponses, PostV0CityByCityNameBeadByIdUpdateData, PostV0CityByCityNameBeadByIdUpdateError, PostV0CityByCityNameBeadByIdUpdateErrors, PostV0CityByCityNameBeadByIdUpdateResponse, PostV0CityByCityNameBeadByIdUpdateResponses, PostV0CityByCityNameConvoyByIdAddData, PostV0CityByCityNameConvoyByIdAddError, PostV0CityByCityNameConvoyByIdAddErrors, PostV0CityByCityNameConvoyByIdAddResponse, PostV0CityByCityNameConvoyByIdAddResponses, PostV0CityByCityNameConvoyByIdCloseData, PostV0CityByCityNameConvoyByIdCloseError, PostV0CityByCityNameConvoyByIdCloseErrors, PostV0CityByCityNameConvoyByIdCloseResponse, PostV0CityByCityNameConvoyByIdCloseResponses, PostV0CityByCityNameConvoyByIdRemoveData, PostV0CityByCityNameConvoyByIdRemoveError, PostV0CityByCityNameConvoyByIdRemoveErrors, PostV0CityByCityNameConvoyByIdRemoveResponse, PostV0CityByCityNameConvoyByIdRemoveResponses, PostV0CityByCityNameExtmsgBindData, PostV0CityByCityNameExtmsgBindError, PostV0CityByCityNameExtmsgBindErrors, PostV0CityByCityNameExtmsgBindResponse, PostV0CityByCityNameExtmsgBindResponses, PostV0CityByCityNameExtmsgInboundData, PostV0CityByCityNameExtmsgInboundError, PostV0CityByCityNameExtmsgInboundErrors, PostV0CityByCityNameExtmsgInboundResponse, PostV0CityByCityNameExtmsgInboundResponses, PostV0CityByCityNameExtmsgOutboundData, PostV0CityByCityNameExtmsgOutboundError, PostV0CityByCityNameExtmsgOutboundErrors, PostV0CityByCityNameExtmsgOutboundResponse, PostV0CityByCityNameExtmsgOutboundResponses, PostV0CityByCityNameExtmsgParticipantsData, PostV0CityByCityNameExtmsgParticipantsError, PostV0CityByCityNameExtmsgParticipantsErrors, PostV0CityByCityNameExtmsgParticipantsResponse, PostV0CityByCityNameExtmsgParticipantsResponses, PostV0CityByCityNameExtmsgTranscriptAckData, PostV0CityByCityNameExtmsgTranscriptAckError, PostV0CityByCityNameExtmsgTranscriptAckErrors, PostV0CityByCityNameExtmsgTranscriptAckResponse, PostV0CityByCityNameExtmsgTranscriptAckResponses, PostV0CityByCityNameExtmsgUnbindData, PostV0CityByCityNameExtmsgUnbindError, PostV0CityByCityNameExtmsgUnbindErrors, PostV0CityByCityNameExtmsgUnbindResponse, PostV0CityByCityNameExtmsgUnbindResponses, PostV0CityByCityNameFormulasByNamePreviewData, PostV0CityByCityNameFormulasByNamePreviewError, PostV0CityByCityNameFormulasByNamePreviewErrors, PostV0CityByCityNameFormulasByNamePreviewResponse, PostV0CityByCityNameFormulasByNamePreviewResponses, PostV0CityByCityNameMailByIdArchiveData, PostV0CityByCityNameMailByIdArchiveError, PostV0CityByCityNameMailByIdArchiveErrors, PostV0CityByCityNameMailByIdArchiveResponse, PostV0CityByCityNameMailByIdArchiveResponses, PostV0CityByCityNameMailByIdMarkUnreadData, PostV0CityByCityNameMailByIdMarkUnreadError, PostV0CityByCityNameMailByIdMarkUnreadErrors, PostV0CityByCityNameMailByIdMarkUnreadResponse, PostV0CityByCityNameMailByIdMarkUnreadResponses, PostV0CityByCityNameMailByIdReadData, PostV0CityByCityNameMailByIdReadError, PostV0CityByCityNameMailByIdReadErrors, PostV0CityByCityNameMailByIdReadResponse, PostV0CityByCityNameMailByIdReadResponses, PostV0CityByCityNameOrderByNameDisableData, PostV0CityByCityNameOrderByNameDisableError, PostV0CityByCityNameOrderByNameDisableErrors, PostV0CityByCityNameOrderByNameDisableResponse, PostV0CityByCityNameOrderByNameDisableResponses, PostV0CityByCityNameOrderByNameEnableData, PostV0CityByCityNameOrderByNameEnableError, PostV0CityByCityNameOrderByNameEnableErrors, PostV0CityByCityNameOrderByNameEnableResponse, PostV0CityByCityNameOrderByNameEnableResponses, PostV0CityByCityNameRigByNameByActionData, PostV0CityByCityNameRigByNameByActionError, PostV0CityByCityNameRigByNameByActionErrors, PostV0CityByCityNameRigByNameByActionResponse, PostV0CityByCityNameRigByNameByActionResponses, PostV0CityByCityNameServiceByNameRestartData, PostV0CityByCityNameServiceByNameRestartError, PostV0CityByCityNameServiceByNameRestartErrors, PostV0CityByCityNameServiceByNameRestartResponse, PostV0CityByCityNameServiceByNameRestartResponses, PostV0CityByCityNameSessionByIdCloseData, PostV0CityByCityNameSessionByIdCloseError, PostV0CityByCityNameSessionByIdCloseErrors, PostV0CityByCityNameSessionByIdCloseResponse, PostV0CityByCityNameSessionByIdCloseResponses, PostV0CityByCityNameSessionByIdKillData, PostV0CityByCityNameSessionByIdKillError, PostV0CityByCityNameSessionByIdKillErrors, PostV0CityByCityNameSessionByIdKillResponse, PostV0CityByCityNameSessionByIdKillResponses, PostV0CityByCityNameSessionByIdRenameData, PostV0CityByCityNameSessionByIdRenameError, PostV0CityByCityNameSessionByIdRenameErrors, PostV0CityByCityNameSessionByIdRenameResponse, PostV0CityByCityNameSessionByIdRenameResponses, PostV0CityByCityNameSessionByIdStopData, PostV0CityByCityNameSessionByIdStopError, PostV0CityByCityNameSessionByIdStopErrors, PostV0CityByCityNameSessionByIdStopResponse, PostV0CityByCityNameSessionByIdStopResponses, PostV0CityByCityNameSessionByIdSuspendData, PostV0CityByCityNameSessionByIdSuspendError, PostV0CityByCityNameSessionByIdSuspendErrors, PostV0CityByCityNameSessionByIdSuspendResponse, PostV0CityByCityNameSessionByIdSuspendResponses, PostV0CityByCityNameSessionByIdWakeData, PostV0CityByCityNameSessionByIdWakeError, PostV0CityByCityNameSessionByIdWakeErrors, PostV0CityByCityNameSessionByIdWakeResponse, PostV0CityByCityNameSessionByIdWakeResponses, PostV0CityByCityNameSlingData, PostV0CityByCityNameSlingError, PostV0CityByCityNameSlingErrors, PostV0CityByCityNameSlingResponse, PostV0CityByCityNameSlingResponses, PostV0CityByCityNameUnregisterData, PostV0CityByCityNameUnregisterError, PostV0CityByCityNameUnregisterErrors, PostV0CityByCityNameUnregisterResponse, PostV0CityByCityNameUnregisterResponses, PostV0CityData, PostV0CityError, PostV0CityErrors, PostV0CityResponse, PostV0CityResponses, ProjectIdentityStampedPayload, ProviderCreatedOutputBody, ProviderCreateInputBody, ProviderOptionDto, ProviderPatch, ProviderPatchSetInputBody, ProviderPublicListBody, ProviderPublicResponse, ProviderReadiness, ProviderReadinessResponse, ProviderResponse, ProviderSpecJson, ProviderUpdateInputBody, PublishReceipt, PutV0CityByCityNamePatchesAgentsData, PutV0CityByCityNamePatchesAgentsError, PutV0CityByCityNamePatchesAgentsErrors, PutV0CityByCityNamePatchesAgentsResponse, PutV0CityByCityNamePatchesAgentsResponses, PutV0CityByCityNamePatchesProvidersData, PutV0CityByCityNamePatchesProvidersError, PutV0CityByCityNamePatchesProvidersErrors, PutV0CityByCityNamePatchesProvidersResponse, PutV0CityByCityNamePatchesProvidersResponses, PutV0CityByCityNamePatchesRigsData, PutV0CityByCityNamePatchesRigsError, PutV0CityByCityNamePatchesRigsErrors, PutV0CityByCityNamePatchesRigsResponse, PutV0CityByCityNamePatchesRigsResponses, ReadinessItem, ReadinessResponse, RegisterExtmsgAdapterData, RegisterExtmsgAdapterError, RegisterExtmsgAdapterErrors, RegisterExtmsgAdapterResponse, RegisterExtmsgAdapterResponses, ReplyMailData, ReplyMailError, ReplyMailErrors, ReplyMailResponse, ReplyMailResponses, RequestFailedPayload, RespondSessionData, RespondSessionError, RespondSessionErrors, RespondSessionResponse, RespondSessionResponses, RigActionBody, RigCreatedOutputBody, RigCreateInputBody, RigPatch, RigPatchSetInputBody, RigResponse, RigUpdateInputBody, RotatedPayload, ScopeGroup, SendMailData, SendMailError, SendMailErrors, SendMailResponse, SendMailResponses, SendSessionMessageData, SendSessionMessageError, SendSessionMessageErrors, SendSessionMessageResponse, SendSessionMessageResponses, ServiceRestartOutputBody, SessionActivityEvent, SessionAgentGetResponse, SessionAgentListResponse, SessionBindingRecord, SessionCreateBody, SessionCreateSucceededPayload, SessionInfo, SessionLifecyclePayload, SessionMessageInputBody, SessionMessageSucceededPayload, SessionPatchBody, SessionPendingResponse, SessionRawMessageFrame, SessionRenameInputBody, SessionRespondInputBody, SessionRespondOutputBody, SessionResponse, SessionStreamCommonEvent, SessionStreamMessageEvent, SessionStreamRawMessageEvent, SessionSubmitInputBody, SessionSubmitSucceededPayload, SessionTranscriptGetResponse, SlingInputBody, SlingResponse, Status, StatusAgentCounts, StatusBody, StatusMailCounts, StatusRigCounts, StatusWorkCounts, StreamAgentOutputData, StreamAgentOutputError, StreamAgentOutputErrors, StreamAgentOutputQualifiedData, StreamAgentOutputQualifiedError, StreamAgentOutputQualifiedErrors, StreamAgentOutputQualifiedResponse, StreamAgentOutputQualifiedResponses, StreamAgentOutputResponse, StreamAgentOutputResponses, StreamEventsData, StreamEventsError, StreamEventsErrors, StreamEventsResponse, StreamEventsResponses, StreamSessionData, StreamSessionError, StreamSessionErrors, StreamSessionResponse, StreamSessionResponses, StreamSupervisorEventsData, StreamSupervisorEventsError, StreamSupervisorEventsErrors, StreamSupervisorEventsResponse, StreamSupervisorEventsResponses, SubmissionCapabilities, SubmitIntent, SubmitSessionData, SubmitSessionError, SubmitSessionErrors, SubmitSessionResponse, SubmitSessionResponses, SupervisorCitiesOutputBody, SupervisorEventListOutputBody, SupervisorFsPressureSkippedTickPayload, SupervisorHealthOutputBody, SupervisorStartup, TaggedEventStreamEnvelope, TranscriptMessageKind, TranscriptProvenance, TypedEventStreamEnvelope, TypedEventStreamEnvelopeBeadClosed, TypedEventStreamEnvelopeBeadCreated, TypedEventStreamEnvelopeBeadUpdated, TypedEventStreamEnvelopeCityCreated, TypedEventStreamEnvelopeCityResumed, TypedEventStreamEnvelopeCitySuspended, TypedEventStreamEnvelopeCityUnregisterRequested, TypedEventStreamEnvelopeControllerStarted, TypedEventStreamEnvelopeControllerStopped, TypedEventStreamEnvelopeConvoyClosed, TypedEventStreamEnvelopeConvoyCreated, TypedEventStreamEnvelopeCustom, TypedEventStreamEnvelopeEventsRotated, TypedEventStreamEnvelopeExtmsgAdapterAdded, TypedEventStreamEnvelopeExtmsgAdapterRemoved, TypedEventStreamEnvelopeExtmsgBound, TypedEventStreamEnvelopeExtmsgGroupCreated, TypedEventStreamEnvelopeExtmsgInbound, TypedEventStreamEnvelopeExtmsgOutbound, TypedEventStreamEnvelopeExtmsgUnbound, TypedEventStreamEnvelopeMailArchived, TypedEventStreamEnvelopeMailDeleted, TypedEventStreamEnvelopeMailMarkedRead, TypedEventStreamEnvelopeMailMarkedUnread, TypedEventStreamEnvelopeMailRead, TypedEventStreamEnvelopeMailReplied, TypedEventStreamEnvelopeMailSent, TypedEventStreamEnvelopeOrderCompleted, TypedEventStreamEnvelopeOrderFailed, TypedEventStreamEnvelopeOrderFired, TypedEventStreamEnvelopeProjectIdentityStamped, TypedEventStreamEnvelopeProviderSwapped, TypedEventStreamEnvelopeRequestFailed, TypedEventStreamEnvelopeRequestResultCityCreate, TypedEventStreamEnvelopeRequestResultCityUnregister, TypedEventStreamEnvelopeRequestResultSessionCreate, TypedEventStreamEnvelopeRequestResultSessionMessage, TypedEventStreamEnvelopeRequestResultSessionSubmit, TypedEventStreamEnvelopeSessionCrashed, TypedEventStreamEnvelopeSessionDraining, TypedEventStreamEnvelopeSessionIdleKilled, TypedEventStreamEnvelopeSessionMaxAgeKilled, TypedEventStreamEnvelopeSessionQuarantined, TypedEventStreamEnvelopeSessionStopped, TypedEventStreamEnvelopeSessionSuspended, TypedEventStreamEnvelopeSessionUndrained, TypedEventStreamEnvelopeSessionUpdated, TypedEventStreamEnvelopeSessionWoke, TypedEventStreamEnvelopeSupervisorFsPressureSkippedTick, TypedEventStreamEnvelopeWorkerOperation, TypedTaggedEventStreamEnvelope, TypedTaggedEventStreamEnvelopeBeadClosed, TypedTaggedEventStreamEnvelopeBeadCreated, TypedTaggedEventStreamEnvelopeBeadUpdated, TypedTaggedEventStreamEnvelopeCityCreated, TypedTaggedEventStreamEnvelopeCityResumed, TypedTaggedEventStreamEnvelopeCitySuspended, TypedTaggedEventStreamEnvelopeCityUnregisterRequested, TypedTaggedEventStreamEnvelopeControllerStarted, TypedTaggedEventStreamEnvelopeControllerStopped, TypedTaggedEventStreamEnvelopeConvoyClosed, TypedTaggedEventStreamEnvelopeConvoyCreated, TypedTaggedEventStreamEnvelopeCustom, TypedTaggedEventStreamEnvelopeEventsRotated, TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded, TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved, TypedTaggedEventStreamEnvelopeExtmsgBound, TypedTaggedEventStreamEnvelopeExtmsgGroupCreated, TypedTaggedEventStreamEnvelopeExtmsgInbound, TypedTaggedEventStreamEnvelopeExtmsgOutbound, TypedTaggedEventStreamEnvelopeExtmsgUnbound, TypedTaggedEventStreamEnvelopeMailArchived, TypedTaggedEventStreamEnvelopeMailDeleted, TypedTaggedEventStreamEnvelopeMailMarkedRead, TypedTaggedEventStreamEnvelopeMailMarkedUnread, TypedTaggedEventStreamEnvelopeMailRead, TypedTaggedEventStreamEnvelopeMailReplied, TypedTaggedEventStreamEnvelopeMailSent, TypedTaggedEventStreamEnvelopeOrderCompleted, TypedTaggedEventStreamEnvelopeOrderFailed, TypedTaggedEventStreamEnvelopeOrderFired, TypedTaggedEventStreamEnvelopeProjectIdentityStamped, TypedTaggedEventStreamEnvelopeProviderSwapped, TypedTaggedEventStreamEnvelopeRequestFailed, TypedTaggedEventStreamEnvelopeRequestResultCityCreate, TypedTaggedEventStreamEnvelopeRequestResultCityUnregister, TypedTaggedEventStreamEnvelopeRequestResultSessionCreate, TypedTaggedEventStreamEnvelopeRequestResultSessionMessage, TypedTaggedEventStreamEnvelopeRequestResultSessionSubmit, TypedTaggedEventStreamEnvelopeSessionCrashed, TypedTaggedEventStreamEnvelopeSessionDraining, TypedTaggedEventStreamEnvelopeSessionIdleKilled, TypedTaggedEventStreamEnvelopeSessionMaxAgeKilled, TypedTaggedEventStreamEnvelopeSessionQuarantined, TypedTaggedEventStreamEnvelopeSessionStopped, TypedTaggedEventStreamEnvelopeSessionSuspended, TypedTaggedEventStreamEnvelopeSessionUndrained, TypedTaggedEventStreamEnvelopeSessionUpdated, TypedTaggedEventStreamEnvelopeSessionWoke, TypedTaggedEventStreamEnvelopeSupervisorFsPressureSkippedTick, TypedTaggedEventStreamEnvelopeWorkerOperation, UnboundEventPayload, WorkerOperationEventPayload, WorkflowAttemptSummary, WorkflowBeadResponse, WorkflowDeleteResponse, WorkflowDepResponse, WorkflowEventProjection, WorkflowSnapshotResponse, WorkspaceResponse } from './types.gen'; +export { createAgent, createBead, createConvoy, createProvider, createRig, createSession, deleteV0CityByCityNameAgentByBase, deleteV0CityByCityNameAgentByDirByBase, deleteV0CityByCityNameBeadById, deleteV0CityByCityNameConvoyById, deleteV0CityByCityNameExtmsgAdapters, deleteV0CityByCityNameExtmsgParticipants, deleteV0CityByCityNameMailById, deleteV0CityByCityNamePatchesAgentByBase, deleteV0CityByCityNamePatchesAgentByDirByBase, deleteV0CityByCityNamePatchesProviderByName, deleteV0CityByCityNamePatchesRigByName, deleteV0CityByCityNameProviderByName, deleteV0CityByCityNameRigByName, deleteV0CityByCityNameWorkflowByWorkflowId, emitEvent, ensureExtmsgGroup, getHealth, getV0Cities, getV0CityByCityName, getV0CityByCityNameAgentByBase, getV0CityByCityNameAgentByBaseOutput, getV0CityByCityNameAgentByDirByBase, getV0CityByCityNameAgentByDirByBaseOutput, getV0CityByCityNameAgents, getV0CityByCityNameBeadById, getV0CityByCityNameBeadByIdDeps, getV0CityByCityNameBeads, getV0CityByCityNameBeadsGraphByRootId, getV0CityByCityNameBeadsReady, getV0CityByCityNameConfig, getV0CityByCityNameConfigExplain, getV0CityByCityNameConfigValidate, getV0CityByCityNameConvoyById, getV0CityByCityNameConvoyByIdCheck, getV0CityByCityNameConvoys, getV0CityByCityNameEvents, getV0CityByCityNameExtmsgAdapters, getV0CityByCityNameExtmsgBindings, getV0CityByCityNameExtmsgGroups, getV0CityByCityNameExtmsgTranscript, getV0CityByCityNameFormulaByName, getV0CityByCityNameFormulas, getV0CityByCityNameFormulasByName, getV0CityByCityNameFormulasByNameRuns, getV0CityByCityNameFormulasFeed, getV0CityByCityNameHealth, getV0CityByCityNameMail, getV0CityByCityNameMailById, getV0CityByCityNameMailCount, getV0CityByCityNameMailThreadById, getV0CityByCityNameOrderByName, getV0CityByCityNameOrderHistoryByBeadId, getV0CityByCityNameOrders, getV0CityByCityNameOrdersCheck, getV0CityByCityNameOrdersFeed, getV0CityByCityNameOrdersHistory, getV0CityByCityNamePacks, getV0CityByCityNamePatchesAgentByBase, getV0CityByCityNamePatchesAgentByDirByBase, getV0CityByCityNamePatchesAgents, getV0CityByCityNamePatchesProviderByName, getV0CityByCityNamePatchesProviders, getV0CityByCityNamePatchesRigByName, getV0CityByCityNamePatchesRigs, getV0CityByCityNameProviderByName, getV0CityByCityNameProviderReadiness, getV0CityByCityNameProviders, getV0CityByCityNameProvidersPublic, getV0CityByCityNameReadiness, getV0CityByCityNameRigByName, getV0CityByCityNameRigs, getV0CityByCityNameServiceByName, getV0CityByCityNameServices, getV0CityByCityNameSessionById, getV0CityByCityNameSessionByIdAgents, getV0CityByCityNameSessionByIdAgentsByAgentId, getV0CityByCityNameSessionByIdPending, getV0CityByCityNameSessionByIdTranscript, getV0CityByCityNameSessions, getV0CityByCityNameStatus, getV0CityByCityNameWorkflowByWorkflowId, getV0Events, getV0ProviderReadiness, getV0Readiness, type Options, patchV0CityByCityName, patchV0CityByCityNameAgentByBase, patchV0CityByCityNameAgentByDirByBase, patchV0CityByCityNameBeadById, patchV0CityByCityNameProviderByName, patchV0CityByCityNameRigByName, patchV0CityByCityNameSessionById, postV0City, postV0CityByCityNameAgentByBaseByAction, postV0CityByCityNameAgentByDirByBaseByAction, postV0CityByCityNameBeadByIdAssign, postV0CityByCityNameBeadByIdClose, postV0CityByCityNameBeadByIdReopen, postV0CityByCityNameBeadByIdUpdate, postV0CityByCityNameConvoyByIdAdd, postV0CityByCityNameConvoyByIdClose, postV0CityByCityNameConvoyByIdRemove, postV0CityByCityNameExtmsgBind, postV0CityByCityNameExtmsgInbound, postV0CityByCityNameExtmsgOutbound, postV0CityByCityNameExtmsgParticipants, postV0CityByCityNameExtmsgTranscriptAck, postV0CityByCityNameExtmsgUnbind, postV0CityByCityNameFormulasByNamePreview, postV0CityByCityNameMailByIdArchive, postV0CityByCityNameMailByIdMarkUnread, postV0CityByCityNameMailByIdRead, postV0CityByCityNameOrderByNameDisable, postV0CityByCityNameOrderByNameEnable, postV0CityByCityNameRigByNameByAction, postV0CityByCityNameServiceByNameRestart, postV0CityByCityNameSessionByIdClose, postV0CityByCityNameSessionByIdKill, postV0CityByCityNameSessionByIdPermissionMode, postV0CityByCityNameSessionByIdRename, postV0CityByCityNameSessionByIdStop, postV0CityByCityNameSessionByIdSuspend, postV0CityByCityNameSessionByIdWake, postV0CityByCityNameSling, postV0CityByCityNameUnregister, putV0CityByCityNamePatchesAgents, putV0CityByCityNamePatchesProviders, putV0CityByCityNamePatchesRigs, registerExtmsgAdapter, replyMail, respondSession, sendMail, sendSessionMessage, streamAgentOutput, streamAgentOutputQualified, streamEvents, streamSession, streamSupervisorEvents, submitSession } from './sdk.gen'; +export type { AdapterCapabilities, AdapterEventPayload, AgentCreatedOutputBody, AgentCreateInputBody, AgentMapping, AgentOutputResponse, AgentPatch, AgentPatchSetInputBody, AgentResponse, AgentUpdateInputBody, AgentUpdateQualifiedInputBody, AnnotatedAgentResponse, AnnotatedProviderResponse, AsyncAcceptedBody, AsyncAcceptedResponse, Bead, BeadAssignInputBody, BeadCreateInputBody, BeadDepsResponse, BeadEventPayload, BeadGraphResponse, BeadUpdateBody, BindingStatus, BoundEventPayload, CityCreateRequest, CityCreateSucceededPayload, CityGetResponse, CityInfo, CityLifecyclePayload, CityPatchInputBody, CityUnregisterSucceededPayload, ClientOptions, ConfigAgentResponse, ConfigExplainPatches, ConfigExplainResponse, ConfigPatchesResponse, ConfigResponse, ConfigRigResponse, ConfigValidateOutputBody, ConversationGroupParticipant, ConversationGroupRecord, ConversationKind, ConversationRef, ConversationTranscriptRecord, ConvoyAddInputBody, ConvoyCheckResponse, ConvoyCreateInputBody, ConvoyGetResponse, ConvoyProgress, ConvoyRemoveInputBody, CreateAgentData, CreateAgentError, CreateAgentErrors, CreateAgentResponse, CreateAgentResponses, CreateBeadData, CreateBeadError, CreateBeadErrors, CreateBeadResponse, CreateBeadResponses, CreateConvoyData, CreateConvoyError, CreateConvoyErrors, CreateConvoyResponse, CreateConvoyResponses, CreateProviderData, CreateProviderError, CreateProviderErrors, CreateProviderResponse, CreateProviderResponses, CreateRigData, CreateRigError, CreateRigErrors, CreateRigResponse, CreateRigResponses, CreateSessionData, CreateSessionError, CreateSessionErrors, CreateSessionResponse, CreateSessionResponses, DeleteV0CityByCityNameAgentByBaseData, DeleteV0CityByCityNameAgentByBaseError, DeleteV0CityByCityNameAgentByBaseErrors, DeleteV0CityByCityNameAgentByBaseResponse, DeleteV0CityByCityNameAgentByBaseResponses, DeleteV0CityByCityNameAgentByDirByBaseData, DeleteV0CityByCityNameAgentByDirByBaseError, DeleteV0CityByCityNameAgentByDirByBaseErrors, DeleteV0CityByCityNameAgentByDirByBaseResponse, DeleteV0CityByCityNameAgentByDirByBaseResponses, DeleteV0CityByCityNameBeadByIdData, DeleteV0CityByCityNameBeadByIdError, DeleteV0CityByCityNameBeadByIdErrors, DeleteV0CityByCityNameBeadByIdResponse, DeleteV0CityByCityNameBeadByIdResponses, DeleteV0CityByCityNameConvoyByIdData, DeleteV0CityByCityNameConvoyByIdError, DeleteV0CityByCityNameConvoyByIdErrors, DeleteV0CityByCityNameConvoyByIdResponse, DeleteV0CityByCityNameConvoyByIdResponses, DeleteV0CityByCityNameExtmsgAdaptersData, DeleteV0CityByCityNameExtmsgAdaptersError, DeleteV0CityByCityNameExtmsgAdaptersErrors, DeleteV0CityByCityNameExtmsgAdaptersResponse, DeleteV0CityByCityNameExtmsgAdaptersResponses, DeleteV0CityByCityNameExtmsgParticipantsData, DeleteV0CityByCityNameExtmsgParticipantsError, DeleteV0CityByCityNameExtmsgParticipantsErrors, DeleteV0CityByCityNameExtmsgParticipantsResponse, DeleteV0CityByCityNameExtmsgParticipantsResponses, DeleteV0CityByCityNameMailByIdData, DeleteV0CityByCityNameMailByIdError, DeleteV0CityByCityNameMailByIdErrors, DeleteV0CityByCityNameMailByIdResponse, DeleteV0CityByCityNameMailByIdResponses, DeleteV0CityByCityNamePatchesAgentByBaseData, DeleteV0CityByCityNamePatchesAgentByBaseError, DeleteV0CityByCityNamePatchesAgentByBaseErrors, DeleteV0CityByCityNamePatchesAgentByBaseResponse, DeleteV0CityByCityNamePatchesAgentByBaseResponses, DeleteV0CityByCityNamePatchesAgentByDirByBaseData, DeleteV0CityByCityNamePatchesAgentByDirByBaseError, DeleteV0CityByCityNamePatchesAgentByDirByBaseErrors, DeleteV0CityByCityNamePatchesAgentByDirByBaseResponse, DeleteV0CityByCityNamePatchesAgentByDirByBaseResponses, DeleteV0CityByCityNamePatchesProviderByNameData, DeleteV0CityByCityNamePatchesProviderByNameError, DeleteV0CityByCityNamePatchesProviderByNameErrors, DeleteV0CityByCityNamePatchesProviderByNameResponse, DeleteV0CityByCityNamePatchesProviderByNameResponses, DeleteV0CityByCityNamePatchesRigByNameData, DeleteV0CityByCityNamePatchesRigByNameError, DeleteV0CityByCityNamePatchesRigByNameErrors, DeleteV0CityByCityNamePatchesRigByNameResponse, DeleteV0CityByCityNamePatchesRigByNameResponses, DeleteV0CityByCityNameProviderByNameData, DeleteV0CityByCityNameProviderByNameError, DeleteV0CityByCityNameProviderByNameErrors, DeleteV0CityByCityNameProviderByNameResponse, DeleteV0CityByCityNameProviderByNameResponses, DeleteV0CityByCityNameRigByNameData, DeleteV0CityByCityNameRigByNameError, DeleteV0CityByCityNameRigByNameErrors, DeleteV0CityByCityNameRigByNameResponse, DeleteV0CityByCityNameRigByNameResponses, DeleteV0CityByCityNameWorkflowByWorkflowIdData, DeleteV0CityByCityNameWorkflowByWorkflowIdError, DeleteV0CityByCityNameWorkflowByWorkflowIdErrors, DeleteV0CityByCityNameWorkflowByWorkflowIdResponse, DeleteV0CityByCityNameWorkflowByWorkflowIdResponses, DeliveryContextRecord, Dep, EmitEventData, EmitEventError, EmitEventErrors, EmitEventResponse, EmitEventResponses, EnsureExtmsgGroupData, EnsureExtmsgGroupError, EnsureExtmsgGroupErrors, EnsureExtmsgGroupResponse, EnsureExtmsgGroupResponses, ErrorDetail, ErrorModel, EventEmitOutputBody, EventEmitRequest, EventPayload, EventStreamEnvelope, ExternalActor, ExternalAttachment, ExternalInboundMessage, ExtmsgAdapterInfo, ExtMsgAdapterRegisterInputBody, ExtMsgAdapterRegisterOutputBody, ExtMsgAdapterUnregisterInputBody, ExtMsgBindInputBody, ExtMsgGroupEnsureInputBody, ExtMsgInboundInputBody, ExtMsgOutboundInputBody, ExtMsgParticipantRemoveInputBody, ExtMsgParticipantUpsertInputBody, ExtMsgTranscriptAckInputBody, ExtMsgUnbindBody, ExtMsgUnbindInputBody, FanoutPolicy, FormulaDetailResponse, FormulaFeedBody, FormulaListBody, FormulaPreviewBody, FormulaPreviewEdgeResponse, FormulaPreviewNodeResponse, FormulaPreviewResponse, FormulaRecentRunResponse, FormulaRunsResponse, FormulaStepResponse, FormulaSummaryResponse, FormulaVarDefResponse, GetHealthData, GetHealthError, GetHealthErrors, GetHealthResponse, GetHealthResponses, GetV0CitiesData, GetV0CitiesError, GetV0CitiesErrors, GetV0CitiesResponse, GetV0CitiesResponses, GetV0CityByCityNameAgentByBaseData, GetV0CityByCityNameAgentByBaseError, GetV0CityByCityNameAgentByBaseErrors, GetV0CityByCityNameAgentByBaseOutputData, GetV0CityByCityNameAgentByBaseOutputError, GetV0CityByCityNameAgentByBaseOutputErrors, GetV0CityByCityNameAgentByBaseOutputResponse, GetV0CityByCityNameAgentByBaseOutputResponses, GetV0CityByCityNameAgentByBaseResponse, GetV0CityByCityNameAgentByBaseResponses, GetV0CityByCityNameAgentByDirByBaseData, GetV0CityByCityNameAgentByDirByBaseError, GetV0CityByCityNameAgentByDirByBaseErrors, GetV0CityByCityNameAgentByDirByBaseOutputData, GetV0CityByCityNameAgentByDirByBaseOutputError, GetV0CityByCityNameAgentByDirByBaseOutputErrors, GetV0CityByCityNameAgentByDirByBaseOutputResponse, GetV0CityByCityNameAgentByDirByBaseOutputResponses, GetV0CityByCityNameAgentByDirByBaseResponse, GetV0CityByCityNameAgentByDirByBaseResponses, GetV0CityByCityNameAgentsData, GetV0CityByCityNameAgentsError, GetV0CityByCityNameAgentsErrors, GetV0CityByCityNameAgentsResponse, GetV0CityByCityNameAgentsResponses, GetV0CityByCityNameBeadByIdData, GetV0CityByCityNameBeadByIdDepsData, GetV0CityByCityNameBeadByIdDepsError, GetV0CityByCityNameBeadByIdDepsErrors, GetV0CityByCityNameBeadByIdDepsResponse, GetV0CityByCityNameBeadByIdDepsResponses, GetV0CityByCityNameBeadByIdError, GetV0CityByCityNameBeadByIdErrors, GetV0CityByCityNameBeadByIdResponse, GetV0CityByCityNameBeadByIdResponses, GetV0CityByCityNameBeadsData, GetV0CityByCityNameBeadsError, GetV0CityByCityNameBeadsErrors, GetV0CityByCityNameBeadsGraphByRootIdData, GetV0CityByCityNameBeadsGraphByRootIdError, GetV0CityByCityNameBeadsGraphByRootIdErrors, GetV0CityByCityNameBeadsGraphByRootIdResponse, GetV0CityByCityNameBeadsGraphByRootIdResponses, GetV0CityByCityNameBeadsReadyData, GetV0CityByCityNameBeadsReadyError, GetV0CityByCityNameBeadsReadyErrors, GetV0CityByCityNameBeadsReadyResponse, GetV0CityByCityNameBeadsReadyResponses, GetV0CityByCityNameBeadsResponse, GetV0CityByCityNameBeadsResponses, GetV0CityByCityNameConfigData, GetV0CityByCityNameConfigError, GetV0CityByCityNameConfigErrors, GetV0CityByCityNameConfigExplainData, GetV0CityByCityNameConfigExplainError, GetV0CityByCityNameConfigExplainErrors, GetV0CityByCityNameConfigExplainResponse, GetV0CityByCityNameConfigExplainResponses, GetV0CityByCityNameConfigResponse, GetV0CityByCityNameConfigResponses, GetV0CityByCityNameConfigValidateData, GetV0CityByCityNameConfigValidateError, GetV0CityByCityNameConfigValidateErrors, GetV0CityByCityNameConfigValidateResponse, GetV0CityByCityNameConfigValidateResponses, GetV0CityByCityNameConvoyByIdCheckData, GetV0CityByCityNameConvoyByIdCheckError, GetV0CityByCityNameConvoyByIdCheckErrors, GetV0CityByCityNameConvoyByIdCheckResponse, GetV0CityByCityNameConvoyByIdCheckResponses, GetV0CityByCityNameConvoyByIdData, GetV0CityByCityNameConvoyByIdError, GetV0CityByCityNameConvoyByIdErrors, GetV0CityByCityNameConvoyByIdResponse, GetV0CityByCityNameConvoyByIdResponses, GetV0CityByCityNameConvoysData, GetV0CityByCityNameConvoysError, GetV0CityByCityNameConvoysErrors, GetV0CityByCityNameConvoysResponse, GetV0CityByCityNameConvoysResponses, GetV0CityByCityNameData, GetV0CityByCityNameError, GetV0CityByCityNameErrors, GetV0CityByCityNameEventsData, GetV0CityByCityNameEventsError, GetV0CityByCityNameEventsErrors, GetV0CityByCityNameEventsResponse, GetV0CityByCityNameEventsResponses, GetV0CityByCityNameExtmsgAdaptersData, GetV0CityByCityNameExtmsgAdaptersError, GetV0CityByCityNameExtmsgAdaptersErrors, GetV0CityByCityNameExtmsgAdaptersResponse, GetV0CityByCityNameExtmsgAdaptersResponses, GetV0CityByCityNameExtmsgBindingsData, GetV0CityByCityNameExtmsgBindingsError, GetV0CityByCityNameExtmsgBindingsErrors, GetV0CityByCityNameExtmsgBindingsResponse, GetV0CityByCityNameExtmsgBindingsResponses, GetV0CityByCityNameExtmsgGroupsData, GetV0CityByCityNameExtmsgGroupsError, GetV0CityByCityNameExtmsgGroupsErrors, GetV0CityByCityNameExtmsgGroupsResponse, GetV0CityByCityNameExtmsgGroupsResponses, GetV0CityByCityNameExtmsgTranscriptData, GetV0CityByCityNameExtmsgTranscriptError, GetV0CityByCityNameExtmsgTranscriptErrors, GetV0CityByCityNameExtmsgTranscriptResponse, GetV0CityByCityNameExtmsgTranscriptResponses, GetV0CityByCityNameFormulaByNameData, GetV0CityByCityNameFormulaByNameError, GetV0CityByCityNameFormulaByNameErrors, GetV0CityByCityNameFormulaByNameResponse, GetV0CityByCityNameFormulaByNameResponses, GetV0CityByCityNameFormulasByNameData, GetV0CityByCityNameFormulasByNameError, GetV0CityByCityNameFormulasByNameErrors, GetV0CityByCityNameFormulasByNameResponse, GetV0CityByCityNameFormulasByNameResponses, GetV0CityByCityNameFormulasByNameRunsData, GetV0CityByCityNameFormulasByNameRunsError, GetV0CityByCityNameFormulasByNameRunsErrors, GetV0CityByCityNameFormulasByNameRunsResponse, GetV0CityByCityNameFormulasByNameRunsResponses, GetV0CityByCityNameFormulasData, GetV0CityByCityNameFormulasError, GetV0CityByCityNameFormulasErrors, GetV0CityByCityNameFormulasFeedData, GetV0CityByCityNameFormulasFeedError, GetV0CityByCityNameFormulasFeedErrors, GetV0CityByCityNameFormulasFeedResponse, GetV0CityByCityNameFormulasFeedResponses, GetV0CityByCityNameFormulasResponse, GetV0CityByCityNameFormulasResponses, GetV0CityByCityNameHealthData, GetV0CityByCityNameHealthError, GetV0CityByCityNameHealthErrors, GetV0CityByCityNameHealthResponse, GetV0CityByCityNameHealthResponses, GetV0CityByCityNameMailByIdData, GetV0CityByCityNameMailByIdError, GetV0CityByCityNameMailByIdErrors, GetV0CityByCityNameMailByIdResponse, GetV0CityByCityNameMailByIdResponses, GetV0CityByCityNameMailCountData, GetV0CityByCityNameMailCountError, GetV0CityByCityNameMailCountErrors, GetV0CityByCityNameMailCountResponse, GetV0CityByCityNameMailCountResponses, GetV0CityByCityNameMailData, GetV0CityByCityNameMailError, GetV0CityByCityNameMailErrors, GetV0CityByCityNameMailResponse, GetV0CityByCityNameMailResponses, GetV0CityByCityNameMailThreadByIdData, GetV0CityByCityNameMailThreadByIdError, GetV0CityByCityNameMailThreadByIdErrors, GetV0CityByCityNameMailThreadByIdResponse, GetV0CityByCityNameMailThreadByIdResponses, GetV0CityByCityNameOrderByNameData, GetV0CityByCityNameOrderByNameError, GetV0CityByCityNameOrderByNameErrors, GetV0CityByCityNameOrderByNameResponse, GetV0CityByCityNameOrderByNameResponses, GetV0CityByCityNameOrderHistoryByBeadIdData, GetV0CityByCityNameOrderHistoryByBeadIdError, GetV0CityByCityNameOrderHistoryByBeadIdErrors, GetV0CityByCityNameOrderHistoryByBeadIdResponse, GetV0CityByCityNameOrderHistoryByBeadIdResponses, GetV0CityByCityNameOrdersCheckData, GetV0CityByCityNameOrdersCheckError, GetV0CityByCityNameOrdersCheckErrors, GetV0CityByCityNameOrdersCheckResponse, GetV0CityByCityNameOrdersCheckResponses, GetV0CityByCityNameOrdersData, GetV0CityByCityNameOrdersError, GetV0CityByCityNameOrdersErrors, GetV0CityByCityNameOrdersFeedData, GetV0CityByCityNameOrdersFeedError, GetV0CityByCityNameOrdersFeedErrors, GetV0CityByCityNameOrdersFeedResponse, GetV0CityByCityNameOrdersFeedResponses, GetV0CityByCityNameOrdersHistoryData, GetV0CityByCityNameOrdersHistoryError, GetV0CityByCityNameOrdersHistoryErrors, GetV0CityByCityNameOrdersHistoryResponse, GetV0CityByCityNameOrdersHistoryResponses, GetV0CityByCityNameOrdersResponse, GetV0CityByCityNameOrdersResponses, GetV0CityByCityNamePacksData, GetV0CityByCityNamePacksError, GetV0CityByCityNamePacksErrors, GetV0CityByCityNamePacksResponse, GetV0CityByCityNamePacksResponses, GetV0CityByCityNamePatchesAgentByBaseData, GetV0CityByCityNamePatchesAgentByBaseError, GetV0CityByCityNamePatchesAgentByBaseErrors, GetV0CityByCityNamePatchesAgentByBaseResponse, GetV0CityByCityNamePatchesAgentByBaseResponses, GetV0CityByCityNamePatchesAgentByDirByBaseData, GetV0CityByCityNamePatchesAgentByDirByBaseError, GetV0CityByCityNamePatchesAgentByDirByBaseErrors, GetV0CityByCityNamePatchesAgentByDirByBaseResponse, GetV0CityByCityNamePatchesAgentByDirByBaseResponses, GetV0CityByCityNamePatchesAgentsData, GetV0CityByCityNamePatchesAgentsError, GetV0CityByCityNamePatchesAgentsErrors, GetV0CityByCityNamePatchesAgentsResponse, GetV0CityByCityNamePatchesAgentsResponses, GetV0CityByCityNamePatchesProviderByNameData, GetV0CityByCityNamePatchesProviderByNameError, GetV0CityByCityNamePatchesProviderByNameErrors, GetV0CityByCityNamePatchesProviderByNameResponse, GetV0CityByCityNamePatchesProviderByNameResponses, GetV0CityByCityNamePatchesProvidersData, GetV0CityByCityNamePatchesProvidersError, GetV0CityByCityNamePatchesProvidersErrors, GetV0CityByCityNamePatchesProvidersResponse, GetV0CityByCityNamePatchesProvidersResponses, GetV0CityByCityNamePatchesRigByNameData, GetV0CityByCityNamePatchesRigByNameError, GetV0CityByCityNamePatchesRigByNameErrors, GetV0CityByCityNamePatchesRigByNameResponse, GetV0CityByCityNamePatchesRigByNameResponses, GetV0CityByCityNamePatchesRigsData, GetV0CityByCityNamePatchesRigsError, GetV0CityByCityNamePatchesRigsErrors, GetV0CityByCityNamePatchesRigsResponse, GetV0CityByCityNamePatchesRigsResponses, GetV0CityByCityNameProviderByNameData, GetV0CityByCityNameProviderByNameError, GetV0CityByCityNameProviderByNameErrors, GetV0CityByCityNameProviderByNameResponse, GetV0CityByCityNameProviderByNameResponses, GetV0CityByCityNameProviderReadinessData, GetV0CityByCityNameProviderReadinessError, GetV0CityByCityNameProviderReadinessErrors, GetV0CityByCityNameProviderReadinessResponse, GetV0CityByCityNameProviderReadinessResponses, GetV0CityByCityNameProvidersData, GetV0CityByCityNameProvidersError, GetV0CityByCityNameProvidersErrors, GetV0CityByCityNameProvidersPublicData, GetV0CityByCityNameProvidersPublicError, GetV0CityByCityNameProvidersPublicErrors, GetV0CityByCityNameProvidersPublicResponse, GetV0CityByCityNameProvidersPublicResponses, GetV0CityByCityNameProvidersResponse, GetV0CityByCityNameProvidersResponses, GetV0CityByCityNameReadinessData, GetV0CityByCityNameReadinessError, GetV0CityByCityNameReadinessErrors, GetV0CityByCityNameReadinessResponse, GetV0CityByCityNameReadinessResponses, GetV0CityByCityNameResponse, GetV0CityByCityNameResponses, GetV0CityByCityNameRigByNameData, GetV0CityByCityNameRigByNameError, GetV0CityByCityNameRigByNameErrors, GetV0CityByCityNameRigByNameResponse, GetV0CityByCityNameRigByNameResponses, GetV0CityByCityNameRigsData, GetV0CityByCityNameRigsError, GetV0CityByCityNameRigsErrors, GetV0CityByCityNameRigsResponse, GetV0CityByCityNameRigsResponses, GetV0CityByCityNameServiceByNameData, GetV0CityByCityNameServiceByNameError, GetV0CityByCityNameServiceByNameErrors, GetV0CityByCityNameServiceByNameResponse, GetV0CityByCityNameServiceByNameResponses, GetV0CityByCityNameServicesData, GetV0CityByCityNameServicesError, GetV0CityByCityNameServicesErrors, GetV0CityByCityNameServicesResponse, GetV0CityByCityNameServicesResponses, GetV0CityByCityNameSessionByIdAgentsByAgentIdData, GetV0CityByCityNameSessionByIdAgentsByAgentIdError, GetV0CityByCityNameSessionByIdAgentsByAgentIdErrors, GetV0CityByCityNameSessionByIdAgentsByAgentIdResponse, GetV0CityByCityNameSessionByIdAgentsByAgentIdResponses, GetV0CityByCityNameSessionByIdAgentsData, GetV0CityByCityNameSessionByIdAgentsError, GetV0CityByCityNameSessionByIdAgentsErrors, GetV0CityByCityNameSessionByIdAgentsResponse, GetV0CityByCityNameSessionByIdAgentsResponses, GetV0CityByCityNameSessionByIdData, GetV0CityByCityNameSessionByIdError, GetV0CityByCityNameSessionByIdErrors, GetV0CityByCityNameSessionByIdPendingData, GetV0CityByCityNameSessionByIdPendingError, GetV0CityByCityNameSessionByIdPendingErrors, GetV0CityByCityNameSessionByIdPendingResponse, GetV0CityByCityNameSessionByIdPendingResponses, GetV0CityByCityNameSessionByIdResponse, GetV0CityByCityNameSessionByIdResponses, GetV0CityByCityNameSessionByIdTranscriptData, GetV0CityByCityNameSessionByIdTranscriptError, GetV0CityByCityNameSessionByIdTranscriptErrors, GetV0CityByCityNameSessionByIdTranscriptResponse, GetV0CityByCityNameSessionByIdTranscriptResponses, GetV0CityByCityNameSessionsData, GetV0CityByCityNameSessionsError, GetV0CityByCityNameSessionsErrors, GetV0CityByCityNameSessionsResponse, GetV0CityByCityNameSessionsResponses, GetV0CityByCityNameStatusData, GetV0CityByCityNameStatusError, GetV0CityByCityNameStatusErrors, GetV0CityByCityNameStatusResponse, GetV0CityByCityNameStatusResponses, GetV0CityByCityNameWorkflowByWorkflowIdData, GetV0CityByCityNameWorkflowByWorkflowIdError, GetV0CityByCityNameWorkflowByWorkflowIdErrors, GetV0CityByCityNameWorkflowByWorkflowIdResponse, GetV0CityByCityNameWorkflowByWorkflowIdResponses, GetV0EventsData, GetV0EventsError, GetV0EventsErrors, GetV0EventsResponse, GetV0EventsResponses, GetV0ProviderReadinessData, GetV0ProviderReadinessError, GetV0ProviderReadinessErrors, GetV0ProviderReadinessResponse, GetV0ProviderReadinessResponses, GetV0ReadinessData, GetV0ReadinessError, GetV0ReadinessErrors, GetV0ReadinessResponse, GetV0ReadinessResponses, GitStatus, GroupCreatedEventPayload, GroupRouteDecision, HealthOutputBody, HeartbeatEvent, InboundEventPayload, InboundResult, ListBodyAgentPatch, ListBodyAgentResponse, ListBodyBead, ListBodyConversationTranscriptRecord, ListBodyExtmsgAdapterInfo, ListBodyProviderPatch, ListBodyProviderResponse, ListBodyRigPatch, ListBodyRigResponse, ListBodySessionBindingRecord, ListBodySessionResponse, ListBodyStatus, ListBodyWireEvent, LogicalNode, MailCountOutputBody, MailEventPayload, MailListBody, MailReplyInputBody, MailSendInputBody, Message, MonitorFeedItemResponse, NoPayload, OkResponseBody, OkWithIdResponseBody, OptionChoiceDto, OrderCheckListBody, OrderCheckResponse, OrderHistoryDetailResponse, OrderHistoryEntry, OrderHistoryListBody, OrderListBody, OrderResponse, OrdersFeedBody, OutboundEventPayload, OutboundResult, OutputTurn, PackListBody, PackResponse, PaginationInfo, PatchDeletedResponseBody, PatchOkResponseBody, PatchV0CityByCityNameAgentByBaseData, PatchV0CityByCityNameAgentByBaseError, PatchV0CityByCityNameAgentByBaseErrors, PatchV0CityByCityNameAgentByBaseResponse, PatchV0CityByCityNameAgentByBaseResponses, PatchV0CityByCityNameAgentByDirByBaseData, PatchV0CityByCityNameAgentByDirByBaseError, PatchV0CityByCityNameAgentByDirByBaseErrors, PatchV0CityByCityNameAgentByDirByBaseResponse, PatchV0CityByCityNameAgentByDirByBaseResponses, PatchV0CityByCityNameBeadByIdData, PatchV0CityByCityNameBeadByIdError, PatchV0CityByCityNameBeadByIdErrors, PatchV0CityByCityNameBeadByIdResponse, PatchV0CityByCityNameBeadByIdResponses, PatchV0CityByCityNameData, PatchV0CityByCityNameError, PatchV0CityByCityNameErrors, PatchV0CityByCityNameProviderByNameData, PatchV0CityByCityNameProviderByNameError, PatchV0CityByCityNameProviderByNameErrors, PatchV0CityByCityNameProviderByNameResponse, PatchV0CityByCityNameProviderByNameResponses, PatchV0CityByCityNameResponse, PatchV0CityByCityNameResponses, PatchV0CityByCityNameRigByNameData, PatchV0CityByCityNameRigByNameError, PatchV0CityByCityNameRigByNameErrors, PatchV0CityByCityNameRigByNameResponse, PatchV0CityByCityNameRigByNameResponses, PatchV0CityByCityNameSessionByIdData, PatchV0CityByCityNameSessionByIdError, PatchV0CityByCityNameSessionByIdErrors, PatchV0CityByCityNameSessionByIdResponse, PatchV0CityByCityNameSessionByIdResponses, PendingInteraction, PoolOverride, PostV0CityByCityNameAgentByBaseByActionData, PostV0CityByCityNameAgentByBaseByActionError, PostV0CityByCityNameAgentByBaseByActionErrors, PostV0CityByCityNameAgentByBaseByActionResponse, PostV0CityByCityNameAgentByBaseByActionResponses, PostV0CityByCityNameAgentByDirByBaseByActionData, PostV0CityByCityNameAgentByDirByBaseByActionError, PostV0CityByCityNameAgentByDirByBaseByActionErrors, PostV0CityByCityNameAgentByDirByBaseByActionResponse, PostV0CityByCityNameAgentByDirByBaseByActionResponses, PostV0CityByCityNameBeadByIdAssignData, PostV0CityByCityNameBeadByIdAssignError, PostV0CityByCityNameBeadByIdAssignErrors, PostV0CityByCityNameBeadByIdAssignResponse, PostV0CityByCityNameBeadByIdAssignResponses, PostV0CityByCityNameBeadByIdCloseData, PostV0CityByCityNameBeadByIdCloseError, PostV0CityByCityNameBeadByIdCloseErrors, PostV0CityByCityNameBeadByIdCloseResponse, PostV0CityByCityNameBeadByIdCloseResponses, PostV0CityByCityNameBeadByIdReopenData, PostV0CityByCityNameBeadByIdReopenError, PostV0CityByCityNameBeadByIdReopenErrors, PostV0CityByCityNameBeadByIdReopenResponse, PostV0CityByCityNameBeadByIdReopenResponses, PostV0CityByCityNameBeadByIdUpdateData, PostV0CityByCityNameBeadByIdUpdateError, PostV0CityByCityNameBeadByIdUpdateErrors, PostV0CityByCityNameBeadByIdUpdateResponse, PostV0CityByCityNameBeadByIdUpdateResponses, PostV0CityByCityNameConvoyByIdAddData, PostV0CityByCityNameConvoyByIdAddError, PostV0CityByCityNameConvoyByIdAddErrors, PostV0CityByCityNameConvoyByIdAddResponse, PostV0CityByCityNameConvoyByIdAddResponses, PostV0CityByCityNameConvoyByIdCloseData, PostV0CityByCityNameConvoyByIdCloseError, PostV0CityByCityNameConvoyByIdCloseErrors, PostV0CityByCityNameConvoyByIdCloseResponse, PostV0CityByCityNameConvoyByIdCloseResponses, PostV0CityByCityNameConvoyByIdRemoveData, PostV0CityByCityNameConvoyByIdRemoveError, PostV0CityByCityNameConvoyByIdRemoveErrors, PostV0CityByCityNameConvoyByIdRemoveResponse, PostV0CityByCityNameConvoyByIdRemoveResponses, PostV0CityByCityNameExtmsgBindData, PostV0CityByCityNameExtmsgBindError, PostV0CityByCityNameExtmsgBindErrors, PostV0CityByCityNameExtmsgBindResponse, PostV0CityByCityNameExtmsgBindResponses, PostV0CityByCityNameExtmsgInboundData, PostV0CityByCityNameExtmsgInboundError, PostV0CityByCityNameExtmsgInboundErrors, PostV0CityByCityNameExtmsgInboundResponse, PostV0CityByCityNameExtmsgInboundResponses, PostV0CityByCityNameExtmsgOutboundData, PostV0CityByCityNameExtmsgOutboundError, PostV0CityByCityNameExtmsgOutboundErrors, PostV0CityByCityNameExtmsgOutboundResponse, PostV0CityByCityNameExtmsgOutboundResponses, PostV0CityByCityNameExtmsgParticipantsData, PostV0CityByCityNameExtmsgParticipantsError, PostV0CityByCityNameExtmsgParticipantsErrors, PostV0CityByCityNameExtmsgParticipantsResponse, PostV0CityByCityNameExtmsgParticipantsResponses, PostV0CityByCityNameExtmsgTranscriptAckData, PostV0CityByCityNameExtmsgTranscriptAckError, PostV0CityByCityNameExtmsgTranscriptAckErrors, PostV0CityByCityNameExtmsgTranscriptAckResponse, PostV0CityByCityNameExtmsgTranscriptAckResponses, PostV0CityByCityNameExtmsgUnbindData, PostV0CityByCityNameExtmsgUnbindError, PostV0CityByCityNameExtmsgUnbindErrors, PostV0CityByCityNameExtmsgUnbindResponse, PostV0CityByCityNameExtmsgUnbindResponses, PostV0CityByCityNameFormulasByNamePreviewData, PostV0CityByCityNameFormulasByNamePreviewError, PostV0CityByCityNameFormulasByNamePreviewErrors, PostV0CityByCityNameFormulasByNamePreviewResponse, PostV0CityByCityNameFormulasByNamePreviewResponses, PostV0CityByCityNameMailByIdArchiveData, PostV0CityByCityNameMailByIdArchiveError, PostV0CityByCityNameMailByIdArchiveErrors, PostV0CityByCityNameMailByIdArchiveResponse, PostV0CityByCityNameMailByIdArchiveResponses, PostV0CityByCityNameMailByIdMarkUnreadData, PostV0CityByCityNameMailByIdMarkUnreadError, PostV0CityByCityNameMailByIdMarkUnreadErrors, PostV0CityByCityNameMailByIdMarkUnreadResponse, PostV0CityByCityNameMailByIdMarkUnreadResponses, PostV0CityByCityNameMailByIdReadData, PostV0CityByCityNameMailByIdReadError, PostV0CityByCityNameMailByIdReadErrors, PostV0CityByCityNameMailByIdReadResponse, PostV0CityByCityNameMailByIdReadResponses, PostV0CityByCityNameOrderByNameDisableData, PostV0CityByCityNameOrderByNameDisableError, PostV0CityByCityNameOrderByNameDisableErrors, PostV0CityByCityNameOrderByNameDisableResponse, PostV0CityByCityNameOrderByNameDisableResponses, PostV0CityByCityNameOrderByNameEnableData, PostV0CityByCityNameOrderByNameEnableError, PostV0CityByCityNameOrderByNameEnableErrors, PostV0CityByCityNameOrderByNameEnableResponse, PostV0CityByCityNameOrderByNameEnableResponses, PostV0CityByCityNameRigByNameByActionData, PostV0CityByCityNameRigByNameByActionError, PostV0CityByCityNameRigByNameByActionErrors, PostV0CityByCityNameRigByNameByActionResponse, PostV0CityByCityNameRigByNameByActionResponses, PostV0CityByCityNameServiceByNameRestartData, PostV0CityByCityNameServiceByNameRestartError, PostV0CityByCityNameServiceByNameRestartErrors, PostV0CityByCityNameServiceByNameRestartResponse, PostV0CityByCityNameServiceByNameRestartResponses, PostV0CityByCityNameSessionByIdCloseData, PostV0CityByCityNameSessionByIdCloseError, PostV0CityByCityNameSessionByIdCloseErrors, PostV0CityByCityNameSessionByIdCloseResponse, PostV0CityByCityNameSessionByIdCloseResponses, PostV0CityByCityNameSessionByIdKillData, PostV0CityByCityNameSessionByIdKillError, PostV0CityByCityNameSessionByIdKillErrors, PostV0CityByCityNameSessionByIdKillResponse, PostV0CityByCityNameSessionByIdKillResponses, PostV0CityByCityNameSessionByIdPermissionModeData, PostV0CityByCityNameSessionByIdPermissionModeError, PostV0CityByCityNameSessionByIdPermissionModeErrors, PostV0CityByCityNameSessionByIdPermissionModeResponse, PostV0CityByCityNameSessionByIdPermissionModeResponses, PostV0CityByCityNameSessionByIdRenameData, PostV0CityByCityNameSessionByIdRenameError, PostV0CityByCityNameSessionByIdRenameErrors, PostV0CityByCityNameSessionByIdRenameResponse, PostV0CityByCityNameSessionByIdRenameResponses, PostV0CityByCityNameSessionByIdStopData, PostV0CityByCityNameSessionByIdStopError, PostV0CityByCityNameSessionByIdStopErrors, PostV0CityByCityNameSessionByIdStopResponse, PostV0CityByCityNameSessionByIdStopResponses, PostV0CityByCityNameSessionByIdSuspendData, PostV0CityByCityNameSessionByIdSuspendError, PostV0CityByCityNameSessionByIdSuspendErrors, PostV0CityByCityNameSessionByIdSuspendResponse, PostV0CityByCityNameSessionByIdSuspendResponses, PostV0CityByCityNameSessionByIdWakeData, PostV0CityByCityNameSessionByIdWakeError, PostV0CityByCityNameSessionByIdWakeErrors, PostV0CityByCityNameSessionByIdWakeResponse, PostV0CityByCityNameSessionByIdWakeResponses, PostV0CityByCityNameSlingData, PostV0CityByCityNameSlingError, PostV0CityByCityNameSlingErrors, PostV0CityByCityNameSlingResponse, PostV0CityByCityNameSlingResponses, PostV0CityByCityNameUnregisterData, PostV0CityByCityNameUnregisterError, PostV0CityByCityNameUnregisterErrors, PostV0CityByCityNameUnregisterResponse, PostV0CityByCityNameUnregisterResponses, PostV0CityData, PostV0CityError, PostV0CityErrors, PostV0CityResponse, PostV0CityResponses, ProjectIdentityStampedPayload, ProviderCreatedOutputBody, ProviderCreateInputBody, ProviderOptionDto, ProviderPatch, ProviderPatchSetInputBody, ProviderPublicListBody, ProviderPublicResponse, ProviderReadiness, ProviderReadinessResponse, ProviderResponse, ProviderSpecJson, ProviderUpdateInputBody, PublishReceipt, PutV0CityByCityNamePatchesAgentsData, PutV0CityByCityNamePatchesAgentsError, PutV0CityByCityNamePatchesAgentsErrors, PutV0CityByCityNamePatchesAgentsResponse, PutV0CityByCityNamePatchesAgentsResponses, PutV0CityByCityNamePatchesProvidersData, PutV0CityByCityNamePatchesProvidersError, PutV0CityByCityNamePatchesProvidersErrors, PutV0CityByCityNamePatchesProvidersResponse, PutV0CityByCityNamePatchesProvidersResponses, PutV0CityByCityNamePatchesRigsData, PutV0CityByCityNamePatchesRigsError, PutV0CityByCityNamePatchesRigsErrors, PutV0CityByCityNamePatchesRigsResponse, PutV0CityByCityNamePatchesRigsResponses, ReadinessItem, ReadinessResponse, RegisterExtmsgAdapterData, RegisterExtmsgAdapterError, RegisterExtmsgAdapterErrors, RegisterExtmsgAdapterResponse, RegisterExtmsgAdapterResponses, ReplyMailData, ReplyMailError, ReplyMailErrors, ReplyMailResponse, ReplyMailResponses, RequestFailedPayload, RespondSessionData, RespondSessionError, RespondSessionErrors, RespondSessionResponse, RespondSessionResponses, RigActionBody, RigCreatedOutputBody, RigCreateInputBody, RigPatch, RigPatchSetInputBody, RigResponse, RigUpdateInputBody, RotatedPayload, ScopeGroup, SendMailData, SendMailError, SendMailErrors, SendMailResponse, SendMailResponses, SendSessionMessageData, SendSessionMessageError, SendSessionMessageErrors, SendSessionMessageResponse, SendSessionMessageResponses, ServiceRestartOutputBody, SessionActivityEvent, SessionAgentGetResponse, SessionAgentListResponse, SessionBindingRecord, SessionCreateBody, SessionCreateSucceededPayload, SessionInfo, SessionLifecyclePayload, SessionMessageInputBody, SessionMessageSucceededPayload, SessionPatchBody, SessionPendingResponse, SessionPermissionModeBody, SessionRawMessageFrame, SessionRenameInputBody, SessionRespondInputBody, SessionRespondOutputBody, SessionResponse, SessionStreamCommonEvent, SessionStreamMessageEvent, SessionStreamRawMessageEvent, SessionSubmitInputBody, SessionSubmitSucceededPayload, SessionTranscriptGetResponse, SlingInputBody, SlingResponse, Status, StatusAgentCounts, StatusBody, StatusMailCounts, StatusRigCounts, StatusWorkCounts, StreamAgentOutputData, StreamAgentOutputError, StreamAgentOutputErrors, StreamAgentOutputQualifiedData, StreamAgentOutputQualifiedError, StreamAgentOutputQualifiedErrors, StreamAgentOutputQualifiedResponse, StreamAgentOutputQualifiedResponses, StreamAgentOutputResponse, StreamAgentOutputResponses, StreamEventsData, StreamEventsError, StreamEventsErrors, StreamEventsResponse, StreamEventsResponses, StreamSessionData, StreamSessionError, StreamSessionErrors, StreamSessionResponse, StreamSessionResponses, StreamSupervisorEventsData, StreamSupervisorEventsError, StreamSupervisorEventsErrors, StreamSupervisorEventsResponse, StreamSupervisorEventsResponses, SubmissionCapabilities, SubmitIntent, SubmitSessionData, SubmitSessionError, SubmitSessionErrors, SubmitSessionResponse, SubmitSessionResponses, SupervisorCitiesOutputBody, SupervisorEventListOutputBody, SupervisorFsPressureSkippedTickPayload, SupervisorHealthOutputBody, SupervisorStartup, TaggedEventStreamEnvelope, TranscriptMessageKind, TranscriptProvenance, TypedEventStreamEnvelope, TypedEventStreamEnvelopeBeadClosed, TypedEventStreamEnvelopeBeadCreated, TypedEventStreamEnvelopeBeadUpdated, TypedEventStreamEnvelopeCityCreated, TypedEventStreamEnvelopeCityResumed, TypedEventStreamEnvelopeCitySuspended, TypedEventStreamEnvelopeCityUnregisterRequested, TypedEventStreamEnvelopeControllerStarted, TypedEventStreamEnvelopeControllerStopped, TypedEventStreamEnvelopeConvoyClosed, TypedEventStreamEnvelopeConvoyCreated, TypedEventStreamEnvelopeCustom, TypedEventStreamEnvelopeEventsRotated, TypedEventStreamEnvelopeExtmsgAdapterAdded, TypedEventStreamEnvelopeExtmsgAdapterRemoved, TypedEventStreamEnvelopeExtmsgBound, TypedEventStreamEnvelopeExtmsgGroupCreated, TypedEventStreamEnvelopeExtmsgInbound, TypedEventStreamEnvelopeExtmsgOutbound, TypedEventStreamEnvelopeExtmsgUnbound, TypedEventStreamEnvelopeMailArchived, TypedEventStreamEnvelopeMailDeleted, TypedEventStreamEnvelopeMailMarkedRead, TypedEventStreamEnvelopeMailMarkedUnread, TypedEventStreamEnvelopeMailRead, TypedEventStreamEnvelopeMailReplied, TypedEventStreamEnvelopeMailSent, TypedEventStreamEnvelopeOrderCompleted, TypedEventStreamEnvelopeOrderFailed, TypedEventStreamEnvelopeOrderFired, TypedEventStreamEnvelopeProjectIdentityStamped, TypedEventStreamEnvelopeProviderSwapped, TypedEventStreamEnvelopeRequestFailed, TypedEventStreamEnvelopeRequestResultCityCreate, TypedEventStreamEnvelopeRequestResultCityUnregister, TypedEventStreamEnvelopeRequestResultSessionCreate, TypedEventStreamEnvelopeRequestResultSessionMessage, TypedEventStreamEnvelopeRequestResultSessionSubmit, TypedEventStreamEnvelopeSessionCrashed, TypedEventStreamEnvelopeSessionDraining, TypedEventStreamEnvelopeSessionIdleKilled, TypedEventStreamEnvelopeSessionMaxAgeKilled, TypedEventStreamEnvelopeSessionQuarantined, TypedEventStreamEnvelopeSessionStopped, TypedEventStreamEnvelopeSessionSuspended, TypedEventStreamEnvelopeSessionUndrained, TypedEventStreamEnvelopeSessionUpdated, TypedEventStreamEnvelopeSessionWoke, TypedEventStreamEnvelopeSupervisorFsPressureSkippedTick, TypedEventStreamEnvelopeWorkerOperation, TypedTaggedEventStreamEnvelope, TypedTaggedEventStreamEnvelopeBeadClosed, TypedTaggedEventStreamEnvelopeBeadCreated, TypedTaggedEventStreamEnvelopeBeadUpdated, TypedTaggedEventStreamEnvelopeCityCreated, TypedTaggedEventStreamEnvelopeCityResumed, TypedTaggedEventStreamEnvelopeCitySuspended, TypedTaggedEventStreamEnvelopeCityUnregisterRequested, TypedTaggedEventStreamEnvelopeControllerStarted, TypedTaggedEventStreamEnvelopeControllerStopped, TypedTaggedEventStreamEnvelopeConvoyClosed, TypedTaggedEventStreamEnvelopeConvoyCreated, TypedTaggedEventStreamEnvelopeCustom, TypedTaggedEventStreamEnvelopeEventsRotated, TypedTaggedEventStreamEnvelopeExtmsgAdapterAdded, TypedTaggedEventStreamEnvelopeExtmsgAdapterRemoved, TypedTaggedEventStreamEnvelopeExtmsgBound, TypedTaggedEventStreamEnvelopeExtmsgGroupCreated, TypedTaggedEventStreamEnvelopeExtmsgInbound, TypedTaggedEventStreamEnvelopeExtmsgOutbound, TypedTaggedEventStreamEnvelopeExtmsgUnbound, TypedTaggedEventStreamEnvelopeMailArchived, TypedTaggedEventStreamEnvelopeMailDeleted, TypedTaggedEventStreamEnvelopeMailMarkedRead, TypedTaggedEventStreamEnvelopeMailMarkedUnread, TypedTaggedEventStreamEnvelopeMailRead, TypedTaggedEventStreamEnvelopeMailReplied, TypedTaggedEventStreamEnvelopeMailSent, TypedTaggedEventStreamEnvelopeOrderCompleted, TypedTaggedEventStreamEnvelopeOrderFailed, TypedTaggedEventStreamEnvelopeOrderFired, TypedTaggedEventStreamEnvelopeProjectIdentityStamped, TypedTaggedEventStreamEnvelopeProviderSwapped, TypedTaggedEventStreamEnvelopeRequestFailed, TypedTaggedEventStreamEnvelopeRequestResultCityCreate, TypedTaggedEventStreamEnvelopeRequestResultCityUnregister, TypedTaggedEventStreamEnvelopeRequestResultSessionCreate, TypedTaggedEventStreamEnvelopeRequestResultSessionMessage, TypedTaggedEventStreamEnvelopeRequestResultSessionSubmit, TypedTaggedEventStreamEnvelopeSessionCrashed, TypedTaggedEventStreamEnvelopeSessionDraining, TypedTaggedEventStreamEnvelopeSessionIdleKilled, TypedTaggedEventStreamEnvelopeSessionMaxAgeKilled, TypedTaggedEventStreamEnvelopeSessionQuarantined, TypedTaggedEventStreamEnvelopeSessionStopped, TypedTaggedEventStreamEnvelopeSessionSuspended, TypedTaggedEventStreamEnvelopeSessionUndrained, TypedTaggedEventStreamEnvelopeSessionUpdated, TypedTaggedEventStreamEnvelopeSessionWoke, TypedTaggedEventStreamEnvelopeSupervisorFsPressureSkippedTick, TypedTaggedEventStreamEnvelopeWorkerOperation, UnboundEventPayload, WorkerOperationEventPayload, WorkflowAttemptSummary, WorkflowBeadResponse, WorkflowDeleteResponse, WorkflowDepResponse, WorkflowEventProjection, WorkflowSnapshotResponse, WorkspaceResponse } from './types.gen'; diff --git a/cmd/gc/dashboard/web/src/generated/schema.d.ts b/cmd/gc/dashboard/web/src/generated/schema.d.ts index c7db24875a..5a0660e5e2 100644 --- a/cmd/gc/dashboard/web/src/generated/schema.d.ts +++ b/cmd/gc/dashboard/web/src/generated/schema.d.ts @@ -1612,6 +1612,23 @@ export interface paths { patch?: never; trace?: never; }; + "/v0/city/{cityName}/session/{id}/permission-mode": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Post v0 city by city name session by ID permission mode */ + post: operations["post-v0-city-by-city-name-session-by-id-permission-mode"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/v0/city/{cityName}/session/{id}/rename": { parameters: { query?: never; @@ -3714,6 +3731,10 @@ export interface components { pending?: components["schemas"]["PendingInteraction"]; supported: boolean; }; + SessionPermissionModeBody: { + /** @description Provider schema value for the permission_mode option. */ + permission_mode: string; + }; /** * Session raw transcript frame * @description Provider-native transcript frame. Gas City forwards the exact JSON the provider wrote to its session log, so the shape is provider-specific and can be any JSON value. The producing provider is identified by the Provider field on the enclosing envelope; consumers dispatch per-provider frame parsing keyed by that identifier. @@ -11058,6 +11079,50 @@ export interface operations { }; }; }; + "post-v0-city-by-city-name-session-by-id-permission-mode": { + parameters: { + query?: never; + header: { + /** @description Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. */ + "X-GC-Request": string; + }; + path: { + /** @description City name. */ + cityName: string; + /** @description Session ID, alias, or runtime session_name. */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SessionPermissionModeBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + "X-GC-Index"?: number; + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SessionResponse"]; + }; + }; + /** @description Error */ + default: { + headers: { + "X-GC-Request-Id": components["headers"]["X-GC-Request-Id"]; + [name: string]: unknown; + }; + content: { + "application/problem+json": components["schemas"]["ErrorModel"]; + }; + }; + }; + }; "post-v0-city-by-city-name-session-by-id-rename": { parameters: { query?: never; diff --git a/cmd/gc/dashboard/web/src/generated/sdk.gen.ts b/cmd/gc/dashboard/web/src/generated/sdk.gen.ts index 17d682f994..34145ad8b6 100644 --- a/cmd/gc/dashboard/web/src/generated/sdk.gen.ts +++ b/cmd/gc/dashboard/web/src/generated/sdk.gen.ts @@ -2,7 +2,7 @@ import type { Client, Options as Options2, TDataShape } from './client'; import { client } from './client.gen'; -import type { CreateAgentData, CreateAgentErrors, CreateAgentResponses, CreateBeadData, CreateBeadErrors, CreateBeadResponses, CreateConvoyData, CreateConvoyErrors, CreateConvoyResponses, CreateProviderData, CreateProviderErrors, CreateProviderResponses, CreateRigData, CreateRigErrors, CreateRigResponses, CreateSessionData, CreateSessionErrors, CreateSessionResponses, DeleteV0CityByCityNameAgentByBaseData, DeleteV0CityByCityNameAgentByBaseErrors, DeleteV0CityByCityNameAgentByBaseResponses, DeleteV0CityByCityNameAgentByDirByBaseData, DeleteV0CityByCityNameAgentByDirByBaseErrors, DeleteV0CityByCityNameAgentByDirByBaseResponses, DeleteV0CityByCityNameBeadByIdData, DeleteV0CityByCityNameBeadByIdErrors, DeleteV0CityByCityNameBeadByIdResponses, DeleteV0CityByCityNameConvoyByIdData, DeleteV0CityByCityNameConvoyByIdErrors, DeleteV0CityByCityNameConvoyByIdResponses, DeleteV0CityByCityNameExtmsgAdaptersData, DeleteV0CityByCityNameExtmsgAdaptersErrors, DeleteV0CityByCityNameExtmsgAdaptersResponses, DeleteV0CityByCityNameExtmsgParticipantsData, DeleteV0CityByCityNameExtmsgParticipantsErrors, DeleteV0CityByCityNameExtmsgParticipantsResponses, DeleteV0CityByCityNameMailByIdData, DeleteV0CityByCityNameMailByIdErrors, DeleteV0CityByCityNameMailByIdResponses, DeleteV0CityByCityNamePatchesAgentByBaseData, DeleteV0CityByCityNamePatchesAgentByBaseErrors, DeleteV0CityByCityNamePatchesAgentByBaseResponses, DeleteV0CityByCityNamePatchesAgentByDirByBaseData, DeleteV0CityByCityNamePatchesAgentByDirByBaseErrors, DeleteV0CityByCityNamePatchesAgentByDirByBaseResponses, DeleteV0CityByCityNamePatchesProviderByNameData, DeleteV0CityByCityNamePatchesProviderByNameErrors, DeleteV0CityByCityNamePatchesProviderByNameResponses, DeleteV0CityByCityNamePatchesRigByNameData, DeleteV0CityByCityNamePatchesRigByNameErrors, DeleteV0CityByCityNamePatchesRigByNameResponses, DeleteV0CityByCityNameProviderByNameData, DeleteV0CityByCityNameProviderByNameErrors, DeleteV0CityByCityNameProviderByNameResponses, DeleteV0CityByCityNameRigByNameData, DeleteV0CityByCityNameRigByNameErrors, DeleteV0CityByCityNameRigByNameResponses, DeleteV0CityByCityNameWorkflowByWorkflowIdData, DeleteV0CityByCityNameWorkflowByWorkflowIdErrors, DeleteV0CityByCityNameWorkflowByWorkflowIdResponses, EmitEventData, EmitEventErrors, EmitEventResponses, EnsureExtmsgGroupData, EnsureExtmsgGroupErrors, EnsureExtmsgGroupResponses, GetHealthData, GetHealthErrors, GetHealthResponses, GetV0CitiesData, GetV0CitiesErrors, GetV0CitiesResponses, GetV0CityByCityNameAgentByBaseData, GetV0CityByCityNameAgentByBaseErrors, GetV0CityByCityNameAgentByBaseOutputData, GetV0CityByCityNameAgentByBaseOutputErrors, GetV0CityByCityNameAgentByBaseOutputResponses, GetV0CityByCityNameAgentByBaseResponses, GetV0CityByCityNameAgentByDirByBaseData, GetV0CityByCityNameAgentByDirByBaseErrors, GetV0CityByCityNameAgentByDirByBaseOutputData, GetV0CityByCityNameAgentByDirByBaseOutputErrors, GetV0CityByCityNameAgentByDirByBaseOutputResponses, GetV0CityByCityNameAgentByDirByBaseResponses, GetV0CityByCityNameAgentsData, GetV0CityByCityNameAgentsErrors, GetV0CityByCityNameAgentsResponses, GetV0CityByCityNameBeadByIdData, GetV0CityByCityNameBeadByIdDepsData, GetV0CityByCityNameBeadByIdDepsErrors, GetV0CityByCityNameBeadByIdDepsResponses, GetV0CityByCityNameBeadByIdErrors, GetV0CityByCityNameBeadByIdResponses, GetV0CityByCityNameBeadsData, GetV0CityByCityNameBeadsErrors, GetV0CityByCityNameBeadsGraphByRootIdData, GetV0CityByCityNameBeadsGraphByRootIdErrors, GetV0CityByCityNameBeadsGraphByRootIdResponses, GetV0CityByCityNameBeadsReadyData, GetV0CityByCityNameBeadsReadyErrors, GetV0CityByCityNameBeadsReadyResponses, GetV0CityByCityNameBeadsResponses, GetV0CityByCityNameConfigData, GetV0CityByCityNameConfigErrors, GetV0CityByCityNameConfigExplainData, GetV0CityByCityNameConfigExplainErrors, GetV0CityByCityNameConfigExplainResponses, GetV0CityByCityNameConfigResponses, GetV0CityByCityNameConfigValidateData, GetV0CityByCityNameConfigValidateErrors, GetV0CityByCityNameConfigValidateResponses, GetV0CityByCityNameConvoyByIdCheckData, GetV0CityByCityNameConvoyByIdCheckErrors, GetV0CityByCityNameConvoyByIdCheckResponses, GetV0CityByCityNameConvoyByIdData, GetV0CityByCityNameConvoyByIdErrors, GetV0CityByCityNameConvoyByIdResponses, GetV0CityByCityNameConvoysData, GetV0CityByCityNameConvoysErrors, GetV0CityByCityNameConvoysResponses, GetV0CityByCityNameData, GetV0CityByCityNameErrors, GetV0CityByCityNameEventsData, GetV0CityByCityNameEventsErrors, GetV0CityByCityNameEventsResponses, GetV0CityByCityNameExtmsgAdaptersData, GetV0CityByCityNameExtmsgAdaptersErrors, GetV0CityByCityNameExtmsgAdaptersResponses, GetV0CityByCityNameExtmsgBindingsData, GetV0CityByCityNameExtmsgBindingsErrors, GetV0CityByCityNameExtmsgBindingsResponses, GetV0CityByCityNameExtmsgGroupsData, GetV0CityByCityNameExtmsgGroupsErrors, GetV0CityByCityNameExtmsgGroupsResponses, GetV0CityByCityNameExtmsgTranscriptData, GetV0CityByCityNameExtmsgTranscriptErrors, GetV0CityByCityNameExtmsgTranscriptResponses, GetV0CityByCityNameFormulaByNameData, GetV0CityByCityNameFormulaByNameErrors, GetV0CityByCityNameFormulaByNameResponses, GetV0CityByCityNameFormulasByNameData, GetV0CityByCityNameFormulasByNameErrors, GetV0CityByCityNameFormulasByNameResponses, GetV0CityByCityNameFormulasByNameRunsData, GetV0CityByCityNameFormulasByNameRunsErrors, GetV0CityByCityNameFormulasByNameRunsResponses, GetV0CityByCityNameFormulasData, GetV0CityByCityNameFormulasErrors, GetV0CityByCityNameFormulasFeedData, GetV0CityByCityNameFormulasFeedErrors, GetV0CityByCityNameFormulasFeedResponses, GetV0CityByCityNameFormulasResponses, GetV0CityByCityNameHealthData, GetV0CityByCityNameHealthErrors, GetV0CityByCityNameHealthResponses, GetV0CityByCityNameMailByIdData, GetV0CityByCityNameMailByIdErrors, GetV0CityByCityNameMailByIdResponses, GetV0CityByCityNameMailCountData, GetV0CityByCityNameMailCountErrors, GetV0CityByCityNameMailCountResponses, GetV0CityByCityNameMailData, GetV0CityByCityNameMailErrors, GetV0CityByCityNameMailResponses, GetV0CityByCityNameMailThreadByIdData, GetV0CityByCityNameMailThreadByIdErrors, GetV0CityByCityNameMailThreadByIdResponses, GetV0CityByCityNameOrderByNameData, GetV0CityByCityNameOrderByNameErrors, GetV0CityByCityNameOrderByNameResponses, GetV0CityByCityNameOrderHistoryByBeadIdData, GetV0CityByCityNameOrderHistoryByBeadIdErrors, GetV0CityByCityNameOrderHistoryByBeadIdResponses, GetV0CityByCityNameOrdersCheckData, GetV0CityByCityNameOrdersCheckErrors, GetV0CityByCityNameOrdersCheckResponses, GetV0CityByCityNameOrdersData, GetV0CityByCityNameOrdersErrors, GetV0CityByCityNameOrdersFeedData, GetV0CityByCityNameOrdersFeedErrors, GetV0CityByCityNameOrdersFeedResponses, GetV0CityByCityNameOrdersHistoryData, GetV0CityByCityNameOrdersHistoryErrors, GetV0CityByCityNameOrdersHistoryResponses, GetV0CityByCityNameOrdersResponses, GetV0CityByCityNamePacksData, GetV0CityByCityNamePacksErrors, GetV0CityByCityNamePacksResponses, GetV0CityByCityNamePatchesAgentByBaseData, GetV0CityByCityNamePatchesAgentByBaseErrors, GetV0CityByCityNamePatchesAgentByBaseResponses, GetV0CityByCityNamePatchesAgentByDirByBaseData, GetV0CityByCityNamePatchesAgentByDirByBaseErrors, GetV0CityByCityNamePatchesAgentByDirByBaseResponses, GetV0CityByCityNamePatchesAgentsData, GetV0CityByCityNamePatchesAgentsErrors, GetV0CityByCityNamePatchesAgentsResponses, GetV0CityByCityNamePatchesProviderByNameData, GetV0CityByCityNamePatchesProviderByNameErrors, GetV0CityByCityNamePatchesProviderByNameResponses, GetV0CityByCityNamePatchesProvidersData, GetV0CityByCityNamePatchesProvidersErrors, GetV0CityByCityNamePatchesProvidersResponses, GetV0CityByCityNamePatchesRigByNameData, GetV0CityByCityNamePatchesRigByNameErrors, GetV0CityByCityNamePatchesRigByNameResponses, GetV0CityByCityNamePatchesRigsData, GetV0CityByCityNamePatchesRigsErrors, GetV0CityByCityNamePatchesRigsResponses, GetV0CityByCityNameProviderByNameData, GetV0CityByCityNameProviderByNameErrors, GetV0CityByCityNameProviderByNameResponses, GetV0CityByCityNameProviderReadinessData, GetV0CityByCityNameProviderReadinessErrors, GetV0CityByCityNameProviderReadinessResponses, GetV0CityByCityNameProvidersData, GetV0CityByCityNameProvidersErrors, GetV0CityByCityNameProvidersPublicData, GetV0CityByCityNameProvidersPublicErrors, GetV0CityByCityNameProvidersPublicResponses, GetV0CityByCityNameProvidersResponses, GetV0CityByCityNameReadinessData, GetV0CityByCityNameReadinessErrors, GetV0CityByCityNameReadinessResponses, GetV0CityByCityNameResponses, GetV0CityByCityNameRigByNameData, GetV0CityByCityNameRigByNameErrors, GetV0CityByCityNameRigByNameResponses, GetV0CityByCityNameRigsData, GetV0CityByCityNameRigsErrors, GetV0CityByCityNameRigsResponses, GetV0CityByCityNameServiceByNameData, GetV0CityByCityNameServiceByNameErrors, GetV0CityByCityNameServiceByNameResponses, GetV0CityByCityNameServicesData, GetV0CityByCityNameServicesErrors, GetV0CityByCityNameServicesResponses, GetV0CityByCityNameSessionByIdAgentsByAgentIdData, GetV0CityByCityNameSessionByIdAgentsByAgentIdErrors, GetV0CityByCityNameSessionByIdAgentsByAgentIdResponses, GetV0CityByCityNameSessionByIdAgentsData, GetV0CityByCityNameSessionByIdAgentsErrors, GetV0CityByCityNameSessionByIdAgentsResponses, GetV0CityByCityNameSessionByIdData, GetV0CityByCityNameSessionByIdErrors, GetV0CityByCityNameSessionByIdPendingData, GetV0CityByCityNameSessionByIdPendingErrors, GetV0CityByCityNameSessionByIdPendingResponses, GetV0CityByCityNameSessionByIdResponses, GetV0CityByCityNameSessionByIdTranscriptData, GetV0CityByCityNameSessionByIdTranscriptErrors, GetV0CityByCityNameSessionByIdTranscriptResponses, GetV0CityByCityNameSessionsData, GetV0CityByCityNameSessionsErrors, GetV0CityByCityNameSessionsResponses, GetV0CityByCityNameStatusData, GetV0CityByCityNameStatusErrors, GetV0CityByCityNameStatusResponses, GetV0CityByCityNameWorkflowByWorkflowIdData, GetV0CityByCityNameWorkflowByWorkflowIdErrors, GetV0CityByCityNameWorkflowByWorkflowIdResponses, GetV0EventsData, GetV0EventsErrors, GetV0EventsResponses, GetV0ProviderReadinessData, GetV0ProviderReadinessErrors, GetV0ProviderReadinessResponses, GetV0ReadinessData, GetV0ReadinessErrors, GetV0ReadinessResponses, PatchV0CityByCityNameAgentByBaseData, PatchV0CityByCityNameAgentByBaseErrors, PatchV0CityByCityNameAgentByBaseResponses, PatchV0CityByCityNameAgentByDirByBaseData, PatchV0CityByCityNameAgentByDirByBaseErrors, PatchV0CityByCityNameAgentByDirByBaseResponses, PatchV0CityByCityNameBeadByIdData, PatchV0CityByCityNameBeadByIdErrors, PatchV0CityByCityNameBeadByIdResponses, PatchV0CityByCityNameData, PatchV0CityByCityNameErrors, PatchV0CityByCityNameProviderByNameData, PatchV0CityByCityNameProviderByNameErrors, PatchV0CityByCityNameProviderByNameResponses, PatchV0CityByCityNameResponses, PatchV0CityByCityNameRigByNameData, PatchV0CityByCityNameRigByNameErrors, PatchV0CityByCityNameRigByNameResponses, PatchV0CityByCityNameSessionByIdData, PatchV0CityByCityNameSessionByIdErrors, PatchV0CityByCityNameSessionByIdResponses, PostV0CityByCityNameAgentByBaseByActionData, PostV0CityByCityNameAgentByBaseByActionErrors, PostV0CityByCityNameAgentByBaseByActionResponses, PostV0CityByCityNameAgentByDirByBaseByActionData, PostV0CityByCityNameAgentByDirByBaseByActionErrors, PostV0CityByCityNameAgentByDirByBaseByActionResponses, PostV0CityByCityNameBeadByIdAssignData, PostV0CityByCityNameBeadByIdAssignErrors, PostV0CityByCityNameBeadByIdAssignResponses, PostV0CityByCityNameBeadByIdCloseData, PostV0CityByCityNameBeadByIdCloseErrors, PostV0CityByCityNameBeadByIdCloseResponses, PostV0CityByCityNameBeadByIdReopenData, PostV0CityByCityNameBeadByIdReopenErrors, PostV0CityByCityNameBeadByIdReopenResponses, PostV0CityByCityNameBeadByIdUpdateData, PostV0CityByCityNameBeadByIdUpdateErrors, PostV0CityByCityNameBeadByIdUpdateResponses, PostV0CityByCityNameConvoyByIdAddData, PostV0CityByCityNameConvoyByIdAddErrors, PostV0CityByCityNameConvoyByIdAddResponses, PostV0CityByCityNameConvoyByIdCloseData, PostV0CityByCityNameConvoyByIdCloseErrors, PostV0CityByCityNameConvoyByIdCloseResponses, PostV0CityByCityNameConvoyByIdRemoveData, PostV0CityByCityNameConvoyByIdRemoveErrors, PostV0CityByCityNameConvoyByIdRemoveResponses, PostV0CityByCityNameExtmsgBindData, PostV0CityByCityNameExtmsgBindErrors, PostV0CityByCityNameExtmsgBindResponses, PostV0CityByCityNameExtmsgInboundData, PostV0CityByCityNameExtmsgInboundErrors, PostV0CityByCityNameExtmsgInboundResponses, PostV0CityByCityNameExtmsgOutboundData, PostV0CityByCityNameExtmsgOutboundErrors, PostV0CityByCityNameExtmsgOutboundResponses, PostV0CityByCityNameExtmsgParticipantsData, PostV0CityByCityNameExtmsgParticipantsErrors, PostV0CityByCityNameExtmsgParticipantsResponses, PostV0CityByCityNameExtmsgTranscriptAckData, PostV0CityByCityNameExtmsgTranscriptAckErrors, PostV0CityByCityNameExtmsgTranscriptAckResponses, PostV0CityByCityNameExtmsgUnbindData, PostV0CityByCityNameExtmsgUnbindErrors, PostV0CityByCityNameExtmsgUnbindResponses, PostV0CityByCityNameFormulasByNamePreviewData, PostV0CityByCityNameFormulasByNamePreviewErrors, PostV0CityByCityNameFormulasByNamePreviewResponses, PostV0CityByCityNameMailByIdArchiveData, PostV0CityByCityNameMailByIdArchiveErrors, PostV0CityByCityNameMailByIdArchiveResponses, PostV0CityByCityNameMailByIdMarkUnreadData, PostV0CityByCityNameMailByIdMarkUnreadErrors, PostV0CityByCityNameMailByIdMarkUnreadResponses, PostV0CityByCityNameMailByIdReadData, PostV0CityByCityNameMailByIdReadErrors, PostV0CityByCityNameMailByIdReadResponses, PostV0CityByCityNameOrderByNameDisableData, PostV0CityByCityNameOrderByNameDisableErrors, PostV0CityByCityNameOrderByNameDisableResponses, PostV0CityByCityNameOrderByNameEnableData, PostV0CityByCityNameOrderByNameEnableErrors, PostV0CityByCityNameOrderByNameEnableResponses, PostV0CityByCityNameRigByNameByActionData, PostV0CityByCityNameRigByNameByActionErrors, PostV0CityByCityNameRigByNameByActionResponses, PostV0CityByCityNameServiceByNameRestartData, PostV0CityByCityNameServiceByNameRestartErrors, PostV0CityByCityNameServiceByNameRestartResponses, PostV0CityByCityNameSessionByIdCloseData, PostV0CityByCityNameSessionByIdCloseErrors, PostV0CityByCityNameSessionByIdCloseResponses, PostV0CityByCityNameSessionByIdKillData, PostV0CityByCityNameSessionByIdKillErrors, PostV0CityByCityNameSessionByIdKillResponses, PostV0CityByCityNameSessionByIdRenameData, PostV0CityByCityNameSessionByIdRenameErrors, PostV0CityByCityNameSessionByIdRenameResponses, PostV0CityByCityNameSessionByIdStopData, PostV0CityByCityNameSessionByIdStopErrors, PostV0CityByCityNameSessionByIdStopResponses, PostV0CityByCityNameSessionByIdSuspendData, PostV0CityByCityNameSessionByIdSuspendErrors, PostV0CityByCityNameSessionByIdSuspendResponses, PostV0CityByCityNameSessionByIdWakeData, PostV0CityByCityNameSessionByIdWakeErrors, PostV0CityByCityNameSessionByIdWakeResponses, PostV0CityByCityNameSlingData, PostV0CityByCityNameSlingErrors, PostV0CityByCityNameSlingResponses, PostV0CityByCityNameUnregisterData, PostV0CityByCityNameUnregisterErrors, PostV0CityByCityNameUnregisterResponses, PostV0CityData, PostV0CityErrors, PostV0CityResponses, PutV0CityByCityNamePatchesAgentsData, PutV0CityByCityNamePatchesAgentsErrors, PutV0CityByCityNamePatchesAgentsResponses, PutV0CityByCityNamePatchesProvidersData, PutV0CityByCityNamePatchesProvidersErrors, PutV0CityByCityNamePatchesProvidersResponses, PutV0CityByCityNamePatchesRigsData, PutV0CityByCityNamePatchesRigsErrors, PutV0CityByCityNamePatchesRigsResponses, RegisterExtmsgAdapterData, RegisterExtmsgAdapterErrors, RegisterExtmsgAdapterResponses, ReplyMailData, ReplyMailErrors, ReplyMailResponses, RespondSessionData, RespondSessionErrors, RespondSessionResponses, SendMailData, SendMailErrors, SendMailResponses, SendSessionMessageData, SendSessionMessageErrors, SendSessionMessageResponses, StreamAgentOutputData, StreamAgentOutputErrors, StreamAgentOutputQualifiedData, StreamAgentOutputQualifiedErrors, StreamAgentOutputQualifiedResponse, StreamAgentOutputQualifiedResponses, StreamAgentOutputResponse, StreamAgentOutputResponses, StreamEventsData, StreamEventsErrors, StreamEventsResponse, StreamEventsResponses, StreamSessionData, StreamSessionErrors, StreamSessionResponse, StreamSessionResponses, StreamSupervisorEventsData, StreamSupervisorEventsErrors, StreamSupervisorEventsResponse, StreamSupervisorEventsResponses, SubmitSessionData, SubmitSessionErrors, SubmitSessionResponses } from './types.gen'; +import type { CreateAgentData, CreateAgentErrors, CreateAgentResponses, CreateBeadData, CreateBeadErrors, CreateBeadResponses, CreateConvoyData, CreateConvoyErrors, CreateConvoyResponses, CreateProviderData, CreateProviderErrors, CreateProviderResponses, CreateRigData, CreateRigErrors, CreateRigResponses, CreateSessionData, CreateSessionErrors, CreateSessionResponses, DeleteV0CityByCityNameAgentByBaseData, DeleteV0CityByCityNameAgentByBaseErrors, DeleteV0CityByCityNameAgentByBaseResponses, DeleteV0CityByCityNameAgentByDirByBaseData, DeleteV0CityByCityNameAgentByDirByBaseErrors, DeleteV0CityByCityNameAgentByDirByBaseResponses, DeleteV0CityByCityNameBeadByIdData, DeleteV0CityByCityNameBeadByIdErrors, DeleteV0CityByCityNameBeadByIdResponses, DeleteV0CityByCityNameConvoyByIdData, DeleteV0CityByCityNameConvoyByIdErrors, DeleteV0CityByCityNameConvoyByIdResponses, DeleteV0CityByCityNameExtmsgAdaptersData, DeleteV0CityByCityNameExtmsgAdaptersErrors, DeleteV0CityByCityNameExtmsgAdaptersResponses, DeleteV0CityByCityNameExtmsgParticipantsData, DeleteV0CityByCityNameExtmsgParticipantsErrors, DeleteV0CityByCityNameExtmsgParticipantsResponses, DeleteV0CityByCityNameMailByIdData, DeleteV0CityByCityNameMailByIdErrors, DeleteV0CityByCityNameMailByIdResponses, DeleteV0CityByCityNamePatchesAgentByBaseData, DeleteV0CityByCityNamePatchesAgentByBaseErrors, DeleteV0CityByCityNamePatchesAgentByBaseResponses, DeleteV0CityByCityNamePatchesAgentByDirByBaseData, DeleteV0CityByCityNamePatchesAgentByDirByBaseErrors, DeleteV0CityByCityNamePatchesAgentByDirByBaseResponses, DeleteV0CityByCityNamePatchesProviderByNameData, DeleteV0CityByCityNamePatchesProviderByNameErrors, DeleteV0CityByCityNamePatchesProviderByNameResponses, DeleteV0CityByCityNamePatchesRigByNameData, DeleteV0CityByCityNamePatchesRigByNameErrors, DeleteV0CityByCityNamePatchesRigByNameResponses, DeleteV0CityByCityNameProviderByNameData, DeleteV0CityByCityNameProviderByNameErrors, DeleteV0CityByCityNameProviderByNameResponses, DeleteV0CityByCityNameRigByNameData, DeleteV0CityByCityNameRigByNameErrors, DeleteV0CityByCityNameRigByNameResponses, DeleteV0CityByCityNameWorkflowByWorkflowIdData, DeleteV0CityByCityNameWorkflowByWorkflowIdErrors, DeleteV0CityByCityNameWorkflowByWorkflowIdResponses, EmitEventData, EmitEventErrors, EmitEventResponses, EnsureExtmsgGroupData, EnsureExtmsgGroupErrors, EnsureExtmsgGroupResponses, GetHealthData, GetHealthErrors, GetHealthResponses, GetV0CitiesData, GetV0CitiesErrors, GetV0CitiesResponses, GetV0CityByCityNameAgentByBaseData, GetV0CityByCityNameAgentByBaseErrors, GetV0CityByCityNameAgentByBaseOutputData, GetV0CityByCityNameAgentByBaseOutputErrors, GetV0CityByCityNameAgentByBaseOutputResponses, GetV0CityByCityNameAgentByBaseResponses, GetV0CityByCityNameAgentByDirByBaseData, GetV0CityByCityNameAgentByDirByBaseErrors, GetV0CityByCityNameAgentByDirByBaseOutputData, GetV0CityByCityNameAgentByDirByBaseOutputErrors, GetV0CityByCityNameAgentByDirByBaseOutputResponses, GetV0CityByCityNameAgentByDirByBaseResponses, GetV0CityByCityNameAgentsData, GetV0CityByCityNameAgentsErrors, GetV0CityByCityNameAgentsResponses, GetV0CityByCityNameBeadByIdData, GetV0CityByCityNameBeadByIdDepsData, GetV0CityByCityNameBeadByIdDepsErrors, GetV0CityByCityNameBeadByIdDepsResponses, GetV0CityByCityNameBeadByIdErrors, GetV0CityByCityNameBeadByIdResponses, GetV0CityByCityNameBeadsData, GetV0CityByCityNameBeadsErrors, GetV0CityByCityNameBeadsGraphByRootIdData, GetV0CityByCityNameBeadsGraphByRootIdErrors, GetV0CityByCityNameBeadsGraphByRootIdResponses, GetV0CityByCityNameBeadsReadyData, GetV0CityByCityNameBeadsReadyErrors, GetV0CityByCityNameBeadsReadyResponses, GetV0CityByCityNameBeadsResponses, GetV0CityByCityNameConfigData, GetV0CityByCityNameConfigErrors, GetV0CityByCityNameConfigExplainData, GetV0CityByCityNameConfigExplainErrors, GetV0CityByCityNameConfigExplainResponses, GetV0CityByCityNameConfigResponses, GetV0CityByCityNameConfigValidateData, GetV0CityByCityNameConfigValidateErrors, GetV0CityByCityNameConfigValidateResponses, GetV0CityByCityNameConvoyByIdCheckData, GetV0CityByCityNameConvoyByIdCheckErrors, GetV0CityByCityNameConvoyByIdCheckResponses, GetV0CityByCityNameConvoyByIdData, GetV0CityByCityNameConvoyByIdErrors, GetV0CityByCityNameConvoyByIdResponses, GetV0CityByCityNameConvoysData, GetV0CityByCityNameConvoysErrors, GetV0CityByCityNameConvoysResponses, GetV0CityByCityNameData, GetV0CityByCityNameErrors, GetV0CityByCityNameEventsData, GetV0CityByCityNameEventsErrors, GetV0CityByCityNameEventsResponses, GetV0CityByCityNameExtmsgAdaptersData, GetV0CityByCityNameExtmsgAdaptersErrors, GetV0CityByCityNameExtmsgAdaptersResponses, GetV0CityByCityNameExtmsgBindingsData, GetV0CityByCityNameExtmsgBindingsErrors, GetV0CityByCityNameExtmsgBindingsResponses, GetV0CityByCityNameExtmsgGroupsData, GetV0CityByCityNameExtmsgGroupsErrors, GetV0CityByCityNameExtmsgGroupsResponses, GetV0CityByCityNameExtmsgTranscriptData, GetV0CityByCityNameExtmsgTranscriptErrors, GetV0CityByCityNameExtmsgTranscriptResponses, GetV0CityByCityNameFormulaByNameData, GetV0CityByCityNameFormulaByNameErrors, GetV0CityByCityNameFormulaByNameResponses, GetV0CityByCityNameFormulasByNameData, GetV0CityByCityNameFormulasByNameErrors, GetV0CityByCityNameFormulasByNameResponses, GetV0CityByCityNameFormulasByNameRunsData, GetV0CityByCityNameFormulasByNameRunsErrors, GetV0CityByCityNameFormulasByNameRunsResponses, GetV0CityByCityNameFormulasData, GetV0CityByCityNameFormulasErrors, GetV0CityByCityNameFormulasFeedData, GetV0CityByCityNameFormulasFeedErrors, GetV0CityByCityNameFormulasFeedResponses, GetV0CityByCityNameFormulasResponses, GetV0CityByCityNameHealthData, GetV0CityByCityNameHealthErrors, GetV0CityByCityNameHealthResponses, GetV0CityByCityNameMailByIdData, GetV0CityByCityNameMailByIdErrors, GetV0CityByCityNameMailByIdResponses, GetV0CityByCityNameMailCountData, GetV0CityByCityNameMailCountErrors, GetV0CityByCityNameMailCountResponses, GetV0CityByCityNameMailData, GetV0CityByCityNameMailErrors, GetV0CityByCityNameMailResponses, GetV0CityByCityNameMailThreadByIdData, GetV0CityByCityNameMailThreadByIdErrors, GetV0CityByCityNameMailThreadByIdResponses, GetV0CityByCityNameOrderByNameData, GetV0CityByCityNameOrderByNameErrors, GetV0CityByCityNameOrderByNameResponses, GetV0CityByCityNameOrderHistoryByBeadIdData, GetV0CityByCityNameOrderHistoryByBeadIdErrors, GetV0CityByCityNameOrderHistoryByBeadIdResponses, GetV0CityByCityNameOrdersCheckData, GetV0CityByCityNameOrdersCheckErrors, GetV0CityByCityNameOrdersCheckResponses, GetV0CityByCityNameOrdersData, GetV0CityByCityNameOrdersErrors, GetV0CityByCityNameOrdersFeedData, GetV0CityByCityNameOrdersFeedErrors, GetV0CityByCityNameOrdersFeedResponses, GetV0CityByCityNameOrdersHistoryData, GetV0CityByCityNameOrdersHistoryErrors, GetV0CityByCityNameOrdersHistoryResponses, GetV0CityByCityNameOrdersResponses, GetV0CityByCityNamePacksData, GetV0CityByCityNamePacksErrors, GetV0CityByCityNamePacksResponses, GetV0CityByCityNamePatchesAgentByBaseData, GetV0CityByCityNamePatchesAgentByBaseErrors, GetV0CityByCityNamePatchesAgentByBaseResponses, GetV0CityByCityNamePatchesAgentByDirByBaseData, GetV0CityByCityNamePatchesAgentByDirByBaseErrors, GetV0CityByCityNamePatchesAgentByDirByBaseResponses, GetV0CityByCityNamePatchesAgentsData, GetV0CityByCityNamePatchesAgentsErrors, GetV0CityByCityNamePatchesAgentsResponses, GetV0CityByCityNamePatchesProviderByNameData, GetV0CityByCityNamePatchesProviderByNameErrors, GetV0CityByCityNamePatchesProviderByNameResponses, GetV0CityByCityNamePatchesProvidersData, GetV0CityByCityNamePatchesProvidersErrors, GetV0CityByCityNamePatchesProvidersResponses, GetV0CityByCityNamePatchesRigByNameData, GetV0CityByCityNamePatchesRigByNameErrors, GetV0CityByCityNamePatchesRigByNameResponses, GetV0CityByCityNamePatchesRigsData, GetV0CityByCityNamePatchesRigsErrors, GetV0CityByCityNamePatchesRigsResponses, GetV0CityByCityNameProviderByNameData, GetV0CityByCityNameProviderByNameErrors, GetV0CityByCityNameProviderByNameResponses, GetV0CityByCityNameProviderReadinessData, GetV0CityByCityNameProviderReadinessErrors, GetV0CityByCityNameProviderReadinessResponses, GetV0CityByCityNameProvidersData, GetV0CityByCityNameProvidersErrors, GetV0CityByCityNameProvidersPublicData, GetV0CityByCityNameProvidersPublicErrors, GetV0CityByCityNameProvidersPublicResponses, GetV0CityByCityNameProvidersResponses, GetV0CityByCityNameReadinessData, GetV0CityByCityNameReadinessErrors, GetV0CityByCityNameReadinessResponses, GetV0CityByCityNameResponses, GetV0CityByCityNameRigByNameData, GetV0CityByCityNameRigByNameErrors, GetV0CityByCityNameRigByNameResponses, GetV0CityByCityNameRigsData, GetV0CityByCityNameRigsErrors, GetV0CityByCityNameRigsResponses, GetV0CityByCityNameServiceByNameData, GetV0CityByCityNameServiceByNameErrors, GetV0CityByCityNameServiceByNameResponses, GetV0CityByCityNameServicesData, GetV0CityByCityNameServicesErrors, GetV0CityByCityNameServicesResponses, GetV0CityByCityNameSessionByIdAgentsByAgentIdData, GetV0CityByCityNameSessionByIdAgentsByAgentIdErrors, GetV0CityByCityNameSessionByIdAgentsByAgentIdResponses, GetV0CityByCityNameSessionByIdAgentsData, GetV0CityByCityNameSessionByIdAgentsErrors, GetV0CityByCityNameSessionByIdAgentsResponses, GetV0CityByCityNameSessionByIdData, GetV0CityByCityNameSessionByIdErrors, GetV0CityByCityNameSessionByIdPendingData, GetV0CityByCityNameSessionByIdPendingErrors, GetV0CityByCityNameSessionByIdPendingResponses, GetV0CityByCityNameSessionByIdResponses, GetV0CityByCityNameSessionByIdTranscriptData, GetV0CityByCityNameSessionByIdTranscriptErrors, GetV0CityByCityNameSessionByIdTranscriptResponses, GetV0CityByCityNameSessionsData, GetV0CityByCityNameSessionsErrors, GetV0CityByCityNameSessionsResponses, GetV0CityByCityNameStatusData, GetV0CityByCityNameStatusErrors, GetV0CityByCityNameStatusResponses, GetV0CityByCityNameWorkflowByWorkflowIdData, GetV0CityByCityNameWorkflowByWorkflowIdErrors, GetV0CityByCityNameWorkflowByWorkflowIdResponses, GetV0EventsData, GetV0EventsErrors, GetV0EventsResponses, GetV0ProviderReadinessData, GetV0ProviderReadinessErrors, GetV0ProviderReadinessResponses, GetV0ReadinessData, GetV0ReadinessErrors, GetV0ReadinessResponses, PatchV0CityByCityNameAgentByBaseData, PatchV0CityByCityNameAgentByBaseErrors, PatchV0CityByCityNameAgentByBaseResponses, PatchV0CityByCityNameAgentByDirByBaseData, PatchV0CityByCityNameAgentByDirByBaseErrors, PatchV0CityByCityNameAgentByDirByBaseResponses, PatchV0CityByCityNameBeadByIdData, PatchV0CityByCityNameBeadByIdErrors, PatchV0CityByCityNameBeadByIdResponses, PatchV0CityByCityNameData, PatchV0CityByCityNameErrors, PatchV0CityByCityNameProviderByNameData, PatchV0CityByCityNameProviderByNameErrors, PatchV0CityByCityNameProviderByNameResponses, PatchV0CityByCityNameResponses, PatchV0CityByCityNameRigByNameData, PatchV0CityByCityNameRigByNameErrors, PatchV0CityByCityNameRigByNameResponses, PatchV0CityByCityNameSessionByIdData, PatchV0CityByCityNameSessionByIdErrors, PatchV0CityByCityNameSessionByIdResponses, PostV0CityByCityNameAgentByBaseByActionData, PostV0CityByCityNameAgentByBaseByActionErrors, PostV0CityByCityNameAgentByBaseByActionResponses, PostV0CityByCityNameAgentByDirByBaseByActionData, PostV0CityByCityNameAgentByDirByBaseByActionErrors, PostV0CityByCityNameAgentByDirByBaseByActionResponses, PostV0CityByCityNameBeadByIdAssignData, PostV0CityByCityNameBeadByIdAssignErrors, PostV0CityByCityNameBeadByIdAssignResponses, PostV0CityByCityNameBeadByIdCloseData, PostV0CityByCityNameBeadByIdCloseErrors, PostV0CityByCityNameBeadByIdCloseResponses, PostV0CityByCityNameBeadByIdReopenData, PostV0CityByCityNameBeadByIdReopenErrors, PostV0CityByCityNameBeadByIdReopenResponses, PostV0CityByCityNameBeadByIdUpdateData, PostV0CityByCityNameBeadByIdUpdateErrors, PostV0CityByCityNameBeadByIdUpdateResponses, PostV0CityByCityNameConvoyByIdAddData, PostV0CityByCityNameConvoyByIdAddErrors, PostV0CityByCityNameConvoyByIdAddResponses, PostV0CityByCityNameConvoyByIdCloseData, PostV0CityByCityNameConvoyByIdCloseErrors, PostV0CityByCityNameConvoyByIdCloseResponses, PostV0CityByCityNameConvoyByIdRemoveData, PostV0CityByCityNameConvoyByIdRemoveErrors, PostV0CityByCityNameConvoyByIdRemoveResponses, PostV0CityByCityNameExtmsgBindData, PostV0CityByCityNameExtmsgBindErrors, PostV0CityByCityNameExtmsgBindResponses, PostV0CityByCityNameExtmsgInboundData, PostV0CityByCityNameExtmsgInboundErrors, PostV0CityByCityNameExtmsgInboundResponses, PostV0CityByCityNameExtmsgOutboundData, PostV0CityByCityNameExtmsgOutboundErrors, PostV0CityByCityNameExtmsgOutboundResponses, PostV0CityByCityNameExtmsgParticipantsData, PostV0CityByCityNameExtmsgParticipantsErrors, PostV0CityByCityNameExtmsgParticipantsResponses, PostV0CityByCityNameExtmsgTranscriptAckData, PostV0CityByCityNameExtmsgTranscriptAckErrors, PostV0CityByCityNameExtmsgTranscriptAckResponses, PostV0CityByCityNameExtmsgUnbindData, PostV0CityByCityNameExtmsgUnbindErrors, PostV0CityByCityNameExtmsgUnbindResponses, PostV0CityByCityNameFormulasByNamePreviewData, PostV0CityByCityNameFormulasByNamePreviewErrors, PostV0CityByCityNameFormulasByNamePreviewResponses, PostV0CityByCityNameMailByIdArchiveData, PostV0CityByCityNameMailByIdArchiveErrors, PostV0CityByCityNameMailByIdArchiveResponses, PostV0CityByCityNameMailByIdMarkUnreadData, PostV0CityByCityNameMailByIdMarkUnreadErrors, PostV0CityByCityNameMailByIdMarkUnreadResponses, PostV0CityByCityNameMailByIdReadData, PostV0CityByCityNameMailByIdReadErrors, PostV0CityByCityNameMailByIdReadResponses, PostV0CityByCityNameOrderByNameDisableData, PostV0CityByCityNameOrderByNameDisableErrors, PostV0CityByCityNameOrderByNameDisableResponses, PostV0CityByCityNameOrderByNameEnableData, PostV0CityByCityNameOrderByNameEnableErrors, PostV0CityByCityNameOrderByNameEnableResponses, PostV0CityByCityNameRigByNameByActionData, PostV0CityByCityNameRigByNameByActionErrors, PostV0CityByCityNameRigByNameByActionResponses, PostV0CityByCityNameServiceByNameRestartData, PostV0CityByCityNameServiceByNameRestartErrors, PostV0CityByCityNameServiceByNameRestartResponses, PostV0CityByCityNameSessionByIdCloseData, PostV0CityByCityNameSessionByIdCloseErrors, PostV0CityByCityNameSessionByIdCloseResponses, PostV0CityByCityNameSessionByIdKillData, PostV0CityByCityNameSessionByIdKillErrors, PostV0CityByCityNameSessionByIdKillResponses, PostV0CityByCityNameSessionByIdPermissionModeData, PostV0CityByCityNameSessionByIdPermissionModeErrors, PostV0CityByCityNameSessionByIdPermissionModeResponses, PostV0CityByCityNameSessionByIdRenameData, PostV0CityByCityNameSessionByIdRenameErrors, PostV0CityByCityNameSessionByIdRenameResponses, PostV0CityByCityNameSessionByIdStopData, PostV0CityByCityNameSessionByIdStopErrors, PostV0CityByCityNameSessionByIdStopResponses, PostV0CityByCityNameSessionByIdSuspendData, PostV0CityByCityNameSessionByIdSuspendErrors, PostV0CityByCityNameSessionByIdSuspendResponses, PostV0CityByCityNameSessionByIdWakeData, PostV0CityByCityNameSessionByIdWakeErrors, PostV0CityByCityNameSessionByIdWakeResponses, PostV0CityByCityNameSlingData, PostV0CityByCityNameSlingErrors, PostV0CityByCityNameSlingResponses, PostV0CityByCityNameUnregisterData, PostV0CityByCityNameUnregisterErrors, PostV0CityByCityNameUnregisterResponses, PostV0CityData, PostV0CityErrors, PostV0CityResponses, PutV0CityByCityNamePatchesAgentsData, PutV0CityByCityNamePatchesAgentsErrors, PutV0CityByCityNamePatchesAgentsResponses, PutV0CityByCityNamePatchesProvidersData, PutV0CityByCityNamePatchesProvidersErrors, PutV0CityByCityNamePatchesProvidersResponses, PutV0CityByCityNamePatchesRigsData, PutV0CityByCityNamePatchesRigsErrors, PutV0CityByCityNamePatchesRigsResponses, RegisterExtmsgAdapterData, RegisterExtmsgAdapterErrors, RegisterExtmsgAdapterResponses, ReplyMailData, ReplyMailErrors, ReplyMailResponses, RespondSessionData, RespondSessionErrors, RespondSessionResponses, SendMailData, SendMailErrors, SendMailResponses, SendSessionMessageData, SendSessionMessageErrors, SendSessionMessageResponses, StreamAgentOutputData, StreamAgentOutputErrors, StreamAgentOutputQualifiedData, StreamAgentOutputQualifiedErrors, StreamAgentOutputQualifiedResponse, StreamAgentOutputQualifiedResponses, StreamAgentOutputResponse, StreamAgentOutputResponses, StreamEventsData, StreamEventsErrors, StreamEventsResponse, StreamEventsResponses, StreamSessionData, StreamSessionErrors, StreamSessionResponse, StreamSessionResponses, StreamSupervisorEventsData, StreamSupervisorEventsErrors, StreamSupervisorEventsResponse, StreamSupervisorEventsResponses, SubmitSessionData, SubmitSessionErrors, SubmitSessionResponses } from './types.gen'; export type Options = Options2 & { /** @@ -891,6 +891,18 @@ export const sendSessionMessage = (options */ export const getV0CityByCityNameSessionByIdPending = (options: Options) => (options.client ?? client).get({ url: '/v0/city/{cityName}/session/{id}/pending', ...options }); +/** + * Post v0 city by city name session by ID permission mode + */ +export const postV0CityByCityNameSessionByIdPermissionMode = (options: Options) => (options.client ?? client).post({ + url: '/v0/city/{cityName}/session/{id}/permission-mode', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + /** * Post v0 city by city name session by ID rename */ diff --git a/cmd/gc/dashboard/web/src/generated/types.gen.ts b/cmd/gc/dashboard/web/src/generated/types.gen.ts index 3f24de9499..3f41733ee6 100644 --- a/cmd/gc/dashboard/web/src/generated/types.gen.ts +++ b/cmd/gc/dashboard/web/src/generated/types.gen.ts @@ -2417,6 +2417,13 @@ export type SessionPendingResponse = { supported: boolean; }; +export type SessionPermissionModeBody = { + /** + * Provider schema value for the permission_mode option. + */ + permission_mode: string; +}; + /** * Session raw transcript frame * @@ -9616,6 +9623,46 @@ export type GetV0CityByCityNameSessionByIdPendingResponses = { export type GetV0CityByCityNameSessionByIdPendingResponse = GetV0CityByCityNameSessionByIdPendingResponses[keyof GetV0CityByCityNameSessionByIdPendingResponses]; +export type PostV0CityByCityNameSessionByIdPermissionModeData = { + body: SessionPermissionModeBody; + headers: { + /** + * Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + */ + 'X-GC-Request': string; + }; + path: { + /** + * City name. + */ + cityName: string; + /** + * Session ID, alias, or runtime session_name. + */ + id: string; + }; + query?: never; + url: '/v0/city/{cityName}/session/{id}/permission-mode'; +}; + +export type PostV0CityByCityNameSessionByIdPermissionModeErrors = { + /** + * Error + */ + default: ErrorModel; +}; + +export type PostV0CityByCityNameSessionByIdPermissionModeError = PostV0CityByCityNameSessionByIdPermissionModeErrors[keyof PostV0CityByCityNameSessionByIdPermissionModeErrors]; + +export type PostV0CityByCityNameSessionByIdPermissionModeResponses = { + /** + * OK + */ + 200: SessionResponse; +}; + +export type PostV0CityByCityNameSessionByIdPermissionModeResponse = PostV0CityByCityNameSessionByIdPermissionModeResponses[keyof PostV0CityByCityNameSessionByIdPermissionModeResponses]; + export type PostV0CityByCityNameSessionByIdRenameData = { body: SessionRenameInputBody; headers: { diff --git a/cmd/gc/live_submit_probe_test.go b/cmd/gc/live_submit_probe_test.go index f44f8c0c45..0e84e734b0 100644 --- a/cmd/gc/live_submit_probe_test.go +++ b/cmd/gc/live_submit_probe_test.go @@ -98,7 +98,7 @@ func TestLiveClaudeInterruptNow(t *testing.T) { if err != nil { t.Fatalf("mgr.Get(%q): %v", id, err) } - resumeCmd, hints := buildResumeCommand(t.TempDir(), cfg, info, "", io.Discard) + resumeCmd, hints := buildResumeCommand(t.TempDir(), cfg, info, "", nil, io.Discard) socket := cfg.Session.Socket if socket == "" { socket = cfg.Workspace.Name @@ -174,7 +174,7 @@ func TestLiveGeminiSubmitIntents(t *testing.T) { if err != nil { t.Fatalf("mgr.Get(%q): %v", id, err) } - resumeCmd, hints := buildResumeCommand(t.TempDir(), cfg, info, "", io.Discard) + resumeCmd, hints := buildResumeCommand(t.TempDir(), cfg, info, "", nil, io.Discard) socket := cfg.Session.Socket if socket == "" { socket = cfg.Workspace.Name diff --git a/cmd/gc/session_lifecycle_parallel.go b/cmd/gc/session_lifecycle_parallel.go index 1f3a102a1d..6f91897083 100644 --- a/cmd/gc/session_lifecycle_parallel.go +++ b/cmd/gc/session_lifecycle_parallel.go @@ -643,7 +643,19 @@ func prepareStartCandidateForCity( stderr io.Writer, ) (*preparedStart, error) { session := candidate.session - if _, _, err := preWakeCommit(session, store, clk); err != nil { + if session != nil && strings.TrimSpace(session.ID) != "" && store != nil { + if err := sessionpkg.WithSessionMutationLock(session.ID, func() error { + current, err := store.Get(session.ID) + if err != nil { + return err + } + candidate.session = ¤t + _, _, err = preWakeCommit(candidate.session, store, clk) + return err + }); err != nil { + return nil, err + } + } else if _, _, err := preWakeCommit(session, store, clk); err != nil { return nil, err } candidate = refreshConfiguredNamedStartCandidate(candidate, cityPath, cityName, cfg, sp, store, clk, stderr) @@ -706,6 +718,7 @@ func buildPreparedStart( log.Printf("session %s: invalid template_overrides JSON: %v", session.ID, err) } else if len(overrides) > 0 { fullOptions := make(map[string]string) + hasSchemaOverride := false for k, v := range tp.ResolvedProvider.EffectiveDefaults { fullOptions[k] = v } @@ -714,6 +727,7 @@ func buildPreparedStart( continue // handled separately below, not a schema option } fullOptions[k] = v + hasSchemaOverride = true } args, resolveErr := config.ResolveExplicitOptions(tp.ResolvedProvider.OptionsSchema, fullOptions) if resolveErr != nil { @@ -721,6 +735,13 @@ func buildPreparedStart( } else if len(args) > 0 { agentCfg.Command = replaceSchemaFlags(agentCfg.Command, tp.ResolvedProvider.OptionsSchema, args) } + if hasSchemaOverride { + if command, err := config.BuildProviderResumeCommand(tp.ResolvedProvider, overrides); err == nil && strings.TrimSpace(command) != "" { + resolved := *tp.ResolvedProvider + resolved.ResumeCommand = command + tp.ResolvedProvider = &resolved + } + } } } } diff --git a/cmd/gc/session_lifecycle_parallel_test.go b/cmd/gc/session_lifecycle_parallel_test.go index 3b8576c60e..6874f5e630 100644 --- a/cmd/gc/session_lifecycle_parallel_test.go +++ b/cmd/gc/session_lifecycle_parallel_test.go @@ -735,6 +735,61 @@ func TestPrepareStartCandidate_UsesSessionIDForTaskWorkDir(t *testing.T) { } } +func TestPrepareStartCandidateReloadsOverridesBeforeWake(t *testing.T) { + store := beads.NewMemStore() + session, err := store.Create(beads.Bead{ + Title: "worker", + Type: sessionBeadType, + Labels: []string{sessionBeadLabel, "agent:worker"}, + Metadata: map[string]string{ + "template": "worker", + "session_name": "worker", + "state": "asleep", + "session_key": "abc-123", + "started_config_hash": "previous-start", + }, + }) + if err != nil { + t.Fatal(err) + } + if err := store.SetMetadata(session.ID, "template_overrides", `{"permission_mode":"plan"}`); err != nil { + t.Fatalf("SetMetadata(template_overrides): %v", err) + } + + prepared, err := prepareStartCandidate(startCandidate{ + session: &session, + tp: TemplateParams{ + TemplateName: "worker", + SessionName: "worker", + Command: "codex --ask-for-approval on-request", + ResolvedProvider: &config.ResolvedProvider{ + Name: "codex", + ResumeFlag: "resume", + ResumeStyle: "subcommand", + ResumeCommand: "codex resume {{.SessionKey}} --ask-for-approval on-request", + OptionsSchema: []config.ProviderOption{{ + Key: "permission_mode", + Choices: []config.OptionChoice{ + {Value: "default", FlagArgs: []string{"--ask-for-approval", "on-request"}}, + {Value: "plan", FlagArgs: []string{"--ask-for-approval", "never"}}, + }, + }}, + }, + }, + order: 0, + }, &config.City{}, store, &clock.Fake{Time: time.Date(2026, 5, 13, 12, 0, 0, 0, time.UTC)}) + if err != nil { + t.Fatalf("prepareStartCandidate: %v", err) + } + if !strings.Contains(prepared.cfg.Command, "--ask-for-approval never") { + t.Fatalf("prepared.cfg.Command = %q, want reloaded permission override", prepared.cfg.Command) + } + want := "codex resume --ask-for-approval never abc-123" + if prepared.cfg.Command != want { + t.Fatalf("prepared.cfg.Command = %q, want %q", prepared.cfg.Command, want) + } +} + func TestExecutePlannedStarts_FreshWakeAfterDrainRetainsStartupContext(t *testing.T) { skipSlowCmdGCTest(t, "waits through stale session-key detection; run make test-cmd-gc-process for full coverage") sp := runtime.NewFake() @@ -886,18 +941,17 @@ func TestPrepareStartCandidate_GeneratesMissingSessionKeyBeforeWake(t *testing.T t.Fatalf("prepareStartCandidate: %v", err) } - sessionKey := session.Metadata["session_key"] + stored, err := store.Get(session.ID) + if err != nil { + t.Fatalf("store.Get: %v", err) + } + sessionKey := stored.Metadata["session_key"] if sessionKey == "" { t.Fatal("session_key should be generated before wake") } if !strings.Contains(prepared.cfg.Command, "--session-id "+sessionKey) { t.Fatalf("prepared.cfg.Command = %q, want --session-id %s", prepared.cfg.Command, sessionKey) } - - stored, err := store.Get(session.ID) - if err != nil { - t.Fatalf("store.Get: %v", err) - } if stored.Metadata["session_key"] != sessionKey { t.Fatalf("stored session_key = %q, want %q", stored.Metadata["session_key"], sessionKey) } @@ -5738,25 +5792,29 @@ func TestPrepareStartCandidate_PreservesRuntimeConfigAndProviderEnv(t *testing.T t.Fatalf("prepareStartCandidate: %v", err) } - generation, err := strconv.Atoi(bead.Metadata["generation"]) + stored, err := store.Get(bead.ID) if err != nil { - t.Fatalf("generation metadata = %q: %v", bead.Metadata["generation"], err) + t.Fatalf("store.Get: %v", err) } - continuationEpoch, err := strconv.Atoi(bead.Metadata["continuation_epoch"]) + generation, err := strconv.Atoi(stored.Metadata["generation"]) if err != nil { - t.Fatalf("continuation_epoch metadata = %q: %v", bead.Metadata["continuation_epoch"], err) + t.Fatalf("generation metadata = %q: %v", stored.Metadata["generation"], err) + } + continuationEpoch, err := strconv.Atoi(stored.Metadata["continuation_epoch"]) + if err != nil { + t.Fatalf("continuation_epoch metadata = %q: %v", stored.Metadata["continuation_epoch"], err) } expected := templateParamsToConfig(tp) expected.Env = mergeEnv(expected.Env, sessionpkg.RuntimeEnvWithSessionContext( - bead.ID, + stored.ID, tp.SessionName, tp.Alias, - bead.Metadata["template"], - bead.Metadata["session_origin"], + stored.Metadata["template"], + stored.Metadata["session_origin"], generation, continuationEpoch, - bead.Metadata["instance_token"], + stored.Metadata["instance_token"], )) expected.Env = mergeEnv(expected.Env, map[string]string{"GC_PROVIDER": "gemini"}) expected = runtime.SyncWorkDirEnv(expected) @@ -6015,9 +6073,11 @@ func TestCommitStartResult_TransitionsCreatingToActive(t *testing.T) { Type: sessionBeadType, Labels: []string{sessionBeadLabel}, Metadata: map[string]string{ - "template": "worker", - "session_name": "worker-1", - "state": "creating", + "template": "worker", + "session_name": "worker-1", + "state": "creating", + "template_overrides": `{"effort":"high","permission_mode":"plan"}`, + "opt_permission_mode": "plan", }, }) if err != nil { @@ -6055,6 +6115,19 @@ func TestCommitStartResult_TransitionsCreatingToActive(t *testing.T) { if got.Metadata["started_config_hash"] != "core-abc" { t.Errorf("started_config_hash = %q, want %q", got.Metadata["started_config_hash"], "core-abc") } + var overrides map[string]string + if err := json.Unmarshal([]byte(got.Metadata["template_overrides"]), &overrides); err != nil { + t.Fatalf("unmarshal template_overrides: %v", err) + } + if overrides["permission_mode"] != "plan" { + t.Fatalf("permission_mode override = %q, want plan", overrides["permission_mode"]) + } + if overrides["effort"] != "high" { + t.Fatalf("effort override = %q, want high", overrides["effort"]) + } + if got.Metadata["opt_permission_mode"] != "plan" { + t.Fatalf("opt_permission_mode = %q, want plan", got.Metadata["opt_permission_mode"]) + } } func TestCommitStartResult_PersistsMCPIdentityForACPStart(t *testing.T) { diff --git a/cmd/gc/worker_handle.go b/cmd/gc/worker_handle.go index d870d31e7c..04cd1e80e8 100644 --- a/cmd/gc/worker_handle.go +++ b/cmd/gc/worker_handle.go @@ -463,7 +463,7 @@ func resolvedWorkerRuntimeWithConfigAndMetadata(cityPath string, cfg *config.Cit if cfg == nil { return nil, nil } - resolved, configuredTransport := resolveWorkerRuntimeProviderWithConfig(cfg, info, sessionKind) + resolved, configuredTransport := resolveWorkerRuntimeProviderWithConfigAndMetadata(cfg, info, sessionKind, metadata) if resolved == nil { return nil, nil } @@ -485,6 +485,14 @@ func resolvedWorkerRuntimeWithConfigAndMetadata(cityPath string, cfg *config.Cit if err != nil { return nil, err } + resumeCommand := firstNonEmptyGCString(resolved.ResumeCommand, info.ResumeCommand) + if overrides, err := session.ParseTemplateOverrides(metadata); err == nil && strings.TrimSpace(resumeCommand) != "" { + resumeProvider := *resolved + resumeProvider.ResumeCommand = resumeCommand + if command, err := config.BuildProviderResumeCommand(&resumeProvider, overrides); err == nil && strings.TrimSpace(command) != "" { + resumeCommand = command + } + } return &worker.ResolvedRuntime{ Command: command, WorkDir: workDir, @@ -502,7 +510,7 @@ func resolvedWorkerRuntimeWithConfigAndMetadata(cityPath string, cfg *config.Cit Resume: session.ProviderResume{ ResumeFlag: firstNonEmptyGCString(resolved.ResumeFlag, info.ResumeFlag), ResumeStyle: firstNonEmptyGCString(resolved.ResumeStyle, info.ResumeStyle), - ResumeCommand: firstNonEmptyGCString(resolved.ResumeCommand, info.ResumeCommand), + ResumeCommand: resumeCommand, SessionIDFlag: resolved.SessionIDFlag, }, }, nil @@ -695,21 +703,32 @@ func resolvedWorkerRuntimeTransport(info session.Info, resolved *config.Resolved } func resolveWorkerRuntimeProviderWithConfig(cfg *config.City, info session.Info, sessionKind string) (*config.ResolvedProvider, string) { + return resolveWorkerRuntimeProviderWithConfigAndMetadata(cfg, info, sessionKind, nil) +} + +func resolveWorkerRuntimeProviderWithConfigAndMetadata(cfg *config.City, info session.Info, sessionKind string, metadata map[string]string) (*config.ResolvedProvider, string) { if cfg == nil { return nil, "" } - if sessionKind != "provider" { - if found, ok := resolveAgentIdentity(cfg, info.Template, ""); ok { + found, foundAgent := resolveAgentIdentity(cfg, info.Template, "") + if session.UseAgentTemplateForProviderResolution(sessionKind, metadata, info.Provider, found.Provider, foundAgent) { + if foundAgent { if resolved, err := config.ResolveProvider(&found, &cfg.Workspace, cfg.Providers, exec.LookPath); err == nil { return resolved, config.ResolveSessionCreateTransport(found.Session, resolved) } } } - resolved, err := config.ResolveProvider(&config.Agent{Provider: info.Template}, &cfg.Workspace, cfg.Providers, exec.LookPath) - if err != nil { - return nil, "" + for _, providerName := range []string{info.Provider, info.Template} { + providerName = strings.TrimSpace(providerName) + if providerName == "" { + continue + } + resolved, err := config.ResolveProvider(&config.Agent{Provider: providerName}, &cfg.Workspace, cfg.Providers, exec.LookPath) + if err == nil { + return resolved, strings.TrimSpace(resolved.ProviderSessionCreateTransport()) + } } - return resolved, strings.TrimSpace(resolved.ProviderSessionCreateTransport()) + return nil, "" } func workerDeliveryIntentForSubmitIntent(intent session.SubmitIntent) worker.DeliveryIntent { diff --git a/cmd/gc/worker_handle_test.go b/cmd/gc/worker_handle_test.go index a59fed0614..dbccfec741 100644 --- a/cmd/gc/worker_handle_test.go +++ b/cmd/gc/worker_handle_test.go @@ -434,6 +434,92 @@ args = ["{{.AgentName}}"] } } +func TestResolveWorkerRuntimeProviderWithConfigProviderKindPrefersPersistedProvider(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "stored-provider": { + Command: "true", + Args: []string{"stored"}, + }, + "template-provider": { + Command: "true", + Args: []string{"template"}, + }, + }, + } + info := session.Info{ + Template: "template-provider", + Provider: "stored-provider", + } + + resolved, _ := resolveWorkerRuntimeProviderWithConfig(cfg, info, "provider") + if resolved == nil { + t.Fatal("resolveWorkerRuntimeProviderWithConfig() = nil") + } + if got := resolved.Name; got != "stored-provider" { + t.Fatalf("resolved.Name = %q, want stored-provider", got) + } +} + +func TestResolveWorkerRuntimeProviderWithConfigMetadataIdentifiesProviderSession(t *testing.T) { + cfg := &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Agents: []config.Agent{ + {Name: "worker", Dir: "myrig", Provider: "agent-provider"}, + }, + Providers: map[string]config.ProviderSpec{ + "stored-provider": { + Command: "true", + Args: []string{"stored"}, + }, + "agent-provider": { + Command: "true", + Args: []string{"agent"}, + }, + }, + } + info := session.Info{ + Template: "myrig/worker", + Provider: "stored-provider", + } + + resolved, _ := resolveWorkerRuntimeProviderWithConfigAndMetadata(cfg, info, "", map[string]string{ + "session_origin": "manual", + }) + if resolved == nil { + t.Fatal("resolveWorkerRuntimeProviderWithConfigAndMetadata() = nil") + } + if got := resolved.Name; got != "stored-provider" { + t.Fatalf("resolved.Name = %q, want stored-provider", got) + } + + legacyAgentInfo := session.Info{ + Template: "myrig/worker", + Provider: "agent-provider", + } + resolved, _ = resolveWorkerRuntimeProviderWithConfigAndMetadata(cfg, legacyAgentInfo, "", map[string]string{ + "session_origin": "manual", + }) + if resolved == nil { + t.Fatal("resolveWorkerRuntimeProviderWithConfigAndMetadata(legacy agent) = nil") + } + if got := resolved.Name; got != "agent-provider" { + t.Fatalf("legacy agent resolved.Name = %q, want agent-provider", got) + } + + resolved, _ = resolveWorkerRuntimeProviderWithConfigAndMetadata(cfg, info, "", map[string]string{ + "session_origin": "manual", + "agent_name": "myrig/worker", + }) + if resolved == nil { + t.Fatal("resolveWorkerRuntimeProviderWithConfigAndMetadata(agent) = nil") + } + if got := resolved.Name; got != "agent-provider" { + t.Fatalf("agent resolved.Name = %q, want agent-provider", got) + } +} + func TestResolvedWorkerRuntimeWithConfigKeepsDefaultTransportWithoutExplicitACPTemplate(t *testing.T) { cityDir := t.TempDir() writePhase0InterfaceCity(t, cityDir, `[workspace] @@ -638,14 +724,20 @@ func TestResolvedWorkerRuntimeWithConfigReplaysTemplateOverridesOnResume(t *test }}, Providers: map[string]config.ProviderSpec{ "custom": { - Command: "/bin/echo", - PathCheck: "true", + Command: "/bin/echo", + ResumeCommand: "/bin/echo --resume {{.SessionKey}} --effort low", + ResumeFlag: "--resume", + ResumeStyle: "flag", + PathCheck: "true", OptionsSchema: []config.ProviderOption{{ Key: "effort", Type: "select", Choices: []config.OptionChoice{{ Value: "high", FlagArgs: []string{"--effort", "high"}, + }, { + Value: "low", + FlagArgs: []string{"--effort", "low"}, }}, }}, }, @@ -653,9 +745,10 @@ func TestResolvedWorkerRuntimeWithConfigReplaysTemplateOverridesOnResume(t *test } resolved, err := resolvedWorkerRuntimeWithConfigAndMetadata(cityDir, cfg, session.Info{ - Template: "myrig/worker", - Command: "/bin/echo", - WorkDir: cityDir, + Template: "myrig/worker", + Command: "/bin/echo", + WorkDir: cityDir, + SessionKey: "abc-123", }, "", map[string]string{ "template_overrides": `{"effort":"high","initial_message":"hello"}`, }) @@ -668,6 +761,9 @@ func TestResolvedWorkerRuntimeWithConfigReplaysTemplateOverridesOnResume(t *test if got, want := resolved.Command, "/bin/echo --effort high"; got != want { t.Fatalf("Command = %q, want %q", got, want) } + if got, want := resolved.Resume.ResumeCommand, "/bin/echo --resume {{.SessionKey}} --effort high"; got != want { + t.Fatalf("Resume.ResumeCommand = %q, want %q", got, want) + } } func TestResolvedWorkerRuntimeWithConfigFallsBackToStoredCommandWhenTemplateOverridesInvalid(t *testing.T) { diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 25e20f9c45..d93a4bde51 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1251,7 +1251,7 @@ gc help [command] Checks for available work using the agent's work_query config. -Without --inject: prints raw output, exits 0 if work exists, 1 if empty. +Without --inject: prints normalized ready-only output, exits 0 if work exists, 1 if empty. With --inject: silent legacy Stop-hook compatibility; skips the work query and always exits 0. The agent is determined from $GC_AGENT or a positional argument. diff --git a/docs/schema/openapi.json b/docs/schema/openapi.json index 44148c0f49..94b756be80 100644 --- a/docs/schema/openapi.json +++ b/docs/schema/openapi.json @@ -5875,6 +5875,21 @@ ], "type": "object" }, + "SessionPermissionModeBody": { + "additionalProperties": false, + "properties": { + "permission_mode": { + "description": "Provider schema value for the permission_mode option.", + "minLength": 1, + "pattern": "\\S", + "type": "string" + } + }, + "required": [ + "permission_mode" + ], + "type": "object" + }, "SessionRawMessageFrame": { "description": "Provider-native transcript frame. Gas City forwards the exact JSON the provider wrote to its session log, so the shape is provider-specific and can be any JSON value. The producing provider is identified by the Provider field on the enclosing envelope; consumers dispatch per-provider frame parsing keyed by that identifier.", "title": "Session raw transcript frame" @@ -21911,6 +21926,97 @@ "summary": "Get v0 city by city name session by ID pending" } }, + "/v0/city/{cityName}/session/{id}/permission-mode": { + "post": { + "operationId": "post-v0-city-by-city-name-session-by-id-permission-mode", + "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, + { + "description": "City name.", + "in": "path", + "name": "cityName", + "required": true, + "schema": { + "description": "City name.", + "minLength": 1, + "pattern": "\\S", + "type": "string" + } + }, + { + "description": "Session ID, alias, or runtime session_name.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "description": "Session ID, alias, or runtime session_name.", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionPermissionModeBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionResponse" + } + } + }, + "description": "OK", + "headers": { + "X-GC-Index": { + "schema": { + "description": "Latest event sequence number.", + "format": "int64", + "minimum": 0, + "type": "integer" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + } + }, + "summary": "Post v0 city by city name session by ID permission mode" + } + }, "/v0/city/{cityName}/session/{id}/rename": { "post": { "operationId": "post-v0-city-by-city-name-session-by-id-rename", diff --git a/docs/schema/openapi.txt b/docs/schema/openapi.txt index 44148c0f49..94b756be80 100644 --- a/docs/schema/openapi.txt +++ b/docs/schema/openapi.txt @@ -5875,6 +5875,21 @@ ], "type": "object" }, + "SessionPermissionModeBody": { + "additionalProperties": false, + "properties": { + "permission_mode": { + "description": "Provider schema value for the permission_mode option.", + "minLength": 1, + "pattern": "\\S", + "type": "string" + } + }, + "required": [ + "permission_mode" + ], + "type": "object" + }, "SessionRawMessageFrame": { "description": "Provider-native transcript frame. Gas City forwards the exact JSON the provider wrote to its session log, so the shape is provider-specific and can be any JSON value. The producing provider is identified by the Provider field on the enclosing envelope; consumers dispatch per-provider frame parsing keyed by that identifier.", "title": "Session raw transcript frame" @@ -21911,6 +21926,97 @@ "summary": "Get v0 city by city name session by ID pending" } }, + "/v0/city/{cityName}/session/{id}/permission-mode": { + "post": { + "operationId": "post-v0-city-by-city-name-session-by-id-permission-mode", + "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, + { + "description": "City name.", + "in": "path", + "name": "cityName", + "required": true, + "schema": { + "description": "City name.", + "minLength": 1, + "pattern": "\\S", + "type": "string" + } + }, + { + "description": "Session ID, alias, or runtime session_name.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "description": "Session ID, alias, or runtime session_name.", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionPermissionModeBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionResponse" + } + } + }, + "description": "OK", + "headers": { + "X-GC-Index": { + "schema": { + "description": "Latest event sequence number.", + "format": "int64", + "minimum": 0, + "type": "integer" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + } + }, + "summary": "Post v0 city by city name session by ID permission mode" + } + }, "/v0/city/{cityName}/session/{id}/rename": { "post": { "operationId": "post-v0-city-by-city-name-session-by-id-rename", diff --git a/internal/api/genclient/client_gen.go b/internal/api/genclient/client_gen.go index 3ebdc4e216..70fd3aca92 100644 --- a/internal/api/genclient/client_gen.go +++ b/internal/api/genclient/client_gen.go @@ -2317,6 +2317,12 @@ type SessionPendingResponse struct { Supported bool `json:"supported"` } +// SessionPermissionModeBody defines model for SessionPermissionModeBody. +type SessionPermissionModeBody struct { + // PermissionMode Provider schema value for the permission_mode option. + PermissionMode string `json:"permission_mode"` +} + // SessionRawMessageFrame Provider-native transcript frame. Gas City forwards the exact JSON the provider wrote to its session log, so the shape is provider-specific and can be any JSON value. The producing provider is identified by the Provider field on the enclosing envelope; consumers dispatch per-provider frame parsing keyed by that identifier. type SessionRawMessageFrame = interface{} @@ -4855,6 +4861,12 @@ type SendSessionMessageParams struct { XGCRequest string `json:"X-GC-Request"` } +// PostV0CityByCityNameSessionByIdPermissionModeParams defines parameters for PostV0CityByCityNameSessionByIdPermissionMode. +type PostV0CityByCityNameSessionByIdPermissionModeParams struct { + // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. + XGCRequest string `json:"X-GC-Request"` +} + // PostV0CityByCityNameSessionByIdRenameParams defines parameters for PostV0CityByCityNameSessionByIdRename. type PostV0CityByCityNameSessionByIdRenameParams struct { // XGCRequest Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks. @@ -5128,6 +5140,9 @@ type PatchV0CityByCityNameSessionByIdJSONRequestBody = SessionPatchBody // SendSessionMessageJSONRequestBody defines body for SendSessionMessage for application/json ContentType. type SendSessionMessageJSONRequestBody = SessionMessageInputBody +// PostV0CityByCityNameSessionByIdPermissionModeJSONRequestBody defines body for PostV0CityByCityNameSessionByIdPermissionMode for application/json ContentType. +type PostV0CityByCityNameSessionByIdPermissionModeJSONRequestBody = SessionPermissionModeBody + // PostV0CityByCityNameSessionByIdRenameJSONRequestBody defines body for PostV0CityByCityNameSessionByIdRename for application/json ContentType. type PostV0CityByCityNameSessionByIdRenameJSONRequestBody = SessionRenameInputBody @@ -9360,6 +9375,11 @@ type ClientInterface interface { // GetV0CityByCityNameSessionByIdPending request GetV0CityByCityNameSessionByIdPending(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostV0CityByCityNameSessionByIdPermissionModeWithBody request with any body + PostV0CityByCityNameSessionByIdPermissionModeWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostV0CityByCityNameSessionByIdPermissionMode(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, body PostV0CityByCityNameSessionByIdPermissionModeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostV0CityByCityNameSessionByIdRenameWithBody request with any body PostV0CityByCityNameSessionByIdRenameWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -11336,6 +11356,30 @@ func (c *Client) GetV0CityByCityNameSessionByIdPending(ctx context.Context, city return c.Client.Do(req) } +func (c *Client) PostV0CityByCityNameSessionByIdPermissionModeWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSessionByIdPermissionModeRequestWithBody(c.Server, cityName, id, params, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostV0CityByCityNameSessionByIdPermissionMode(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, body PostV0CityByCityNameSessionByIdPermissionModeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV0CityByCityNameSessionByIdPermissionModeRequest(c.Server, cityName, id, params, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PostV0CityByCityNameSessionByIdRenameWithBody(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostV0CityByCityNameSessionByIdRenameRequestWithBody(c.Server, cityName, id, params, contentType, body) if err != nil { @@ -19330,6 +19374,73 @@ func NewGetV0CityByCityNameSessionByIdPendingRequest(server string, cityName str return req, nil } +// NewPostV0CityByCityNameSessionByIdPermissionModeRequest calls the generic PostV0CityByCityNameSessionByIdPermissionMode builder with application/json body +func NewPostV0CityByCityNameSessionByIdPermissionModeRequest(server string, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, body PostV0CityByCityNameSessionByIdPermissionModeJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostV0CityByCityNameSessionByIdPermissionModeRequestWithBody(server, cityName, id, params, "application/json", bodyReader) +} + +// NewPostV0CityByCityNameSessionByIdPermissionModeRequestWithBody generates requests for PostV0CityByCityNameSessionByIdPermissionMode with any type of body +func NewPostV0CityByCityNameSessionByIdPermissionModeRequestWithBody(server string, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "cityName", cityName, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "id", id, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v0/city/%s/session/%s/permission-mode", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + if params != nil { + + var headerParam0 string + + headerParam0, err = runtime.StyleParamWithOptions("simple", false, "X-GC-Request", params.XGCRequest, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationHeader, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + req.Header.Set("X-GC-Request", headerParam0) + + } + + return req, nil +} + // NewPostV0CityByCityNameSessionByIdRenameRequest calls the generic PostV0CityByCityNameSessionByIdRename builder with application/json body func NewPostV0CityByCityNameSessionByIdRenameRequest(server string, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, body PostV0CityByCityNameSessionByIdRenameJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -21189,6 +21300,11 @@ type ClientWithResponsesInterface interface { // GetV0CityByCityNameSessionByIdPendingWithResponse request GetV0CityByCityNameSessionByIdPendingWithResponse(ctx context.Context, cityName string, id string, reqEditors ...RequestEditorFn) (*GetV0CityByCityNameSessionByIdPendingResponse, error) + // PostV0CityByCityNameSessionByIdPermissionModeWithBodyWithResponse request with any body + PostV0CityByCityNameSessionByIdPermissionModeWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdPermissionModeResponse, error) + + PostV0CityByCityNameSessionByIdPermissionModeWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, body PostV0CityByCityNameSessionByIdPermissionModeJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdPermissionModeResponse, error) + // PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse request with any body PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) @@ -24106,6 +24222,29 @@ func (r GetV0CityByCityNameSessionByIdPendingResponse) StatusCode() int { return 0 } +type PostV0CityByCityNameSessionByIdPermissionModeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *SessionResponse + ApplicationproblemJSONDefault *ErrorModel +} + +// Status returns HTTPResponse.Status +func (r PostV0CityByCityNameSessionByIdPermissionModeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostV0CityByCityNameSessionByIdPermissionModeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type PostV0CityByCityNameSessionByIdRenameResponse struct { Body []byte HTTPResponse *http.Response @@ -25937,6 +26076,23 @@ func (c *ClientWithResponses) GetV0CityByCityNameSessionByIdPendingWithResponse( return ParseGetV0CityByCityNameSessionByIdPendingResponse(rsp) } +// PostV0CityByCityNameSessionByIdPermissionModeWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameSessionByIdPermissionModeResponse +func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdPermissionModeWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdPermissionModeResponse, error) { + rsp, err := c.PostV0CityByCityNameSessionByIdPermissionModeWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostV0CityByCityNameSessionByIdPermissionModeResponse(rsp) +} + +func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdPermissionModeWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdPermissionModeParams, body PostV0CityByCityNameSessionByIdPermissionModeJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdPermissionModeResponse, error) { + rsp, err := c.PostV0CityByCityNameSessionByIdPermissionMode(ctx, cityName, id, params, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostV0CityByCityNameSessionByIdPermissionModeResponse(rsp) +} + // PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse request with arbitrary body returning *PostV0CityByCityNameSessionByIdRenameResponse func (c *ClientWithResponses) PostV0CityByCityNameSessionByIdRenameWithBodyWithResponse(ctx context.Context, cityName string, id string, params *PostV0CityByCityNameSessionByIdRenameParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV0CityByCityNameSessionByIdRenameResponse, error) { rsp, err := c.PostV0CityByCityNameSessionByIdRenameWithBody(ctx, cityName, id, params, contentType, body, reqEditors...) @@ -30219,6 +30375,39 @@ func ParseGetV0CityByCityNameSessionByIdPendingResponse(rsp *http.Response) (*Ge return response, nil } +// ParsePostV0CityByCityNameSessionByIdPermissionModeResponse parses an HTTP response from a PostV0CityByCityNameSessionByIdPermissionModeWithResponse call +func ParsePostV0CityByCityNameSessionByIdPermissionModeResponse(rsp *http.Response) (*PostV0CityByCityNameSessionByIdPermissionModeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostV0CityByCityNameSessionByIdPermissionModeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SessionResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest ErrorModel + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + // ParsePostV0CityByCityNameSessionByIdRenameResponse parses an HTTP response from a PostV0CityByCityNameSessionByIdRenameWithResponse call func ParsePostV0CityByCityNameSessionByIdRenameResponse(rsp *http.Response) (*PostV0CityByCityNameSessionByIdRenameResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/internal/api/handler_session_chat_test.go b/internal/api/handler_session_chat_test.go index 88e7298d2c..0c3eb5471e 100644 --- a/internal/api/handler_session_chat_test.go +++ b/internal/api/handler_session_chat_test.go @@ -1,6 +1,7 @@ package api import ( + "context" "os" "path/filepath" "strings" @@ -90,6 +91,53 @@ func TestBuildSessionResumeUsesResolvedProviderCommand(t *testing.T) { } } +func TestBuildSessionResumeAppliesTemplateOverridesToExplicitResumeCommand(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg = &config.City{ + Workspace: config.Workspace{Name: "test-city"}, + Providers: map[string]config.ProviderSpec{ + "codex-provider": { + Command: "codex", + ResumeCommand: "codex resume {{.SessionKey}} --ask-for-approval on-request", + ResumeFlag: "resume", + ResumeStyle: "subcommand", + SessionIDFlag: "--session-id", + PathCheck: "true", + OptionsSchema: []config.ProviderOption{{ + Key: "permission_mode", + Choices: []config.OptionChoice{ + {Value: "default", FlagArgs: []string{"--ask-for-approval", "on-request"}}, + {Value: "plan", FlagArgs: []string{"--ask-for-approval", "never"}}, + }, + }}, + }, + }, + } + mgr := session.NewManager(fs.cityBeadStore, fs.sp) + info, err := mgr.Create(context.Background(), "codex-provider", "chat", "codex --ask-for-approval on-request", "/tmp/workdir", "codex-provider", nil, session.ProviderResume{ + ResumeFlag: "resume", + ResumeStyle: "subcommand", + ResumeCommand: "codex resume {{.SessionKey}} --ask-for-approval on-request", + SessionIDFlag: "--session-id", + }, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := fs.cityBeadStore.SetMetadata(info.ID, "template_overrides", `{"permission_mode":"plan"}`); err != nil { + t.Fatalf("SetMetadata(template_overrides): %v", err) + } + + srv := New(fs) + cmd, _, err := srv.buildSessionResume(info) + if err != nil { + t.Fatalf("buildSessionResume: %v", err) + } + want := "codex resume --ask-for-approval never " + info.SessionKey + if cmd != want { + t.Fatalf("resume command = %q, want %q", cmd, want) + } +} + func TestBuildSessionResumePreservesStoredResolvedCommand(t *testing.T) { fs := newSessionFakeState(t) fs.cfg = &config.City{ @@ -493,7 +541,7 @@ args = ["{{.AgentName}}"] if err != nil { t.Fatalf("resolveBareProvider: %v", err) } - mcpServers, err := srv.sessionMCPServers("custom-acp", "custom-acp", "custom-acp", fs.cityPath, "acp", "provider") + mcpServers, err := srv.sessionMCPServers("custom-acp", "custom-acp", "custom-acp", fs.cityPath, "acp", "provider", nil) if err != nil { t.Fatalf("sessionMCPServers: %v", err) } diff --git a/internal/api/handler_session_create.go b/internal/api/handler_session_create.go index 921b4094a4..5c1d1b1119 100644 --- a/internal/api/handler_session_create.go +++ b/internal/api/handler_session_create.go @@ -141,7 +141,7 @@ func (s *Server) handleSessionCreate(w http.ResponseWriter, r *http.Request) { alias = createCtx.Alias workDir = createCtx.WorkDir - mcpServers, err := s.sessionMCPServers(template, resolved.Name, createCtx.Identity, workDir, transport, kind) + mcpServers, err := s.sessionMCPServers(template, resolved.Name, createCtx.Identity, workDir, transport, kind, nil) if err != nil { s.idem.unreserve(idemKey) writeError(w, http.StatusInternalServerError, "internal", err.Error()) @@ -224,7 +224,7 @@ func (s *Server) handleSessionCreate(w http.ResponseWriter, r *http.Request) { // extraMeta in CreateAliasedBeadOnlyNamedWithMetadata above. Do NOT // overwrite it here — the old code clobbered initial_message by writing // only the options portion. - s.persistSessionMeta(store, info.ID, "agent", body.ProjectID, optMeta) + s.persistSessionMeta(store, info.ID, body.ProjectID, optMeta) s.state.Poke() // wake reconciler to start the agent // Auto-generate a title from the user's message if no explicit title was provided. @@ -352,9 +352,11 @@ func (s *Server) createProviderSession(w http.ResponseWriter, r *http.Request, s writeError(w, http.StatusInternalServerError, "internal", err.Error()) return } - extraMeta := map[string]string{ - "session_origin": "manual", + extraMeta := sessionTemplateOverridesMetadata(body.Options, body.Message) + if extraMeta == nil { + extraMeta = make(map[string]string) } + extraMeta["session_origin"] = "manual" if transport == "acp" { extraMeta, err = session.WithStoredMCPMetadata(extraMeta, mcpIdentity, mcpServers) if err != nil { @@ -392,7 +394,7 @@ func (s *Server) createProviderSession(w http.ResponseWriter, r *http.Request, s } // Persist kind, option metadata, and project_id on the bead. - s.persistSessionMeta(store, info.ID, "provider", body.ProjectID, optMeta) + s.persistSessionMeta(store, info.ID, body.ProjectID, optMeta) if body.Async { s.state.Poke() } @@ -467,14 +469,11 @@ func (s *Server) rollbackCreatedSession(store beads.Store, sessionID string) err } // persistSessionMeta writes option metadata and project_id to the session bead. -func (s *Server) persistSessionMeta(store beads.Store, sessionID, kind, projectID string, optMeta map[string]string) { +func (s *Server) persistSessionMeta(store beads.Store, sessionID, projectID string, optMeta map[string]string) { batch := make(map[string]string) for k, v := range optMeta { batch[k] = v } - if kind != "" && kind != "provider" { - batch["real_world_app_session_kind"] = kind - } if projectID != "" { batch["real_world_app_project_id"] = projectID } diff --git a/internal/api/handler_sessions.go b/internal/api/handler_sessions.go index 8543d85162..43f26abfec 100644 --- a/internal/api/handler_sessions.go +++ b/internal/api/handler_sessions.go @@ -2,7 +2,6 @@ package api import ( "context" - "encoding/json" "errors" "fmt" "log" @@ -137,32 +136,39 @@ func sessionResponseWithReason(info session.Info, b *beads.Bead, cfg *config.Cit // per-session template_overrides. The dashboard uses this to display // the actual permission mode and other settings. if b != nil && cfg != nil { - rp, _ := resolveProviderForTemplate(info.Template, cfg) - if rp != nil && len(rp.EffectiveDefaults) > 0 { - merged := make(map[string]string, len(rp.EffectiveDefaults)) - for k, v := range rp.EffectiveDefaults { - merged[k] = v - } - if raw := b.Metadata["template_overrides"]; raw != "" { - var overrides map[string]string - if err := json.Unmarshal([]byte(raw), &overrides); err == nil { + agentTemplateOK := true + agent, agentFound := findAgent(cfg, info.Template) + if session.UseAgentTemplateForProviderResolution(legacySessionKind(b.Metadata), b.Metadata, info.Provider, agent.Provider, agentFound) { + r.Kind = "agent" + agentTemplateOK = agentFound + } else { + r.Kind = "provider" + } + if agentTemplateOK { + rp, _ := resolveProviderForSessionOptions(info, b.Metadata, cfg) + if rp != nil { + merged := make(map[string]string, len(rp.EffectiveDefaults)) + for k, v := range rp.EffectiveDefaults { + merged[k] = v + } + hasOverrides := false + if overrides, err := session.ParseTemplateOverrides(b.Metadata); err == nil { for k, v := range overrides { if k != "initial_message" { merged[k] = v + hasOverrides = true } } } + if len(rp.EffectiveDefaults) > 0 || hasOverrides { + r.Options = merged + } } - r.Options = merged } } if b == nil || info.Closed { return r } - // Populate kind from persisted metadata. - if k := b.Metadata["real_world_app_session_kind"]; k != "" { - r.Kind = k - } r.Reason = session.LifecycleDisplayReason(b.Status, b.Metadata, time.Now().UTC()) r.ConfiguredNamedSession = strings.TrimSpace(b.Metadata[apiNamedSessionMetadataKey]) == "true" r.SubmissionCapabilities = session.SubmissionCapabilitiesForMetadata(b.Metadata, hasDeferredQueue) @@ -186,6 +192,9 @@ func filterMetadata(m map[string]string) map[string]string { } filtered := make(map[string]string) for k, v := range m { + if k == "real_world_app_session_kind" { + continue + } if strings.HasPrefix(k, "real_world_app_") || filterMetadataAllowedKeys[k] { filtered[k] = v } @@ -397,6 +406,25 @@ func isTransientBeadDeleteConflict(err error) bool { strings.Contains(msg, "serialization failure") } +func (s *Server) handleSessionPermissionMode(w http.ResponseWriter, r *http.Request) { + if r.Header.Get(csrfHeaderName) == "" { + writeError(w, http.StatusForbidden, "csrf", "X-GC-Request header required on mutation endpoints") + return + } + var body SessionPermissionModeBody + if err := decodeBody(r, &body); err != nil { + writeError(w, http.StatusBadRequest, "invalid", err.Error()) + return + } + resp, err := s.updateSessionPermissionMode(r.PathValue("id"), body) + if err != nil { + writeHumaStatusError(w, err) + return + } + w.Header().Set("X-GC-Index", fmt.Sprintf("%d", resp.Index)) + writeJSON(w, http.StatusOK, resp.Body) +} + // handleSessionWake clears hold and quarantine on a session. func (s *Server) handleSessionWake(w http.ResponseWriter, r *http.Request) { store := s.state.CityBeadStore() @@ -716,15 +744,31 @@ func (s *Server) handleSessionPatch(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, presp) } -// resolveProviderForTemplate resolves the provider for an agent template, -// returning the full ResolvedProvider with EffectiveDefaults and OptionsSchema. -func resolveProviderForTemplate(template string, cfg *config.City) (*config.ResolvedProvider, error) { +func resolveProviderForSessionOptions(info session.Info, metadata map[string]string, cfg *config.City) (*config.ResolvedProvider, error) { if cfg == nil { return nil, fmt.Errorf("no config") } - agent, ok := findAgent(cfg, template) - if !ok { - return nil, fmt.Errorf("agent %q not found", template) + agent, agentFound := findAgent(cfg, info.Template) + if session.UseAgentTemplateForProviderResolution(legacySessionKind(metadata), metadata, info.Provider, agent.Provider, agentFound) { + if !agentFound { + return nil, fmt.Errorf("agent template %q not found", info.Template) + } + return config.ResolveProvider(&agent, &cfg.Workspace, cfg.Providers, exec.LookPath) + } + var lastErr error + for _, providerName := range []string{info.Provider, info.Template} { + providerName = strings.TrimSpace(providerName) + if providerName == "" { + continue + } + rp, err := config.ResolveProvider(&config.Agent{Provider: providerName}, &cfg.Workspace, cfg.Providers, exec.LookPath) + if err == nil { + return rp, nil + } + lastErr = err + } + if lastErr != nil { + return nil, lastErr } - return config.ResolveProvider(&agent, &cfg.Workspace, cfg.Providers, exec.LookPath) + return nil, fmt.Errorf("provider for session %q not found", info.ID) } diff --git a/internal/api/handler_sessions_test.go b/internal/api/handler_sessions_test.go index b03bfe5654..50f88cae72 100644 --- a/internal/api/handler_sessions_test.go +++ b/internal/api/handler_sessions_test.go @@ -195,6 +195,14 @@ func createTestSession(t *testing.T, store beads.Store, sp *runtime.Fake, title return info } +func suspendSessionForPermissionModeTest(t *testing.T, fs *fakeState, id string) { + t.Helper() + mgr := session.NewManager(fs.cityBeadStore, fs.sp) + if err := mgr.Suspend(id); err != nil { + t.Fatalf("suspend session: %v", err) + } +} + type cachedOnlyListStoreForSessionTest struct { *beads.MemStore blockList bool @@ -2404,8 +2412,7 @@ func TestHandleSessionCreateAsyncEmitsBeforeOptionalMetadataPersistenceCompletes blocking := &blockingSetMetadataBatchStore{ Store: fs.cityBeadStore, shouldBlock: func(kvs map[string]string) bool { - return kvs["real_world_app_session_kind"] == "agent" && - kvs["real_world_app_project_id"] == "myrig" + return kvs["real_world_app_project_id"] == "myrig" }, entered: make(chan struct{}), release: make(chan struct{}), @@ -2582,7 +2589,7 @@ args = ["{{.AgentName}}", "{{.WorkDir}}", "{{.TemplateName}}"] if err != nil { t.Fatalf("resolveAgentCreateContext: %v", err) } - mcpServers, err := srv.sessionMCPServers("myrig/ant", "opencode", createCtx.Identity, createCtx.WorkDir, "acp", "agent") + mcpServers, err := srv.sessionMCPServers("myrig/ant", "opencode", createCtx.Identity, createCtx.WorkDir, "acp", "agent", nil) if err != nil { t.Fatalf("sessionMCPServers: %v", err) } @@ -3590,6 +3597,725 @@ func TestHandleSessionCreatePreservesInitialMessageWithOptions(t *testing.T) { } } +func TestHandleSessionPermissionModeUpdatesSchemaBackedOverride(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + body := `{"kind":"agent","name":"myrig/worker","message":"keep me","options":{"effort":"high"}}` + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(body)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"plan"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if got := resp.Options["permission_mode"]; got != "plan" { + t.Fatalf("response options.permission_mode = %q, want plan", got) + } + if got := w.Header().Get("X-GC-Index"); got == "" { + t.Fatal("permission-mode response missing X-GC-Index") + } + if got := resp.Options["effort"]; got != "high" { + t.Fatalf("response options.effort = %q, want high", got) + } + + b, err := fs.cityBeadStore.Get(success.Session.ID) + if err != nil { + t.Fatalf("get bead: %v", err) + } + var overrides map[string]string + if err := json.Unmarshal([]byte(b.Metadata["template_overrides"]), &overrides); err != nil { + t.Fatalf("parse template_overrides: %v", err) + } + if got := overrides["permission_mode"]; got != "plan" { + t.Fatalf("template_overrides.permission_mode = %q, want plan", got) + } + if got := b.Metadata["opt_permission_mode"]; got != "plan" { + t.Fatalf("opt_permission_mode = %q, want plan", got) + } + if got := overrides["effort"]; got != "high" { + t.Fatalf("template_overrides.effort = %q, want high", got) + } + if got := overrides["initial_message"]; got != "keep me" { + t.Fatalf("template_overrides.initial_message = %q, want keep me", got) + } +} + +func TestLegacySessionPermissionModeRouteUpdatesSchemaBackedOverride(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + + req = newPostRequest("/v0/session/"+success.Session.ID+"/permission-mode", strings.NewReader(`{"permission_mode":"plan"}`)) + w = httptest.NewRecorder() + srv.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("legacy permission-mode status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if got := resp.Options["permission_mode"]; got != "plan" { + t.Fatalf("response options.permission_mode = %q, want plan", got) + } + if got := w.Header().Get("X-GC-Index"); got == "" { + t.Fatal("legacy permission-mode response missing X-GC-Index") + } + + b, err := fs.cityBeadStore.Get(success.Session.ID) + if err != nil { + t.Fatalf("get bead: %v", err) + } + if got := b.Metadata["opt_permission_mode"]; got != "plan" { + t.Fatalf("opt_permission_mode = %q, want plan", got) + } + var overrides map[string]string + if err := json.Unmarshal([]byte(b.Metadata["template_overrides"]), &overrides); err != nil { + t.Fatalf("parse template_overrides: %v", err) + } + if got := overrides["permission_mode"]; got != "plan" { + t.Fatalf("template_overrides.permission_mode = %q, want plan", got) + } +} + +func TestLegacySessionPermissionModeRouteRequiresCSRFHeader(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + + req := httptest.NewRequest(http.MethodPost, "/v0/session/session-1/permission-mode", strings.NewReader(`{"permission_mode":"plan"}`)) + w := httptest.NewRecorder() + srv.ServeHTTP(w, req) + + if w.Code != http.StatusForbidden { + t.Fatalf("legacy permission-mode status = %d, want %d; body: %s", w.Code, http.StatusForbidden, w.Body.String()) + } +} + +func TestHandleSessionPermissionModeRejectsValueOutsideProviderSchema(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"bypassPermissions"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusBadRequest { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusBadRequest, w.Body.String()) + } + + b, err := fs.cityBeadStore.Get(success.Session.ID) + if err != nil { + t.Fatalf("get bead: %v", err) + } + if strings.Contains(b.Metadata["template_overrides"], "bypassPermissions") { + t.Fatalf("invalid schema value was persisted: %s", b.Metadata["template_overrides"]) + } +} + +func TestHandleSessionPermissionModeRejectsProviderWithoutPermissionModeOption(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + fs.cfg.Providers["test-agent"] = config.ProviderSpec{ + DisplayName: "Test Agent", + Command: "echo", + OptionsSchema: []config.ProviderOption{{ + Key: "effort", Label: "Effort", Type: "select", + Choices: []config.OptionChoice{{Value: "high", Label: "High", FlagArgs: []string{"--effort", "high"}}}, + }}, + } + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"plan"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusNotImplemented { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusNotImplemented, w.Body.String()) + } +} + +func TestHandleSessionPermissionModeUpdatesProviderSessionOptions(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"provider","name":"test-agent"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"auto-edit"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if got := resp.Options["permission_mode"]; got != "auto-edit" { + t.Fatalf("response options.permission_mode = %q, want auto-edit", got) + } + + req = httptest.NewRequest(http.MethodGet, cityURL(fs, "/session/"+success.Session.ID), nil) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("get status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode get response: %v", err) + } + if got := resp.Options["permission_mode"]; got != "auto-edit" { + t.Fatalf("get options.permission_mode = %q, want auto-edit", got) + } + if got := resp.Options["effort"]; got != "max" { + t.Fatalf("get options.effort = %q, want max default", got) + } +} + +func TestHandleSessionPermissionModePreservesProviderCreateOptions(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"provider","name":"test-agent","options":{"permission_mode":"plan","effort":"high"}}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"auto-edit"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if got := resp.Options["permission_mode"]; got != "auto-edit" { + t.Fatalf("response options.permission_mode = %q, want auto-edit", got) + } + if got := resp.Options["effort"]; got != "high" { + t.Fatalf("response options.effort = %q, want high from create-time provider option", got) + } + + mgr := session.NewManager(fs.cityBeadStore, fs.sp) + info, err := mgr.Get(success.Session.ID) + if err != nil { + t.Fatalf("Get session: %v", err) + } + bead, err := fs.cityBeadStore.Get(success.Session.ID) + if err != nil { + t.Fatalf("Get bead: %v", err) + } + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(info, "", bead.Metadata) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if !strings.Contains(runtimeCfg.Command, "--permission-mode auto-edit") { + t.Fatalf("runtime command %q missing updated permission_mode", runtimeCfg.Command) + } + if !strings.Contains(runtimeCfg.Command, "--effort high") { + t.Fatalf("runtime command %q missing preserved effort", runtimeCfg.Command) + } +} + +func TestLegacyHandleProviderSessionCreatePersistsOptionsInTemplateOverrides(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + + req := newPostRequest("/v0/sessions", strings.NewReader(`{"kind":"provider","name":"test-agent","options":{"permission_mode":"plan","effort":"high"}}`)) + w := httptest.NewRecorder() + srv.ServeHTTP(w, req) + if w.Code != http.StatusCreated { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusCreated, w.Body.String()) + } + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + bead, err := fs.cityBeadStore.Get(resp.ID) + if err != nil { + t.Fatalf("Get bead: %v", err) + } + overrides, err := session.ParseTemplateOverrides(bead.Metadata) + if err != nil { + t.Fatalf("ParseTemplateOverrides: %v", err) + } + if got := overrides["permission_mode"]; got != "plan" { + t.Fatalf("template_overrides.permission_mode = %q, want plan", got) + } + if got := overrides["effort"]; got != "high" { + t.Fatalf("template_overrides.effort = %q, want high", got) + } +} + +func TestHandleSessionPermissionModePrefersPersistedProviderOverTemplateProvider(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + fs.cfg.Providers["template-provider"] = config.ProviderSpec{ + DisplayName: "Template Provider", + Command: "echo", + OptionsSchema: []config.ProviderOption{{ + Key: "permission_mode", + Label: "Permission Mode", + Type: "select", + Default: "plan", + Choices: []config.OptionChoice{{ + Value: "plan", + Label: "Plan", + FlagArgs: []string{"--permission-mode", "plan"}, + }}, + }}, + } + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"provider","name":"test-agent"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + if err := fs.cityBeadStore.SetMetadata(success.Session.ID, "template", "template-provider"); err != nil { + t.Fatalf("SetMetadata(template): %v", err) + } + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"auto-edit"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if got := resp.Options["permission_mode"]; got != "auto-edit" { + t.Fatalf("response options.permission_mode = %q, want auto-edit", got) + } + if got := resp.Options["effort"]; got != "max" { + t.Fatalf("response options.effort = %q, want max from persisted provider", got) + } +} + +func TestHandleSessionPermissionModeDoesNotFallbackToAgentWhenProviderMissing(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"provider","name":"test-agent"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + delete(fs.cfg.Providers, "test-agent") + if err := fs.cityBeadStore.SetMetadata(success.Session.ID, "template", "myrig/worker"); err != nil { + t.Fatalf("SetMetadata(template): %v", err) + } + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"plan"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusConflict { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusConflict, w.Body.String()) + } + b, err := fs.cityBeadStore.Get(success.Session.ID) + if err != nil { + t.Fatalf("get bead: %v", err) + } + if strings.Contains(b.Metadata["template_overrides"], "plan") { + t.Fatalf("permission_mode persisted after missing provider: %s", b.Metadata["template_overrides"]) + } +} + +func TestHandleSessionGetUsesAgentDefaultsForConfiguredNamedSession(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + fs.cfg.Agents[0].OptionDefaults = map[string]string{ + "permission_mode": "plan", + } + fs.cfg.Providers["myrig/worker"] = config.ProviderSpec{ + DisplayName: "Colliding Provider", + Command: "echo", + OptionDefaults: map[string]string{ + "permission_mode": "auto-edit", + }, + OptionsSchema: fs.cfg.Providers["test-agent"].OptionsSchema, + } + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + mgr := session.NewManager(fs.cityBeadStore, fs.sp) + info, err := mgr.Create(context.Background(), "myrig/worker", "worker", "echo test", "/tmp", "test-agent", nil, session.ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("create session: %v", err) + } + suspendSessionForPermissionModeTest(t, fs, info.ID) + if err := fs.cityBeadStore.SetMetadata(info.ID, apiNamedSessionMetadataKey, "true"); err != nil { + t.Fatalf("SetMetadata(%s): %v", apiNamedSessionMetadataKey, err) + } + + req := httptest.NewRequest(http.MethodGet, cityURL(fs, "/session/"+info.ID), nil) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("get status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if got := resp.Options["permission_mode"]; got != "plan" { + t.Fatalf("get options.permission_mode = %q, want plan from configured agent defaults", got) + } + if got := resp.Kind; got != "agent" { + t.Fatalf("get kind = %q, want agent", got) + } +} + +func TestHandleSessionGetUsesLegacyProviderKindForNameCollision(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + fs.cfg.Agents = []config.Agent{{ + Name: "codex", + Provider: "codex", + OptionDefaults: map[string]string{ + "permission_mode": "plan", + "effort": "low", + }, + }} + fs.cfg.Providers["codex"] = config.ProviderSpec{ + DisplayName: "Codex", + Command: "echo", + OptionDefaults: map[string]string{ + "permission_mode": "auto-edit", + "effort": "max", + }, + OptionsSchema: fs.cfg.Providers["test-agent"].OptionsSchema, + } + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + mgr := session.NewManager(fs.cityBeadStore, fs.sp) + info, err := mgr.Create(context.Background(), "codex", "codex", "echo", "/tmp/provider", "codex", nil, session.ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("create session: %v", err) + } + suspendSessionForPermissionModeTest(t, fs, info.ID) + if err := fs.cityBeadStore.SetMetadata(info.ID, "real_world_app_session_kind", "provider"); err != nil { + t.Fatalf("SetMetadata(real_world_app_session_kind): %v", err) + } + + req := httptest.NewRequest(http.MethodGet, cityURL(fs, "/session/"+info.ID), nil) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("get status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if got := resp.Options["permission_mode"]; got != "auto-edit" { + t.Fatalf("get options.permission_mode = %q, want provider default auto-edit", got) + } + if got := resp.Options["effort"]; got != "max" { + t.Fatalf("get options.effort = %q, want provider default max", got) + } + if got := resp.Kind; got != "provider" { + t.Fatalf("get kind = %q, want provider", got) + } +} + +func TestHandleSessionPermissionModeRepairsMalformedTemplateOverrides(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + if err := fs.cityBeadStore.SetMetadata(success.Session.ID, "template_overrides", "{not-json"); err != nil { + t.Fatalf("SetMetadata(template_overrides): %v", err) + } + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"plan"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + + b, err := fs.cityBeadStore.Get(success.Session.ID) + if err != nil { + t.Fatalf("get bead: %v", err) + } + overrides, err := session.ParseTemplateOverrides(b.Metadata) + if err != nil { + t.Fatalf("ParseTemplateOverrides: %v", err) + } + if got := overrides["permission_mode"]; got != "plan" { + t.Fatalf("permission_mode = %q, want plan", got) + } +} + +func TestHandleSessionPermissionModeRejectsMissingAgentTemplate(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + if err := fs.cityBeadStore.SetMetadata(success.Session.ID, "template", "myrig/missing-agent"); err != nil { + t.Fatalf("SetMetadata(template): %v", err) + } + if err := fs.cityBeadStore.SetMetadata(success.Session.ID, "agent_name", "myrig/missing-agent"); err != nil { + t.Fatalf("SetMetadata(agent_name): %v", err) + } + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"plan"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusConflict { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusConflict, w.Body.String()) + } + + b, err := fs.cityBeadStore.Get(success.Session.ID) + if err != nil { + t.Fatalf("get bead: %v", err) + } + if strings.Contains(b.Metadata["template_overrides"], "plan") { + t.Fatalf("permission_mode persisted for missing agent template: %s", b.Metadata["template_overrides"]) + } +} + +func TestHandleSessionGetDoesNotExposeOptionsForMissingAgentTemplate(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker","options":{"effort":"high"}}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + if err := fs.cityBeadStore.SetMetadata(success.Session.ID, "template", "myrig/missing-agent"); err != nil { + t.Fatalf("SetMetadata(template): %v", err) + } + if err := fs.cityBeadStore.SetMetadata(success.Session.ID, "agent_name", "myrig/missing-agent"); err != nil { + t.Fatalf("SetMetadata(agent_name): %v", err) + } + + req = httptest.NewRequest(http.MethodGet, cityURL(fs, "/session/"+success.Session.ID), nil) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("get status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if len(resp.Options) != 0 { + t.Fatalf("response options = %#v, want none for missing agent template", resp.Options) + } +} + +func TestHandleSessionPermissionModeRejectsRunningSession(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"plan"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusConflict { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusConflict, w.Body.String()) + } +} + +func TestSessionPermissionModeRuntimeActiveStates(t *testing.T) { + for _, state := range []session.State{session.StateActive, session.StateAwake, session.StateCreating, session.StateDraining, session.StateQuarantined} { + if !session.IsTemplateOverrideRuntimeActive(state) { + t.Fatalf("IsTemplateOverrideRuntimeActive(%q) = false, want true", state) + } + } + for _, state := range []session.State{session.StateAsleep, session.StateSuspended, session.StateDrained, session.StateArchived} { + if session.IsTemplateOverrideRuntimeActive(state) { + t.Fatalf("IsTemplateOverrideRuntimeActive(%q) = true, want false", state) + } + } +} + +func TestHandleSessionPermissionModeReturnsOverrideWithoutProviderDefault(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + provider := fs.cfg.Providers["test-agent"] + delete(provider.OptionDefaults, "permission_mode") + for i := range provider.OptionsSchema { + if provider.OptionsSchema[i].Key == "permission_mode" { + provider.OptionsSchema[i].Default = "" + } + } + fs.cfg.Providers["test-agent"] = provider + srv := New(fs) + h := newTestCityHandlerWith(t, fs, srv) + + req := newPostRequest(cityURL(fs, "/sessions"), strings.NewReader(`{"kind":"agent","name":"myrig/worker"}`)) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusAccepted { + t.Fatalf("create status = %d, want %d; body: %s", w.Code, http.StatusAccepted, w.Body.String()) + } + accepted := decodeAsyncAccepted(t, w.Body) + success, failure := waitForSessionCreateResult(t, fs.eventProv, accepted.RequestID) + if success == nil { + t.Fatalf("session create failed: %s: %s", failure.ErrorCode, failure.ErrorMessage) + } + suspendSessionForPermissionModeTest(t, fs, success.Session.ID) + + req = newPostRequest(cityURL(fs, "/session/"+success.Session.ID+"/permission-mode"), strings.NewReader(`{"permission_mode":"plan"}`)) + w = httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusOK { + t.Fatalf("permission-mode status = %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) + } + var resp sessionResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if got := resp.Options["permission_mode"]; got != "plan" { + t.Fatalf("response options.permission_mode = %q, want plan", got) + } +} + func TestHandleSessionMessageMaterializedNamedSessionUsesLaunchCommandDefaults(t *testing.T) { fs := newSessionFakeStateWithOptions(t) srv := New(fs) @@ -6149,9 +6875,9 @@ func TestFilterMetadataAllowlistsRealWorldAppPrefix(t *testing.T) { want: nil, }, { - name: "real_world_app_ keys preserved", + name: "real_world_app_ keys preserved except removed session kind discriminator", in: map[string]string{"real_world_app_session_kind": "agent", "real_world_app_permission_mode": "plan", "session_key": "secret"}, - want: map[string]string{"real_world_app_session_kind": "agent", "real_world_app_permission_mode": "plan"}, + want: map[string]string{"real_world_app_permission_mode": "plan"}, }, { name: "mixed keys", diff --git a/internal/api/huma_handlers_sessions.go b/internal/api/huma_handlers_sessions.go index d531d21ed9..fa67cc4650 100644 --- a/internal/api/huma_handlers_sessions.go +++ b/internal/api/huma_handlers_sessions.go @@ -2,6 +2,8 @@ package api import ( "errors" + "net/http" + "strings" "github.com/danielgtaylor/huma/v2" "github.com/gastownhall/gascity/internal/beads" @@ -52,6 +54,8 @@ func humaSessionManagerError(err error) error { return huma.Error409Conflict("invalid_interaction: " + err.Error()) case errors.Is(err, session.ErrSessionClosed), errors.Is(err, session.ErrResumeRequired): return huma.Error409Conflict("conflict: " + err.Error()) + case errors.Is(err, session.ErrSessionActive): + return huma.Error409Conflict("conflict: " + err.Error()) case errors.Is(err, session.ErrNotSession): return huma.Error400BadRequest("invalid: " + err.Error()) case errors.Is(err, session.ErrIllegalTransition): @@ -70,6 +74,21 @@ func humaStoreError(err error) error { return huma.Error500InternalServerError("internal: " + err.Error()) } +func writeHumaStatusError(w http.ResponseWriter, err error) { + var statusErr huma.StatusError + if errors.As(err, &statusErr) { + code := "error" + msg := statusErr.Error() + if before, after, ok := strings.Cut(msg, ": "); ok { + code = before + msg = after + } + writeError(w, statusErr.GetStatus(), code, msg) + return + } + writeError(w, http.StatusInternalServerError, "internal", err.Error()) +} + // --- Session List --- // humaHandleSessionList is the Huma-typed handler for GET /v0/sessions. diff --git a/internal/api/huma_handlers_sessions_command.go b/internal/api/huma_handlers_sessions_command.go index 926d7a2585..84a63b132f 100644 --- a/internal/api/huma_handlers_sessions_command.go +++ b/internal/api/huma_handlers_sessions_command.go @@ -117,7 +117,7 @@ func (s *Server) humaHandleSessionCreate(ctx context.Context, input *SessionCrea } extraMeta["agent_name"] = workDirQualifiedName extraMeta["session_origin"] = "manual" - mcpServers, err := s.sessionMCPServers(template, resolved.Name, workDirQualifiedName, workDir, transport, kind) + mcpServers, err := s.sessionMCPServers(template, resolved.Name, workDirQualifiedName, workDir, transport, kind, nil) if err != nil { return nil, huma.Error500InternalServerError(err.Error()) } @@ -207,7 +207,7 @@ func (s *Server) humaHandleSessionCreate(ctx context.Context, input *SessionCrea resp := sessionToResponse(info, s.state.Config()) resp.Kind = "agent" s.emitSessionCreateSucceeded(reqID, resp) - s.persistSessionMeta(store, info.ID, "agent", body.ProjectID, nil) + s.persistSessionMeta(store, info.ID, body.ProjectID, nil) if !waitForCommandable { s.state.Poke() } @@ -299,9 +299,11 @@ func (s *Server) humaCreateProviderSession(_ context.Context, store beads.Store, if err != nil { return nil, huma.Error500InternalServerError(err.Error()) } - extraMeta := map[string]string{ - "session_origin": "manual", + extraMeta := sessionTemplateOverridesMetadata(body.Options, body.Message) + if extraMeta == nil { + extraMeta = make(map[string]string) } + extraMeta["session_origin"] = "manual" if transport == "acp" { extraMeta, err = session.WithStoredMCPMetadata(extraMeta, mcpIdentity, mcpServers) if err != nil { @@ -356,7 +358,7 @@ func (s *Server) humaCreateProviderSession(_ context.Context, store beads.Store, resp := sessionToResponse(info, s.state.Config()) resp.Kind = "provider" s.emitSessionCreateSucceeded(reqID, resp) - s.persistSessionMeta(store, info.ID, "provider", body.ProjectID, optMeta) + s.persistSessionMeta(store, info.ID, body.ProjectID, optMeta) titleProvider := s.resolveTitleProvider() MaybeGenerateTitleAsync(store, info.ID, body.Title, body.Message, titleProvider, info.WorkDir, func(format string, args ...any) { fmt.Fprintf(os.Stderr, "session %s: "+format+"\n", append([]any{info.ID}, args...)...) @@ -454,6 +456,98 @@ func (s *Server) humaHandleSessionPatch(_ context.Context, input *SessionPatchIn }, nil } +const sessionPermissionModeOptionKey = "permission_mode" + +func (s *Server) humaHandleSessionPermissionMode(_ context.Context, input *SessionPermissionModeInput) (*IndexOutput[sessionResponse], error) { + return s.updateSessionPermissionMode(input.ID, input.Body) +} + +func (s *Server) updateSessionPermissionMode(idRef string, body SessionPermissionModeBody) (*IndexOutput[sessionResponse], error) { + store := s.state.CityBeadStore() + if store == nil { + return nil, huma.Error503ServiceUnavailable("no bead store configured") + } + + id, err := s.resolveSessionIDAllowClosedWithConfig(store, idRef) + if err != nil { + return nil, humaResolveError(err) + } + b, err := store.Get(id) + if err != nil { + return nil, humaStoreError(err) + } + if !session.IsSessionBeadOrRepairable(b) { + return nil, huma.Error400BadRequest(id + " is not a session") + } + session.RepairEmptyType(store, &b) + + mgr := s.sessionManager(store) + info, err := mgr.Get(id) + if err != nil { + return nil, humaSessionManagerError(err) + } + if info.Closed { + return nil, huma.Error409Conflict("conflict: session is closed") + } + if session.IsTemplateOverrideRuntimeActive(info.State) { + return nil, huma.Error409Conflict("conflict: session is running; permission_mode changes use schema options and apply only before the next launch") + } + cfg := s.state.Config() + if cfg == nil { + return nil, huma.Error503ServiceUnavailable("city config not loaded yet") + } + agent, agentFound := findAgent(cfg, info.Template) + if session.UseAgentTemplateForProviderResolution(legacySessionKind(b.Metadata), b.Metadata, info.Provider, agent.Provider, agentFound) { + if !agentFound { + return nil, huma.Error409Conflict("conflict: session agent template no longer resolves; restore the template or recreate the session before changing schema options") + } + } + + resolved, resolveErr := resolveProviderForSessionOptions(info, b.Metadata, cfg) + if resolved == nil { + if resolveErr != nil { + return nil, huma.Error409Conflict("conflict: session provider no longer resolves: " + resolveErr.Error()) + } + return nil, huma.Error501NotImplemented("unsupported: session provider does not accept schema options") + } + if !providerHasOption(resolved.OptionsSchema, sessionPermissionModeOptionKey) { + return nil, huma.Error501NotImplemented("unsupported: session provider does not define permission_mode in options_schema") + } + + mode := strings.TrimSpace(body.PermissionMode) + if _, optErr := config.ResolveExplicitOptions(resolved.OptionsSchema, map[string]string{sessionPermissionModeOptionKey: mode}); optErr != nil { + return nil, huma.Error400BadRequest(optErr.Error()) + } + + if _, err := mgr.UpdateTemplateOverrides(id, map[string]string{sessionPermissionModeOptionKey: mode}); err != nil { + return nil, humaSessionManagerError(err) + } + s.state.Poke() + + info, err = mgr.Get(id) + if err != nil { + return nil, humaSessionManagerError(err) + } + updated, err := store.Get(id) + if err != nil { + return nil, humaStoreError(err) + } + resp := sessionResponseWithReason(info, &updated, s.state.Config(), strings.TrimSpace(s.state.CityPath()) != "") + return &IndexOutput[sessionResponse]{ + Index: s.latestIndex(), + Body: resp, + }, nil +} + +func providerHasOption(schema []config.ProviderOption, key string) bool { + for _, opt := range schema { + if opt.Key == key { + return true + } + } + return false +} + // --- Session Submit --- // humaHandleSessionSubmit is the Huma-typed handler for POST /v0/session/{id}/submit. diff --git a/internal/api/huma_types_sessions.go b/internal/api/huma_types_sessions.go index 04374a49ab..6545e11fa8 100644 --- a/internal/api/huma_types_sessions.go +++ b/internal/api/huma_types_sessions.go @@ -120,6 +120,20 @@ type SessionPatchInput struct { Body SessionPatchBody } +// SessionPermissionModeBody is the request body for updating the +// schema-backed permission_mode option on a session. +type SessionPermissionModeBody struct { + _ struct{} `json:"-" additionalProperties:"false"` + PermissionMode string `json:"permission_mode" minLength:"1" pattern:"\\S" doc:"Provider schema value for the permission_mode option."` +} + +// SessionPermissionModeInput is the Huma input for POST /v0/city/{cityName}/session/{id}/permission-mode. +type SessionPermissionModeInput struct { + CityScope + ID string `path:"id" doc:"Session ID, alias, or runtime session_name."` + Body SessionPermissionModeBody +} + // SessionCloseInput is the Huma input for POST /v0/city/{cityName}/session/{id}/close. type SessionCloseInput struct { CityScope diff --git a/internal/api/openapi.json b/internal/api/openapi.json index 44148c0f49..94b756be80 100644 --- a/internal/api/openapi.json +++ b/internal/api/openapi.json @@ -5875,6 +5875,21 @@ ], "type": "object" }, + "SessionPermissionModeBody": { + "additionalProperties": false, + "properties": { + "permission_mode": { + "description": "Provider schema value for the permission_mode option.", + "minLength": 1, + "pattern": "\\S", + "type": "string" + } + }, + "required": [ + "permission_mode" + ], + "type": "object" + }, "SessionRawMessageFrame": { "description": "Provider-native transcript frame. Gas City forwards the exact JSON the provider wrote to its session log, so the shape is provider-specific and can be any JSON value. The producing provider is identified by the Provider field on the enclosing envelope; consumers dispatch per-provider frame parsing keyed by that identifier.", "title": "Session raw transcript frame" @@ -21911,6 +21926,97 @@ "summary": "Get v0 city by city name session by ID pending" } }, + "/v0/city/{cityName}/session/{id}/permission-mode": { + "post": { + "operationId": "post-v0-city-by-city-name-session-by-id-permission-mode", + "parameters": [ + { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "in": "header", + "name": "X-GC-Request", + "required": true, + "schema": { + "description": "Anti-CSRF header required on mutation requests. Any non-empty value is accepted; the header's presence is what the server checks.", + "minLength": 1, + "type": "string" + } + }, + { + "description": "City name.", + "in": "path", + "name": "cityName", + "required": true, + "schema": { + "description": "City name.", + "minLength": 1, + "pattern": "\\S", + "type": "string" + } + }, + { + "description": "Session ID, alias, or runtime session_name.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "description": "Session ID, alias, or runtime session_name.", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionPermissionModeBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionResponse" + } + } + }, + "description": "OK", + "headers": { + "X-GC-Index": { + "schema": { + "description": "Latest event sequence number.", + "format": "int64", + "minimum": 0, + "type": "integer" + } + }, + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + }, + "default": { + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ErrorModel" + } + } + }, + "description": "Error", + "headers": { + "X-GC-Request-Id": { + "$ref": "#/components/headers/X-GC-Request-Id" + } + } + } + }, + "summary": "Post v0 city by city name session by ID permission mode" + } + }, "/v0/city/{cityName}/session/{id}/rename": { "post": { "operationId": "post-v0-city-by-city-name-session-by-id-rename", diff --git a/internal/api/server.go b/internal/api/server.go index 354a9c8583..548ca87760 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -206,6 +206,7 @@ func (s *Server) legacySessionHandler() http.Handler { mux.HandleFunc("GET /v0/session/{id}/stream", s.handleSessionStream) mux.HandleFunc("PATCH /v0/session/{id}", s.handleSessionPatch) mux.HandleFunc("POST /v0/session/{id}/messages", s.handleSessionMessage) + mux.HandleFunc("POST /v0/session/{id}/permission-mode", s.handleSessionPermissionMode) mux.HandleFunc("POST /v0/session/{id}/stop", s.handleSessionStop) mux.HandleFunc("POST /v0/session/{id}/kill", s.handleSessionKill) mux.HandleFunc("POST /v0/session/{id}/respond", s.handleSessionRespond) diff --git a/internal/api/session_model_phase0_spec_test.go b/internal/api/session_model_phase0_spec_test.go index c5edf8c659..68fc4df2e1 100644 --- a/internal/api/session_model_phase0_spec_test.go +++ b/internal/api/session_model_phase0_spec_test.go @@ -11,7 +11,7 @@ import ( // - provider compatibility boundary // - Phase 1 internal unification with compatibility veneers -func TestPhase0ProviderCompatibility_CreateKeepsResponseKindButDoesNotPersistSpecialSessionKind(t *testing.T) { +func TestPhase0ProviderCompatibility_CreateKeepsResponseKind(t *testing.T) { fs := newSessionFakeState(t) srv := New(fs) h := newTestCityHandlerWith(t, fs, srv) diff --git a/internal/api/session_resolution.go b/internal/api/session_resolution.go index b9dd4406eb..ce93072c46 100644 --- a/internal/api/session_resolution.go +++ b/internal/api/session_resolution.go @@ -304,7 +304,7 @@ func (s *Server) materializeNamedSessionWithContext(ctx context.Context, store b if resolved.BuiltinAncestor != "" && resolved.BuiltinAncestor != resolved.Name { extraMeta["builtin_ancestor"] = resolved.BuiltinAncestor } - mcpServers, err := s.sessionMCPServers(qualifiedTemplate, resolved.Name, spec.Identity, workDir, transport, "") + mcpServers, err := s.sessionMCPServers(qualifiedTemplate, resolved.Name, spec.Identity, workDir, transport, "", nil) if err != nil { return "", err } diff --git a/internal/api/session_runtime.go b/internal/api/session_runtime.go index ae623e3661..410ae3a22b 100644 --- a/internal/api/session_runtime.go +++ b/internal/api/session_runtime.go @@ -38,6 +38,13 @@ func sessionCreateHints(resolved *config.ResolvedProvider, mcpServers []runtime. } } +func legacySessionKind(metadata map[string]string) string { + if metadata == nil { + return "" + } + return strings.TrimSpace(metadata["real_world_app_session_kind"]) +} + func sessionResumeHints(resolved *config.ResolvedProvider, workDir string, mcpServers []runtime.MCPServerConfig) runtime.Config { return runtime.Config{ WorkDir: workDir, @@ -70,7 +77,8 @@ func (s *Server) resumeSessionMCPServers(info session.Info, metadata map[string] resumeSessionIdentity(info, metadata), workDir, transport, - s.sessionKind(info.ID), + "", + metadata, ) if err == nil { return mcpServers, nil @@ -102,25 +110,24 @@ func (s *Server) providerSessionMCPServers(providerName, identity, workDir, tran return materialize.RuntimeMCPServers(catalog.Servers), nil } -func (s *Server) sessionMCPServers(template, providerName, identity, workDir, transport, sessionKind string) ([]runtime.MCPServerConfig, error) { +func (s *Server) sessionMCPServers(template, providerName, identity, workDir, transport, sessionKind string, metadata map[string]string) ([]runtime.MCPServerConfig, error) { cfg := s.state.Config() if cfg == nil || strings.TrimSpace(workDir) == "" || strings.TrimSpace(transport) != "acp" { return nil, nil } - if sessionKind != "provider" { - if agentCfg, ok := resolveSessionTemplateAgent(cfg, template); ok { - catalog, err := materialize.EffectiveMCPForSession( - cfg, - s.state.CityPath(), - &agentCfg, - firstNonEmptyString(identity, template), - workDir, - ) - if err != nil { - return nil, fmt.Errorf("loading effective MCP: %w", err) - } - return materialize.RuntimeMCPServers(catalog.Servers), nil + agentCfg, agentFound := resolveSessionTemplateAgent(cfg, template) + if session.UseAgentTemplateForProviderResolution(sessionKind, metadata, providerName, agentCfg.Provider, agentFound) && agentFound { + catalog, err := materialize.EffectiveMCPForSession( + cfg, + s.state.CityPath(), + &agentCfg, + firstNonEmptyString(identity, template), + workDir, + ) + if err != nil { + return nil, fmt.Errorf("loading effective MCP: %w", err) } + return materialize.RuntimeMCPServers(catalog.Servers), nil } return s.providerSessionMCPServers(firstNonEmptyString(providerName, template), identity, workDir, transport) } @@ -252,11 +259,17 @@ func (s *Server) buildSessionResume(info session.Info) (string, runtime.Config, } else { resolvedInfo.Command = fallbackSessionRuntimeCommand(resolved, transport, info.Command, info.Provider) } + resumeCommand := resolved.ResumeCommand + if overrides, err := session.ParseTemplateOverrides(metadata); err == nil { + if command, err := config.BuildProviderResumeCommand(resolved, overrides); err == nil && strings.TrimSpace(command) != "" { + resumeCommand = command + } + } resolvedInfo.Provider = resolved.Name resolvedInfo.Transport = transport resolvedInfo.ResumeFlag = resolved.ResumeFlag resolvedInfo.ResumeStyle = resolved.ResumeStyle - resolvedInfo.ResumeCommand = resolved.ResumeCommand + resolvedInfo.ResumeCommand = resumeCommand return session.BuildResumeCommand(resolvedInfo), sessionResumeHints(resolved, workDir, mcpServers), nil } @@ -368,6 +381,12 @@ func (s *Server) resolveWorkerSessionRuntimeWithMetadata(info session.Info, _ st if err != nil { command = fallbackSessionRuntimeCommand(resolved, transport, info.Command, info.Provider) } + resumeCommand := firstNonEmptyString(resolved.ResumeCommand, info.ResumeCommand) + if overrides, err := session.ParseTemplateOverrides(metadata); err == nil { + if command, err := config.BuildProviderResumeCommand(resolved, overrides); err == nil && strings.TrimSpace(command) != "" { + resumeCommand = command + } + } runtimeCfg, err := worker.NormalizeResolvedRuntime(worker.ResolvedRuntime{ Command: command, WorkDir: firstNonEmptyString(info.WorkDir, workDir), @@ -377,7 +396,7 @@ func (s *Server) resolveWorkerSessionRuntimeWithMetadata(info session.Info, _ st Resume: session.ProviderResume{ ResumeFlag: firstNonEmptyString(resolved.ResumeFlag, info.ResumeFlag), ResumeStyle: firstNonEmptyString(resolved.ResumeStyle, info.ResumeStyle), - ResumeCommand: firstNonEmptyString(resolved.ResumeCommand, info.ResumeCommand), + ResumeCommand: resumeCommand, SessionIDFlag: resolved.SessionIDFlag, }, }) @@ -463,6 +482,7 @@ func (s *Server) startedConfigHashProvesACPTransport( firstNonEmptyString(workDir, info.WorkDir), "acp", sessionKind, + metadata, ) if err != nil { return false @@ -502,31 +522,38 @@ func resolvedSessionTransport(info session.Info, resolved *config.ResolvedProvid } func (s *Server) resolveSessionRuntimeWithMetadata(info session.Info, metadata map[string]string) (*config.ResolvedProvider, string, string, bool) { - kind := s.sessionKind(info.ID) cfg := s.state.Config() var ( resolved *config.ResolvedProvider workDir string configuredTransport string ) - if kind != "provider" && cfg != nil { - if agentCfg, ok := resolveSessionTemplateAgent(cfg, info.Template); ok { - candidate, err := config.ResolveProvider(&agentCfg, &cfg.Workspace, cfg.Providers, exec.LookPath) - if err == nil { - candidateWorkDir, workDirErr := s.resolveSessionWorkDir(agentCfg, agentCfg.QualifiedName()) - if workDirErr == nil { - resolved = candidate - workDir = candidateWorkDir - if info.WorkDir != "" { - workDir = info.WorkDir + if cfg != nil { + agentCfg, agentFound := resolveSessionTemplateAgent(cfg, info.Template) + sessionKind := legacySessionKind(metadata) + if session.UseAgentTemplateForProviderResolution(sessionKind, metadata, info.Provider, agentCfg.Provider, agentFound) { + if agentFound { + candidate, err := config.ResolveProvider(&agentCfg, &cfg.Workspace, cfg.Providers, exec.LookPath) + if err == nil { + candidateWorkDir, workDirErr := s.resolveSessionWorkDir(agentCfg, agentCfg.QualifiedName()) + if workDirErr == nil { + resolved = candidate + workDir = candidateWorkDir + if info.WorkDir != "" { + workDir = info.WorkDir + } + configuredTransport = config.ResolveSessionCreateTransport(agentCfg.Session, resolved) } - configuredTransport = config.ResolveSessionCreateTransport(agentCfg.Session, resolved) } } } } if resolved == nil { - candidate, err := s.resolveBareProvider(info.Template) + providerName := strings.TrimSpace(info.Provider) + if providerName == "" { + providerName = info.Template + } + candidate, err := s.resolveBareProvider(providerName) if err != nil { return nil, "", "", false } @@ -538,25 +565,12 @@ func (s *Server) resolveSessionRuntimeWithMetadata(info session.Info, metadata m configuredTransport = resolved.ProviderSessionCreateTransport() } transport := resolvedSessionTransport(info, resolved, configuredTransport, metadata, false) - if transport == "" && s.startedConfigHashProvesACPTransport(info, metadata, resolved, workDir, configuredTransport, kind) { + if transport == "" && s.startedConfigHashProvesACPTransport(info, metadata, resolved, workDir, configuredTransport, legacySessionKind(metadata)) { transport = "acp" } return resolved, workDir, transport, transport == "" && legacyACPTransportAmbiguous(resolved, configuredTransport, info.Command, metadata) } -// sessionKind reads the persisted real_world_app_session_kind from bead metadata. -func (s *Server) sessionKind(sessionID string) string { - store := s.state.CityBeadStore() - if store == nil { - return "" - } - b, err := store.Get(sessionID) - if err != nil { - return "" - } - return b.Metadata["real_world_app_session_kind"] -} - // resolveBareProvider resolves a provider by name without an agent template. func (s *Server) resolveBareProvider(providerName string) (*config.ResolvedProvider, error) { cfg := s.state.Config() diff --git a/internal/api/supervisor_city_routes.go b/internal/api/supervisor_city_routes.go index 647b40edac..b36dcda93d 100644 --- a/internal/api/supervisor_city_routes.go +++ b/internal/api/supervisor_city_routes.go @@ -259,6 +259,7 @@ func (sm *SupervisorMux) registerCityRoutes() { cityGet(sm, "/session/{id}/transcript", (*Server).humaHandleSessionTranscript) cityGet(sm, "/session/{id}/pending", (*Server).humaHandleSessionPending) cityPatch(sm, "/session/{id}", (*Server).humaHandleSessionPatch) + cityPost(sm, "/session/{id}/permission-mode", (*Server).humaHandleSessionPermissionMode) cityRegister(sm, huma.Operation{ OperationID: "submit-session", Method: http.MethodPost, diff --git a/internal/api/worker_factory_test.go b/internal/api/worker_factory_test.go index b05b3797ed..ce979b878f 100644 --- a/internal/api/worker_factory_test.go +++ b/internal/api/worker_factory_test.go @@ -4,6 +4,7 @@ import ( "context" "os" "path/filepath" + "strings" "testing" "github.com/gastownhall/gascity/internal/config" @@ -619,6 +620,150 @@ func TestResolveWorkerSessionRuntimeFallsBackToPersistedProviderWhenCommandMissi } } +func TestResolveWorkerSessionRuntimeProviderCollisionUsesPersistedProvider(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents = []config.Agent{{ + Name: "test-agent", + Provider: "agent-provider", + }} + fs.cfg.Providers["test-agent"] = config.ProviderSpec{ + Command: "/bin/echo", + Args: []string{"provider-session"}, + PathCheck: "true", + } + fs.cfg.Providers["agent-provider"] = config.ProviderSpec{ + Command: "/bin/echo", + Args: []string{"agent-template"}, + PathCheck: "true", + } + + srv := New(fs) + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(session.Info{ + Template: "test-agent", + Provider: "test-agent", + WorkDir: t.TempDir(), + }, "", map[string]string{ + "session_origin": "manual", + }) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if got, wantPrefix := runtimeCfg.Command, "/bin/echo provider-session"; !strings.HasPrefix(got, wantPrefix) { + t.Fatalf("Command = %q, want prefix %q", got, wantPrefix) + } +} + +func TestResolveWorkerSessionRuntimeProviderNameCollisionUsesPersistedProvider(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents = []config.Agent{{ + Name: "codex", + Provider: "codex", + WorkDir: ".gc/worktrees/agent-codex", + }} + fs.cfg.Providers["codex"] = config.ProviderSpec{ + Command: "/bin/echo", + Args: []string{"provider-session"}, + PathCheck: "true", + } + + srv := New(fs) + providerWorkDir := t.TempDir() + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(session.Info{ + Template: "codex", + Provider: "codex", + WorkDir: providerWorkDir, + }, "", map[string]string{ + "session_origin": "manual", + }) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if got, wantPrefix := runtimeCfg.Command, "/bin/echo provider-session"; !strings.HasPrefix(got, wantPrefix) { + t.Fatalf("Command = %q, want prefix %q", got, wantPrefix) + } + if got, want := runtimeCfg.WorkDir, providerWorkDir; got != want { + t.Fatalf("WorkDir = %q, want %q", got, want) + } +} + +func TestResolveWorkerSessionRuntimeLegacyProviderKindSkipsNameCollisionTemplate(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents = []config.Agent{{ + Name: "codex", + Provider: "codex", + Session: "acp", + WorkDir: ".gc/worktrees/agent-codex", + }} + fs.cfg.Providers["codex"] = config.ProviderSpec{ + Command: "/bin/echo", + Args: []string{"provider-session"}, + PathCheck: "true", + ACPCommand: "/bin/echo", + ACPArgs: []string{"agent-acp"}, + } + + srv := New(fs) + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(session.Info{ + Template: "codex", + Provider: "codex", + WorkDir: t.TempDir(), + }, "", map[string]string{ + "real_world_app_session_kind": "provider", + }) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if got, wantPrefix := runtimeCfg.Command, "/bin/echo provider-session"; !strings.HasPrefix(got, wantPrefix) { + t.Fatalf("Command = %q, want prefix %q", got, wantPrefix) + } +} + +func TestResolveWorkerSessionRuntimeLegacyManualAgentUsesTemplateWhenProviderMatches(t *testing.T) { + fs := newSessionFakeState(t) + fs.cfg.Agents = []config.Agent{{ + Name: "worker", + Dir: "myrig", + Provider: "test-agent", + }} + fs.cfg.Providers["test-agent"] = config.ProviderSpec{ + Command: "/bin/echo", + Args: []string{"agent-template"}, + PathCheck: "true", + ReadyPromptPrefix: "agent-ready>", + } + + srv := New(fs) + runtimeCfg, err := srv.resolveWorkerSessionRuntimeWithMetadata(session.Info{ + Template: "myrig/worker", + Provider: "test-agent", + WorkDir: t.TempDir(), + }, "", map[string]string{ + "agent_name": "myrig/worker", + "session_origin": "manual", + }) + if err != nil { + t.Fatalf("resolveWorkerSessionRuntimeWithMetadata: %v", err) + } + if runtimeCfg == nil { + t.Fatal("resolveWorkerSessionRuntimeWithMetadata() = nil") + } + if got, want := runtimeCfg.Command, "/bin/echo agent-template"; got != want { + t.Fatalf("Command = %q, want %q", got, want) + } + if got, want := runtimeCfg.Hints.ReadyPromptPrefix, "agent-ready>"; got != want { + t.Fatalf("Hints.ReadyPromptPrefix = %q, want %q", got, want) + } +} + func TestWorkerFactorySessionByIDUsesResolvedTemplateRuntime(t *testing.T) { fs := newSessionFakeState(t) fs.cfg.Agents[0].Provider = "resolved-worker" @@ -777,6 +922,63 @@ func TestWorkerFactorySessionByIDUsesResolvedCommandAndResumeSettingsOnResume(t } } +func TestWorkerFactorySessionByIDAppliesTemplateOverridesToExplicitResumeCommand(t *testing.T) { + fs := newSessionFakeStateWithOptions(t) + fs.cfg.Agents[0].Provider = "resolved-worker" + spec := fs.cfg.Providers["test-agent"] + spec.Command = "/bin/echo" + spec.ResumeCommand = "/bin/echo resume {{.SessionKey}} --skip-permissions" + spec.SessionIDFlag = "--session-id" + fs.cfg.Providers["resolved-worker"] = spec + + srv := New(fs) + mgr := session.NewManager(fs.cityBeadStore, fs.sp) + info, err := mgr.Create( + context.Background(), + "myrig/worker", + "Chat", + "/bin/echo --skip-permissions", + t.TempDir(), + "resolved-worker", + nil, + session.ProviderResume{ + ResumeCommand: "/bin/echo resume {{.SessionKey}} --skip-permissions", + SessionIDFlag: "--session-id", + }, + runtime.Config{}, + ) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := mgr.Suspend(info.ID); err != nil { + t.Fatalf("Suspend: %v", err) + } + if err := fs.cityBeadStore.SetMetadata(info.ID, "template_overrides", `{"permission_mode":"plan"}`); err != nil { + t.Fatalf("SetMetadata(template_overrides): %v", err) + } + + factory, err := srv.workerFactory(fs.cityBeadStore) + if err != nil { + t.Fatalf("workerFactory: %v", err) + } + handle, err := factory.SessionByID(info.ID) + if err != nil { + t.Fatalf("SessionByID(%q): %v", info.ID, err) + } + if err := handle.Start(context.Background()); err != nil { + t.Fatalf("Start: %v", err) + } + + start := fs.sp.LastStartConfig(info.SessionName) + if start == nil { + t.Fatal("LastStartConfig() = nil") + } + want := "/bin/echo resume " + info.SessionKey + " --permission-mode plan --effort max" + if got := start.Command; got != want { + t.Fatalf("start command = %q, want %q", got, want) + } +} + func TestWorkerFactoryHandleForTargetUsesResolvedTemplateRuntimeForSessionMeta(t *testing.T) { fs := newSessionFakeState(t) fs.cfg.Agents[0].Provider = "resolved-worker" diff --git a/internal/config/launch_command.go b/internal/config/launch_command.go index 8d2b889f5e..d4e23943bb 100644 --- a/internal/config/launch_command.go +++ b/internal/config/launch_command.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/gastownhall/gascity/internal/citylayout" + "github.com/gastownhall/gascity/internal/shellquote" ) // ProviderLaunchCommand is the fully composed provider command plus any @@ -35,27 +36,32 @@ func BuildProviderLaunchCommand(cityPath string, resolved *ResolvedProvider, opt } command := providerLaunchBaseCommand(resolved, transport) - if len(resolved.OptionsSchema) > 0 { - mergedOptions := make(map[string]string, len(resolved.EffectiveDefaults)+len(optionOverrides)) - for key, value := range resolved.EffectiveDefaults { - mergedOptions[key] = value - } - for key, value := range optionOverrides { - if key == "initial_message" { - continue - } - mergedOptions[key] = value - } - if len(mergedOptions) > 0 { - mergedArgs, err := ResolveExplicitOptions(resolved.OptionsSchema, mergedOptions) - if err != nil { - return ProviderLaunchCommand{}, err - } - command = ReplaceSchemaFlags(command, resolved.OptionsSchema, mergedArgs) + if len(resolved.OptionsSchema) > 0 && hasProviderOptionValues(resolved, optionOverrides) { + mergedArgs, err := providerOptionArgs(resolved, optionOverrides) + if err != nil { + return ProviderLaunchCommand{}, err } + command = ReplaceSchemaFlags(command, resolved.OptionsSchema, mergedArgs) } - return appendProviderSettings(cityPath, resolved.Name, command), nil + return appendProviderSettings(cityPath, providerSettingsFamily(resolved), command), nil +} + +// BuildProviderResumeCommand applies schema-managed option overrides to a +// provider's explicit resume_command template. +func BuildProviderResumeCommand(resolved *ResolvedProvider, optionOverrides map[string]string) (string, error) { + if resolved == nil { + return "", fmt.Errorf("resolved provider is nil") + } + command := strings.TrimSpace(resolved.ResumeCommand) + if command == "" || len(resolved.OptionsSchema) == 0 || !hasSchemaOptionOverrides(optionOverrides) { + return command, nil + } + mergedArgs, err := providerOptionArgs(resolved, optionOverrides) + if err != nil { + return "", err + } + return replaceResumeSchemaFlags(command, resolved.ResumeFlag, resolved.ResumeStyle, resolved.OptionsSchema, mergedArgs), nil } // BuildProviderLaunchCommandWithoutOptions composes the transport-specific @@ -73,7 +79,7 @@ func BuildProviderLaunchCommandWithoutOptions(cityPath string, resolved *Resolve if !IsValidSessionTransport(transport) { return ProviderLaunchCommand{}, fmt.Errorf("unknown session transport %q", strings.TrimSpace(transport)) } - return appendProviderSettings(cityPath, resolved.Name, providerLaunchBaseCommand(resolved, transport)), nil + return appendProviderSettings(cityPath, providerSettingsFamily(resolved), providerLaunchBaseCommand(resolved, transport)), nil } func providerLaunchBaseCommand(resolved *ResolvedProvider, transport string) string { @@ -87,6 +93,63 @@ func providerLaunchBaseCommand(resolved *ResolvedProvider, transport string) str } } +func providerOptionArgs(resolved *ResolvedProvider, optionOverrides map[string]string) ([]string, error) { + if resolved == nil || len(resolved.OptionsSchema) == 0 { + return nil, nil + } + mergedOptions := make(map[string]string, len(resolved.EffectiveDefaults)+len(optionOverrides)) + for key, value := range resolved.EffectiveDefaults { + mergedOptions[key] = value + } + for key, value := range optionOverrides { + if key == "initial_message" { + continue + } + mergedOptions[key] = value + } + if len(mergedOptions) == 0 { + return nil, nil + } + return ResolveExplicitOptions(resolved.OptionsSchema, mergedOptions) +} + +func hasProviderOptionValues(resolved *ResolvedProvider, optionOverrides map[string]string) bool { + if resolved != nil && len(resolved.EffectiveDefaults) > 0 { + return true + } + return hasSchemaOptionOverrides(optionOverrides) +} + +func hasSchemaOptionOverrides(optionOverrides map[string]string) bool { + for key := range optionOverrides { + if key != "initial_message" { + return true + } + } + return false +} + +func replaceResumeSchemaFlags(command, resumeFlag, resumeStyle string, schema []ProviderOption, overrideArgs []string) string { + stripped := StripFlags(command, CollectAllSchemaFlags(schema)) + if len(overrideArgs) == 0 { + return unquoteSessionKeyTemplate(stripped) + } + if resumeStyle == "subcommand" && resumeFlag != "" { + tokens := shellquote.Split(stripped) + insertAt := subcommandResumeInsertIndex(tokens, resumeFlag) + out := make([]string, 0, len(tokens)+len(overrideArgs)) + out = append(out, tokens[:insertAt]...) + out = append(out, overrideArgs...) + out = append(out, tokens[insertAt:]...) + return unquoteSessionKeyTemplate(shellquote.Join(out)) + } + return unquoteSessionKeyTemplate(stripped + " " + shellquote.Join(overrideArgs)) +} + +func unquoteSessionKeyTemplate(command string) string { + return strings.ReplaceAll(command, "'{{.SessionKey}}'", "{{.SessionKey}}") +} + func appendProviderSettings(cityPath, providerName, command string) ProviderLaunchCommand { settingsPath, settingsRel := ProviderSettingsSource(cityPath, providerName) if settingsPath != "" { @@ -100,6 +163,18 @@ func appendProviderSettings(cityPath, providerName, command string) ProviderLaun } } +func providerSettingsFamily(resolved *ResolvedProvider) string { + if resolved == nil { + return "" + } + if family := strings.TrimSpace(resolved.BuiltinAncestor); family != "" { + return family + } + // Keep settings discovery aligned with resolvedProviderLaunchFamily in + // cmd/gc: deprecated Kind is descriptive metadata, not launch family. + return strings.TrimSpace(resolved.Name) +} + // ProviderSettingsSource returns the provider-owned settings file that should // be passed to the launched process, plus the relative destination used when // staging that file into remote runtimes. diff --git a/internal/config/launch_command_test.go b/internal/config/launch_command_test.go index 2cf23d3708..94b4be0615 100644 --- a/internal/config/launch_command_test.go +++ b/internal/config/launch_command_test.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "testing" ) @@ -152,3 +153,65 @@ func TestBuildProviderLaunchCommandWithoutOptionsSkipsDefaultsButKeepsSettings(t t.Fatalf("SettingsRel = %q, want %q", got.SettingsRel, filepath.Join(".gc", "settings.json")) } } + +func TestBuildProviderLaunchCommandWithoutOptionsUsesBuiltinAncestorForSettings(t *testing.T) { + dir := t.TempDir() + runtimeDir := filepath.Join(dir, ".gc") + if err := os.MkdirAll(runtimeDir, 0o755); err != nil { + t.Fatal(err) + } + settingsPath := filepath.Join(runtimeDir, "settings.json") + if err := os.WriteFile(settingsPath, []byte(`{}`), 0o644); err != nil { + t.Fatal(err) + } + + rp := &ResolvedProvider{ + Name: "claude-max", + BuiltinAncestor: "claude", + Command: "aimux", + Args: []string{"run", "claude", "--"}, + } + + got, err := BuildProviderLaunchCommandWithoutOptions(dir, rp, "") + if err != nil { + t.Fatalf("BuildProviderLaunchCommandWithoutOptions: %v", err) + } + + if want := fmt.Sprintf("--settings %q", settingsPath); !strings.Contains(got.Command, want) { + t.Fatalf("Command = %q, want settings arg %q", got.Command, want) + } + if count := strings.Count(got.Command, "--settings"); count != 1 { + t.Fatalf("Command has %d --settings flags, want 1: %q", count, got.Command) + } + if got.SettingsPath != settingsPath { + t.Fatalf("SettingsPath = %q, want %q", got.SettingsPath, settingsPath) + } +} + +func TestBuildProviderLaunchCommandWithoutOptionsIgnoresDeprecatedKindForSettings(t *testing.T) { + dir := t.TempDir() + runtimeDir := filepath.Join(dir, ".gc") + if err := os.MkdirAll(runtimeDir, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(runtimeDir, "settings.json"), []byte(`{}`), 0o644); err != nil { + t.Fatal(err) + } + + rp := &ResolvedProvider{ + Name: "custom-provider", + Kind: "claude", + Command: "custom-provider", + } + + got, err := BuildProviderLaunchCommandWithoutOptions(dir, rp, "") + if err != nil { + t.Fatalf("BuildProviderLaunchCommandWithoutOptions: %v", err) + } + if strings.Contains(got.Command, "--settings") { + t.Fatalf("Command = %q, want no settings from deprecated Kind fallback", got.Command) + } + if got.SettingsPath != "" || got.SettingsRel != "" { + t.Fatalf("unexpected settings source from deprecated Kind fallback: %#v", got) + } +} diff --git a/internal/session/chat.go b/internal/session/chat.go index 3cca55062d..9e36b2c1d7 100644 --- a/internal/session/chat.go +++ b/internal/session/chat.go @@ -121,6 +121,8 @@ var ( ErrSessionClosed = errors.New("session is closed") // ErrSessionInactive reports that the requested session has no live runtime. ErrSessionInactive = errors.New("session is not active") + // ErrSessionActive reports that the requested session currently has or is starting a live runtime. + ErrSessionActive = errors.New("session is active") // ErrResumeRequired reports that the session cannot be resumed without an // explicit resume command. ErrResumeRequired = errors.New("session requires resume command") @@ -148,6 +150,11 @@ var ( sessionMutationLocks = map[string]*sessionMutationLockEntry{} ) +// WithSessionMutationLock serializes metadata mutations for one session bead. +func WithSessionMutationLock(id string, fn func() error) error { + return withSessionMutationLock(id, fn) +} + func withSessionMutationLock(id string, fn func() error) error { lock := acquireSessionMutationLock(id) defer releaseSessionMutationLock(id, lock) diff --git a/internal/session/manager.go b/internal/session/manager.go index 079501c689..53b5674c15 100644 --- a/internal/session/manager.go +++ b/internal/session/manager.go @@ -9,6 +9,7 @@ package session import ( "context" "crypto/rand" + "encoding/json" "errors" "fmt" "log" @@ -16,6 +17,7 @@ import ( "time" "github.com/gastownhall/gascity/internal/beads" + "github.com/gastownhall/gascity/internal/clock" "github.com/gastownhall/gascity/internal/runtime" ) @@ -135,6 +137,7 @@ type Manager struct { sp runtime.Provider cityPath string transportResolver func(template, provider string) transportResolution + clk clock.Clock } // PruneResult reports which sessions were pruned and which queued wait nudges @@ -222,6 +225,13 @@ func (m *Manager) persistTransport(id, provider, transport string) { _ = m.store.SetMetadata(id, "transport", transport) } +func (m *Manager) now() time.Time { + if m != nil && m.clk != nil { + return m.clk.Now() + } + return time.Now() +} + func (m *Manager) routeACPIfNeeded(provider, transport, sessName string) func() { if normalizeTransport(provider, transport) != "acp" { return nil @@ -1115,6 +1125,92 @@ func (m *Manager) UpdatePresentation(id string, title *string, alias *string) er }) } +// UpdateTemplateOverrides merges option overrides into the session metadata. +func (m *Manager) UpdateTemplateOverrides(id string, updates map[string]string) (map[string]string, error) { + var merged map[string]string + err := withSessionMutationLock(id, func() error { + b, sessName, err := m.loadSessionBead(id, true) + if err != nil { + return err + } + state := State(b.Metadata["state"]) + if IsTemplateOverrideRuntimeActive(state) || templateOverrideWakeInFlight(b.Metadata, state, m.now()) || (strings.TrimSpace(sessName) != "" && m.sp != nil && m.sp.IsRunning(sessName)) { + return fmt.Errorf("%w: template overrides apply only before the next launch", ErrSessionActive) + } + overrides, err := ParseTemplateOverrides(b.Metadata) + if err != nil { + log.Printf("session %s: repairing malformed template_overrides: %v", id, err) + overrides = nil + } + if overrides == nil { + overrides = make(map[string]string, len(updates)) + } + for key, value := range updates { + overrides[key] = value + } + raw, err := json.Marshal(overrides) + if err != nil { + return fmt.Errorf("marshal template_overrides: %w", err) + } + metadata := map[string]string{"template_overrides": string(raw)} + for key, value := range updates { + if key == "initial_message" { + continue + } + metadata["opt_"+key] = value + } + if err := m.store.SetMetadataBatch(id, metadata); err != nil { + return err + } + merged = make(map[string]string, len(overrides)) + for key, value := range overrides { + merged[key] = value + } + return nil + }) + if err != nil { + return nil, err + } + return merged, nil +} + +// IsTemplateOverrideRuntimeActive reports whether a session state is too live +// for template override changes that only apply on the next launch. +func IsTemplateOverrideRuntimeActive(state State) bool { + switch state { + case StateActive, StateAwake, StateCreating, StateDraining, StateQuarantined: + return true + default: + return false + } +} + +const templateOverrideWakeInFlightGrace = time.Minute + staleKeyDetectDelay + 5*time.Second + +func templateOverrideWakeInFlight(metadata map[string]string, state State, now time.Time) bool { + if metadata == nil { + return false + } + switch state { + case StateFailedCreate, StateDrained, StateArchived: + return false + } + if strings.TrimSpace(metadata["pending_create_claim"]) == "true" { + return true + } + lastWoke := strings.TrimSpace(metadata["last_woke_at"]) + if lastWoke == "" { + return false + } + started, err := time.Parse(time.RFC3339, lastWoke) + if err != nil { + return false + } + // PreWakePatch records last_woke_at before the reconciler can observe + // runtime liveness; keep overrides locked out through that startup window. + return now.UTC().Before(started.UTC().Add(templateOverrideWakeInFlightGrace)) +} + // Prune closes suspended sessions whose suspension time is before the given // cutoff. Active and already-closed sessions are never pruned. // Returns the number of sessions pruned. diff --git a/internal/session/manager_test.go b/internal/session/manager_test.go index 12d6d38ebd..8fe3c03d2f 100644 --- a/internal/session/manager_test.go +++ b/internal/session/manager_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/gastownhall/gascity/internal/beads" + "github.com/gastownhall/gascity/internal/clock" "github.com/gastownhall/gascity/internal/runtime" sessionauto "github.com/gastownhall/gascity/internal/runtime/auto" "github.com/gastownhall/gascity/internal/sessionlog" @@ -267,6 +268,255 @@ func TestCreate(t *testing.T) { } } +func TestUpdateTemplateOverridesRejectsRunningSessionUnderLock(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + + if _, err := mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}); !errors.Is(err, ErrSessionActive) { + t.Fatalf("UpdateTemplateOverrides active error = %v, want ErrSessionActive", err) + } +} + +func TestUpdateTemplateOverridesRejectsLiveRuntimeEvenWhenStateLooksDormant(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := store.SetMetadataBatch(info.ID, map[string]string{"state": string(StateAsleep)}); err != nil { + t.Fatalf("SetMetadataBatch: %v", err) + } + + if _, err := mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}); !errors.Is(err, ErrSessionActive) { + t.Fatalf("UpdateTemplateOverrides live-runtime error = %v, want ErrSessionActive", err) + } +} + +func TestUpdateTemplateOverridesAllowsSuspendedSession(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := mgr.Suspend(info.ID); err != nil { + t.Fatalf("Suspend: %v", err) + } + + overrides, err := mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}) + if err != nil { + t.Fatalf("UpdateTemplateOverrides suspended: %v", err) + } + if got := overrides["permission_mode"]; got != "auto-edit" { + t.Fatalf("permission_mode = %q, want auto-edit", got) + } + b, err := store.Get(info.ID) + if err != nil { + t.Fatalf("Get: %v", err) + } + if got := b.Metadata["opt_permission_mode"]; got != "auto-edit" { + t.Fatalf("opt_permission_mode = %q, want auto-edit", got) + } +} + +func TestUpdateTemplateOverridesRejectsRecentWakeInFlight(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := mgr.Suspend(info.ID); err != nil { + t.Fatalf("Suspend: %v", err) + } + if err := store.SetMetadata(info.ID, "last_woke_at", time.Now().UTC().Format(time.RFC3339)); err != nil { + t.Fatalf("SetMetadata(last_woke_at): %v", err) + } + + _, err = mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}) + if !errors.Is(err, ErrSessionActive) { + t.Fatalf("UpdateTemplateOverrides recent wake err = %v, want ErrSessionActive", err) + } +} + +func TestUpdateTemplateOverridesRejectsPendingCreateClaim(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := mgr.Suspend(info.ID); err != nil { + t.Fatalf("Suspend: %v", err) + } + if err := store.SetMetadata(info.ID, "pending_create_claim", "true"); err != nil { + t.Fatalf("SetMetadata(pending_create_claim): %v", err) + } + + _, err = mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}) + if !errors.Is(err, ErrSessionActive) { + t.Fatalf("UpdateTemplateOverrides pending create err = %v, want ErrSessionActive", err) + } +} + +func TestUpdateTemplateOverridesWakeInFlightGraceBoundary(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + mgr.clk = &clock.Fake{Time: time.Date(2030, 1, 1, 12, 0, 0, 0, time.UTC)} + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := mgr.Suspend(info.ID); err != nil { + t.Fatalf("Suspend: %v", err) + } + + insideGrace := mgr.clk.Now().Add(-templateOverrideWakeInFlightGrace + time.Second).UTC().Format(time.RFC3339) + if err := store.SetMetadata(info.ID, "last_woke_at", insideGrace); err != nil { + t.Fatalf("SetMetadata(last_woke_at inside): %v", err) + } + _, err = mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}) + if !errors.Is(err, ErrSessionActive) { + t.Fatalf("UpdateTemplateOverrides inside grace err = %v, want ErrSessionActive", err) + } + + outsideGrace := mgr.clk.Now().Add(-templateOverrideWakeInFlightGrace - time.Second).UTC().Format(time.RFC3339) + if err := store.SetMetadata(info.ID, "last_woke_at", outsideGrace); err != nil { + t.Fatalf("SetMetadata(last_woke_at outside): %v", err) + } + overrides, err := mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}) + if err != nil { + t.Fatalf("UpdateTemplateOverrides outside grace: %v", err) + } + if got := overrides["permission_mode"]; got != "auto-edit" { + t.Fatalf("permission_mode = %q, want auto-edit", got) + } +} + +func TestUpdateTemplateOverridesAllowsOldWakeTimestamp(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := mgr.Suspend(info.ID); err != nil { + t.Fatalf("Suspend: %v", err) + } + oldWake := time.Now().UTC().Add(-2 * time.Minute).Format(time.RFC3339) + if err := store.SetMetadata(info.ID, "last_woke_at", oldWake); err != nil { + t.Fatalf("SetMetadata(last_woke_at): %v", err) + } + + overrides, err := mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}) + if err != nil { + t.Fatalf("UpdateTemplateOverrides old wake: %v", err) + } + if got := overrides["permission_mode"]; got != "auto-edit" { + t.Fatalf("permission_mode = %q, want auto-edit", got) + } +} + +func TestUpdateTemplateOverridesUsesManagerClockForWakeWindow(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + mgr.clk = &clock.Fake{Time: time.Date(2030, 1, 1, 12, 0, 0, 0, time.UTC)} + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := mgr.Suspend(info.ID); err != nil { + t.Fatalf("Suspend: %v", err) + } + oldForManagerClock := mgr.clk.Now().Add(-2 * time.Minute).UTC().Format(time.RFC3339) + if err := store.SetMetadata(info.ID, "last_woke_at", oldForManagerClock); err != nil { + t.Fatalf("SetMetadata(last_woke_at): %v", err) + } + + overrides, err := mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}) + if err != nil { + t.Fatalf("UpdateTemplateOverrides old fake-clock wake: %v", err) + } + if got := overrides["permission_mode"]; got != "auto-edit" { + t.Fatalf("permission_mode = %q, want auto-edit", got) + } +} + +func TestUpdateTemplateOverridesAllowsFailedCreateWithRecentWake(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + mgr.clk = &clock.Fake{Time: time.Date(2030, 1, 1, 12, 0, 0, 0, time.UTC)} + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := mgr.Suspend(info.ID); err != nil { + t.Fatalf("Suspend: %v", err) + } + if err := store.SetMetadataBatch(info.ID, map[string]string{ + "state": string(StateFailedCreate), + "last_woke_at": mgr.clk.Now().UTC().Format(time.RFC3339), + }); err != nil { + t.Fatalf("SetMetadataBatch: %v", err) + } + + overrides, err := mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}) + if err != nil { + t.Fatalf("UpdateTemplateOverrides failed-create recent wake: %v", err) + } + if got := overrides["permission_mode"]; got != "auto-edit" { + t.Fatalf("permission_mode = %q, want auto-edit", got) + } +} + +func TestUpdateTemplateOverridesRepairsMalformedMetadata(t *testing.T) { + store := beads.NewMemStore() + sp := runtime.NewFake() + mgr := NewManager(store, sp) + + info, err := mgr.Create(context.Background(), "helper", "my chat", "claude", "/tmp", "claude", nil, ProviderResume{}, runtime.Config{}) + if err != nil { + t.Fatalf("Create: %v", err) + } + if err := mgr.Suspend(info.ID); err != nil { + t.Fatalf("Suspend: %v", err) + } + if err := store.SetMetadata(info.ID, "template_overrides", "{not-json"); err != nil { + t.Fatalf("SetMetadata: %v", err) + } + + overrides, err := mgr.UpdateTemplateOverrides(info.ID, map[string]string{"permission_mode": "auto-edit"}) + if err != nil { + t.Fatalf("UpdateTemplateOverrides malformed metadata: %v", err) + } + if got := overrides["permission_mode"]; got != "auto-edit" { + t.Fatalf("permission_mode = %q, want auto-edit", got) + } +} + func TestCreateConfirmsStartedStateWithoutControllerDriftHash(t *testing.T) { store := beads.NewMemStore() sp := runtime.NewFake() diff --git a/internal/session/provider_resolution.go b/internal/session/provider_resolution.go new file mode 100644 index 0000000000..d25a14c92c --- /dev/null +++ b/internal/session/provider_resolution.go @@ -0,0 +1,29 @@ +package session + +import "strings" + +// UseAgentTemplateForProviderResolution reports whether a session should +// resolve provider options through its agent template instead of treating the +// persisted Template field as a raw provider name. The provider-name arguments +// are accepted for call-site symmetry but do not disqualify non-manual legacy +// sessions when the agent template still exists. +func UseAgentTemplateForProviderResolution(sessionKind string, metadata map[string]string, _, _ string, templateFound bool) bool { + sessionKind = strings.TrimSpace(sessionKind) + switch sessionKind { + case "provider": + return false + case "agent": + return true + } + if metadata == nil { + return true + } + if strings.TrimSpace(metadata["agent_name"]) != "" || + strings.TrimSpace(metadata[NamedSessionMetadataKey]) == "true" { + return true + } + if strings.TrimSpace(metadata["session_origin"]) == "manual" { + return false + } + return templateFound +} diff --git a/internal/session/provider_resolution_test.go b/internal/session/provider_resolution_test.go new file mode 100644 index 0000000000..d2c36b1d7b --- /dev/null +++ b/internal/session/provider_resolution_test.go @@ -0,0 +1,107 @@ +package session + +import "testing" + +func TestUseAgentTemplateForProviderResolution(t *testing.T) { + tests := []struct { + name string + kind string + metadata map[string]string + persistedProvider string + templateProvider string + templateFound bool + want bool + }{ + { + name: "explicit provider kind skips agent template", + kind: "provider", + want: false, + }, + { + name: "explicit agent kind uses agent template", + kind: "agent", + want: true, + }, + { + name: "legacy nil metadata preserves agent template behavior", + want: true, + }, + { + name: "configured named session uses agent template", + metadata: map[string]string{ + NamedSessionMetadataKey: "true", + "session_origin": "manual", + }, + want: true, + }, + { + name: "manual provider session with template collision skips agent template", + metadata: map[string]string{ + "session_origin": "manual", + }, + persistedProvider: "stored-provider", + templateProvider: "agent-provider", + templateFound: true, + want: false, + }, + { + name: "manual session with matching provider but no agent metadata stays provider backed", + metadata: map[string]string{ + "session_origin": "manual", + }, + persistedProvider: "agent-provider", + templateProvider: "agent-provider", + templateFound: true, + want: false, + }, + { + name: "manual session with agent name preserves agent template", + metadata: map[string]string{ + "agent_name": "worker", + "session_origin": "manual", + }, + persistedProvider: "agent-provider", + templateProvider: "agent-provider", + templateFound: true, + want: true, + }, + { + name: "manual session without matching template is provider backed", + metadata: map[string]string{ + "session_origin": "manual", + }, + persistedProvider: "agent-provider", + templateProvider: "", + templateFound: false, + want: false, + }, + { + name: "non-manual legacy metadata preserves agent template behavior", + metadata: map[string]string{ + "session_origin": "ephemeral", + }, + persistedProvider: "agent-provider", + templateProvider: "agent-provider", + templateFound: true, + want: true, + }, + { + name: "non-manual legacy metadata with provider mismatch preserves agent template behavior", + metadata: map[string]string{ + "session_origin": "ephemeral", + }, + persistedProvider: "stored-provider", + templateProvider: "agent-provider", + templateFound: true, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := UseAgentTemplateForProviderResolution(tt.kind, tt.metadata, tt.persistedProvider, tt.templateProvider, tt.templateFound) + if got != tt.want { + t.Fatalf("UseAgentTemplateForProviderResolution() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/test/acceptance/session_test.go b/test/acceptance/session_test.go index acd087c7db..89772656cf 100644 --- a/test/acceptance/session_test.go +++ b/test/acceptance/session_test.go @@ -104,10 +104,8 @@ func TestSessionDefaultNamedSession(t *testing.T) { if strings.Contains(out, "No sessions found") { t.Errorf("expected default named session on fresh city, got:\n%s", out) } - for _, want := range []string{"mayor"} { - if !strings.Contains(out, want) { - t.Errorf("expected %q in default named session list, got:\n%s", want, out) - } + if !strings.Contains(out, "mayor") { + t.Errorf("expected default named session in list, got:\n%s", out) } if !strings.Contains(out, string(session.StateCreating)) && !strings.Contains(out, string(session.StateActive)) &&