scitex_agent_container API Reference
Top-level package surface re-exported from scitex_agent_container.
Use scitex-agent-container list-python-apis for the authoritative
runtime enumeration.
SciTeX Agent Container – Declarative agent management.
Provides a YAML-based framework for defining, managing, and orchestrating AI coding agent instances across container runtimes.
Public surface — CLI-tree-shaped noun submodules:
import scitex_agent_container as sac
sac.agent.list() # `sac agent list`
sac.agent.start("head-nas") # `sac agent start head-nas`
sac.db.query(table="instances") # `sac db query --table=instances`
sac.host.list() # `sac host list`
sac.skills.get("02_quick-start") # `sac skills get 02_quick-start`
Each noun submodule (agent, db, host, image, template, account, skills, mcp) re-exports its verbs under bare names that mirror the CLI subcommand tree. The same function objects power both the Python API and the MCP server (per scitex MCP §6 parity).
Lifecycle helpers that take a shared Registry instance live at
scitex_agent_container._lifecycle.lifecycle for callers that
need them. The submodule verbs go through the CLI for JSON-friendly
input/output.
Config
YAML config loading and validation for agent definitions.
- Public API:
AgentConfig, load_config, validate_config, resolve_config ContainerSpec, ClaudeSpec, HealthSpec, WatchdogSpec, RestartSpec, SkillsSpec, StartupCommand
RemoteSpec and the spec.remote field were deleted in WI-6
(handoff §6, 2026-05-20). Cross-host placement is via spec.host;
the old SSH-dispatch path is retired.
- class scitex_agent_container.config.AgentConfig(name, runtime='tui', provider='anthropic', image='', model='sonnet', workdir='', python_venv='', env=<factory>, env_files=<factory>, screen_name='', labels=<factory>, container=<factory>, claude=<factory>, health=<factory>, watchdog=<factory>, restart=<factory>, autonomous=<factory>, apptainer=<factory>, hooks=<factory>, listen=<factory>, extensions=<factory>, skills=<factory>, context_management=<factory>, startup_commands=<factory>, startup_prompts=<factory>, exclude_hooks=<factory>, exclude_skills=<factory>, mcp_servers=<factory>, multiplexer='tmux', hosts_spec=<factory>, scheduling=<factory>, config_path='', mounts=<factory>, user='', a2a=<factory>, comms=<factory>, lineage=<factory>, kind='Agent', proxy=None, to_home='./to_home')[source]
Bases:
objectParsed agent configuration from a YAML definition file.
- container: ContainerSpec
- claude: ClaudeSpec
- health: HealthSpec
- watchdog: WatchdogSpec
- restart: RestartSpec
- autonomous: AutonomousSpec
- apptainer: ApptainerSpec
- listen: list[ListenPort]
- skills: SkillsSpec
- context_management: ContextManagementConfig
- startup_commands: list[StartupCommand]
- scheduling: SchedulingSpec
- a2a: A2ASpec
- comms: CommsSpec
- lineage: LineageSpec
- __init__(name, runtime='tui', provider='anthropic', image='', model='sonnet', workdir='', python_venv='', env=<factory>, env_files=<factory>, screen_name='', labels=<factory>, container=<factory>, claude=<factory>, health=<factory>, watchdog=<factory>, restart=<factory>, autonomous=<factory>, apptainer=<factory>, hooks=<factory>, listen=<factory>, extensions=<factory>, skills=<factory>, context_management=<factory>, startup_commands=<factory>, startup_prompts=<factory>, exclude_hooks=<factory>, exclude_skills=<factory>, mcp_servers=<factory>, multiplexer='tmux', hosts_spec=<factory>, scheduling=<factory>, config_path='', mounts=<factory>, user='', a2a=<factory>, comms=<factory>, lineage=<factory>, kind='Agent', proxy=None, to_home='./to_home')
- class scitex_agent_container.config.ClaudeSpec(model='', channels=<factory>, flags=<factory>, raw_options=<factory>, session='fresh', continue_max_age_minutes=None, resume_id='', auto_accept=True, account='', credentials_file='', credentials_files=<factory>, provider=None)[source]
Bases:
object- provider: ProviderSpec | None = None
- __init__(model='', channels=<factory>, flags=<factory>, raw_options=<factory>, session='fresh', continue_max_age_minutes=None, resume_id='', auto_accept=True, account='', credentials_file='', credentials_files=<factory>, provider=None)
- class scitex_agent_container.config.ContainerSpec(runtime='none', image='scitex-agent-container:latest', volumes=<factory>, network='host', mount_host_claude=False)[source]
Bases:
object- __init__(runtime='none', image='scitex-agent-container:latest', volumes=<factory>, network='host', mount_host_claude=False)
- class scitex_agent_container.config.ContextManagementConfig(trigger_at_percent=70.0, strategy='noop', warn_before_n_checks=0, check_interval_seconds=300, state_file='~/.scitex/agent-container/state/<agent>.json')[source]
Bases:
objectContext-lifecycle policy for an agent.
Defaults mirror
strategy="noop"so absence of thecontext_managementblock preserves existing behavior (sensor disabled).- __init__(trigger_at_percent=70.0, strategy='noop', warn_before_n_checks=0, check_interval_seconds=300, state_file='~/.scitex/agent-container/state/<agent>.json')
- class scitex_agent_container.config.HealthSpec(enabled=False, interval=30, timeout=5, method='multiplexer-alive')[source]
Bases:
object- __init__(enabled=False, interval=30, timeout=5, method='multiplexer-alive')
- class scitex_agent_container.config.HookSpec(pre_start=<factory>, post_start=<factory>, pre_stop=<factory>, post_stop=<factory>, on_compact=<factory>, on_restart=<factory>, on_diff=<factory>)[source]
Bases:
objectAll hook points supported by the container.
Each entry is a list of opaque commands — shell strings or http(s) URLs. The container executes them fire-and-forget; errors are logged but never raised to the caller. Absent keys default to empty lists (feature disabled).
- __init__(pre_start=<factory>, post_start=<factory>, pre_stop=<factory>, post_stop=<factory>, on_compact=<factory>, on_restart=<factory>, on_diff=<factory>)
- class scitex_agent_container.config.HostsSpec(host='', hosts=<factory>)[source]
Bases:
objectWhere an agent should run, in either singleton or multi-instance form.
Mutually exclusive — exactly one of
hostorhostsmay be set:host(singular) — exactly one instance runs:empty / absent: local singleton (runs wherever sac is invoked)
string: pinned to that host
list: priority order; first available host wins (fallback chain)
hosts(plural) — multiple instances run, one per host:“all”: one per fleet host (replaces the old per-host mode)
list of host names: one per listed host (subset)
Validator (in
_validation.py) enforces mutual exclusion + types. Loader composes effective ids:hoststriggers the<name>-<HOST>suffix;hostkeeps the bare name.- __init__(host='', hosts=<factory>)
- class scitex_agent_container.config.ListenPort(port=0, proto='tcp', path='', name='', owner='')[source]
Bases:
objectDeclaration of a port/socket an external tool binds on behalf of an agent.
The container NEVER binds these — it just validates the shape and echoes them in
status --jsonso orchestrators can see what sidecars are expected to exist.owneris free-form (e.g."orochi") to identify the plugin that actually listens.- __init__(port=0, proto='tcp', path='', name='', owner='')
- class scitex_agent_container.config.ProviderSpec(base_url='', auth_token_env='', allowed_tools=<factory>)[source]
Bases:
objectVendor-agnostic backend override for the Claude SDK session.
When set under
spec.claude.provider, the agent’s SDK session talks to an Anthropic-SDK-compatible backend OTHER than Anthropic (DeepSeek, a self-hosted gateway, etc.) using a never-expiring, login-free API key instead of Anthropic OAuth. Lets bulk fleet work run on a cheap backend without burning Max-plan quota.The runtime injects three env vars into the container at start (see
runtimes/_apptainer_provider.py):ANTHROPIC_BASE_URL←base_urlSAC_ANTHROPIC_API_KEY← the host value of$<auth_token_env>(bridged toANTHROPIC_API_KEYfor the SDK by sac’s existing auth handoff). Fails loud at start if the env var is unset.CLAUDE_CONFIG_DIR← a clean per-agent dir — the conflict-breaker so the OAuth.credentials.jsonbind cannot win (apptainer--envis last-wins).
The model id is the provider’s own (e.g.
deepseek-chat); the claude-* regex inconfig._validationis relaxed whenever a provider is declared.Mutually exclusive with
ClaudeSpec.account(OAuth pin) — an API-key backend needs no OAuth, so declaring both is a config error the runtime rejects loudly.- base_url: str = ''
Anthropic-compatible base URL, e.g.
https://api.deepseek.com/anthropic. Required when the provider block is present.
- __init__(base_url='', auth_token_env='', allowed_tools=<factory>)
- auth_token_env: str = ''
NAME of the host env var holding the API key (e.g.
DEEPSEEK_API_KEY) — NOT the key value. The operator sources the secret file; sac reads the env var’s value at start and never logs it. Required when the provider block is present.
- allowed_tools: list[str]
Whitelist of built-in Claude Code tools to REGISTER when this provider is active. Maps directly to
ClaudeAgentOptions.tools(the SDK--tools <csv>knob, seeclaude_agent_sdk/_internal/transport/subprocess_cli.py:241-250), which controls the REGISTERED tool set rather than the use-allow list (--allowedTools/--disallowedToolsonly affect permission, not whether the tool’s JSON-schema ships in the outbound API request body — confirmed empirically by clew bm172 cohort v7 bind-test 2026-06-06).Why this matters: an Anthropic-API shim (LiteLLM / vLLM / gateway) may not recognize newer Claude Code built-ins (
ExitPlanMode,BashOutput,KillShellwere added after LiteLLM 1.52.16). When the shim sees such a tool in the outboundtools[]it falls through its pydantic Union to the last subclass (AnthropicComputerTool), which requiresdisplay_width_pxthat the unknown tool’s payload doesn’t have → 422 on every API call → capsule errors all 60 turns. Whitelisting only the shim-recognized tools suppresses the unrecognized builtins at REGISTRATION, so they never enter the outbound body.Empty list (the default) means “use the runner’s old-stable default whitelist when the provider is active, otherwise leave
toolsunset and let the CLI’s full default register” — the runner picks the safe shape automatically.The string-form provider (e.g.
provider: mimo) doesn’t carry this field today; the per-registryallowed_toolsplumbing is a follow-up. Operators on the string form fall back to the runner’s old-stable default until the registry plumbing lands.
- class scitex_agent_container.config.ProxySpec(upstream='', trust='untrusted', redact=<factory>, timeout_s=30.0)[source]
Bases:
objectConfiguration for kind: AgentProxy agents.
- upstream: str = ''
REQUIRED. Full URL to the upstream A2A AgentCard endpoint. Either an explicit .well-known path or a base URL (we’ll fetch
<base>/.well-known/agent-card.jsonif a base is given).
- trust: str = 'untrusted'
One of
untrusted(default — operator must opt in to anything more permissive),local-mesh(peers on the same private network you control),trusted(cryptographically verified — reserved for future mTLS work). Surfaced on the AgentCard’sx-scitex-agent-container.trustfield.
- redact: list[str]
Substring tokens; any inbound text field containing one is refused with HTTP 400. Cheap defense-in-depth against accidentally forwarding secrets to an untrusted upstream — NOT a substitute for proper output filtering at the source.
- __init__(upstream='', trust='untrusted', redact=<factory>, timeout_s=30.0)
- class scitex_agent_container.config.RestartSpec(policy='never', max_retries=3, backoff_initial=30, backoff_max=300, backoff_multiplier=2, prune_on_stop=False)[source]
Bases:
object- __init__(policy='never', max_retries=3, backoff_initial=30, backoff_max=300, backoff_multiplier=2, prune_on_stop=False)
- class scitex_agent_container.config.SchedulingSpec(mode='per-host', preferred_host='', fallback_hosts=<factory>)[source]
Bases:
objectFleet-wide scheduling policy for an agent (shared-host layout).
modecontrols effective-id composition and launch-skip behavior:per-host(default): agent is started on every host that runssac agent start <name>; the effective id is<metadata.name>-<HOST>unless the name already ends with-<HOST>.singleton: exactly one instance fleet-wide. The effective id stays as the bare<metadata.name>. Only launched onpreferred-host; on other hosts the launch is a no-op.
fallback-hostsis recorded for observability but not acted on automatically — manual failover today.- __init__(mode='per-host', preferred_host='', fallback_hosts=<factory>)
- class scitex_agent_container.config.SkillsSpec(required=<factory>, available=<factory>, injection_mode='at-import', match_by=<factory>, match_style='exact')[source]
Bases:
object- __init__(required=<factory>, available=<factory>, injection_mode='at-import', match_by=<factory>, match_style='exact')
- class scitex_agent_container.config.StartupCommand(delay=0, command='')[source]
Bases:
object- __init__(delay=0, command='')
- class scitex_agent_container.config.WatchdogSpec(enabled=False, interval=1.5, resp_y_n='1', resp_y_y_n='2', resp_waiting='/speak-and-call')[source]
Bases:
object- __init__(enabled=False, interval=1.5, resp_y_n='1', resp_y_y_n='2', resp_waiting='/speak-and-call')
- scitex_agent_container.config.compose_effective_name(raw_name, hosts_spec, hostname)[source]
Return the effective agent id given dir-derived name + host/hosts + host.
- Return type:
- Rules:
If
hosts:is set (multi-instance), append-<hostname>so each host’s instance has a unique id. Idempotent — names that already end with-<hostname>are not double-suffixed.Otherwise (
host:set, or both empty = local singleton): keep the bareraw_name. Singleton id stays stable across hosts.
- scitex_agent_container.config.load_config(path)[source]
Load and validate a YAML config, returning an AgentConfig.
Only
scitex-agent-container/v3is accepted. Older apiVersions (v1, v2) raise loud validation errors — no backward compatibility.- Return type:
- scitex_agent_container.config.resolve_config(name_or_path)[source]
Resolve agent name or path to a config file path.
Every agent lives in its own directory; the config file is always named
spec.yaml(orspec.yml). The flat-form<base>/<name>.yamland the legacy<base>/<name>/<name>.yamlare no longer accepted — sac is dir-as-SSoT.Search order for short names (no slash, no .yaml/.yml suffix):
Project-local — first
.scitex/agent-container/agents/found walking upward from cwd. Highest priority so checked-in test agents and CI fixtures override globals.~/.scitex/agent-container/agents/<name>/spec.yaml(sac install root).$SCITEX_AGENT_CONTAINER_YAML_DIRS(colon-separated extra dirs, each searched as<dir>/<name>/spec.yaml). Plugin port for downstream orchestrators to inject extra paths without sac knowing about them.
Scope (
$SAC_AGENT_SCOPE): if the requested NAME resolves in BOTH a project-local registry AND the fleet and the scope is unset, this raisesAmbiguousRegistryScope(a real collision) instead of silently preferring project-local. A name present in only one registry resolves with no error, even when both registry dirs exist.SAC_AGENT_SCOPE=usersearches only the fleet;=projectonly project-local.Pass an explicit path (with / or .yaml/.yml) to bypass the search entirely.
- Return type:
- scitex_agent_container.config.resolve_hostname(gethostname=<built-in function gethostname>)[source]
Return the canonical host label for this machine.
- Resolution order (first non-empty wins):
SCITEX_AGENT_CONTAINER_HOSTNAMEenv var (manual override).SCITEX_AGENT_CONTAINER_HOSTNAMEenv var.hostname_aliases[short hostname]fromshared/config.yamlor~/.scitex/agent-container/config.yaml.socket.gethostname()short form (identity fallback).
- Parameters:
gethostname (
Callable[[],str]) – Callable returning the raw OS hostname. Defaults tosocket.gethostname(production). Tests inject a callable returning a fixed string instead of patchingsocket.- Raises:
RuntimeError – If none of the sources produces a non-empty value. This should be practically impossible (
gethostname()returns something on any configured box) but is handled loudly rather than returning the empty string.- Return type:
Runtimes
Modular TUI prompt detection and response for Claude Code.
Each prompt handler defines: - name: identifier for logging - detect(content) -> bool: whether this prompt is visible - respond(send_keys) -> None: keystrokes to accept the prompt - priority: lower = checked first (default 10)
Add new handlers by appending to PROMPT_HANDLERS or calling register_prompt().
- class scitex_agent_container.runtimes.prompts.PromptHandler(name, detect, keys=<factory>, priority=10)[source]
Bases:
objectA single TUI prompt detector and responder.
- __init__(name, detect, keys=<factory>, priority=10)
- scitex_agent_container.runtimes.prompts._detect_bypass_permissions(content)[source]
Bypass Permissions mode prompt with radio selector.
- Return type:
- Matches:
“1. No, exit” “2. Yes, I accept” “Bypass Permissions” “Enter to confirm”
- scitex_agent_container.runtimes.prompts._detect_dev_channels(content)[source]
Development channels loading confirmation.
- Return type:
- Matches:
“1. I am using this for local development” “2. Exit” “development channels” or “dangerously-load-development-channels” “Enter to confirm”
- scitex_agent_container.runtimes.prompts.has_esc_cancel_modal(content)[source]
True iff an on-screen modal treats Esc as CANCEL / EXIT.
The fatal boot bug (card
sac-boot-automation-devchannels-modal-continue-compose-buffer): the stale-compose clear sendsEscape, but while the--dangerously-load- development-channelsconfirmation is up (”❯ 1. I am using this for local development … Enter to confirm · Esc to cancel”) anEscapeCANCELS the launch → claude exits → the tmux session DIES mid-boot. Any drain step that would sendEscape(the compose-buffer clear) MUST first verify no such cancelable modal is on screen.Detection is deliberately BROAD — a keystroke that kills the session is far costlier than a spurious “don’t-Esc” skip:
the explicit
Esc to cancelfooter any confirm-modal renders, ORthe dev-channels modal specifically (its footer wording has varied across Claude Code builds; match the option text too so a reworded footer still guards).
Pure + string-only so the drain-ordering guard is unit-testable without a live TUI.
- Return type:
- scitex_agent_container.runtimes.prompts._detect_thinking_effort(content)[source]
Thinking effort level selector.
- Return type:
- Matches:
“1. * Medium (recommended)” or similar “thinking” in various casings “Enter to confirm”
- scitex_agent_container.runtimes.prompts._detect_skip_permissions_yn(content)[source]
Legacy y/n text prompt for skip-permissions (older Claude Code).
Matches text-based y/n prompts without radio selector.
- Return type:
- scitex_agent_container.runtimes.prompts._detect_mcp_json_edit(content)[source]
Permission prompt when Claude tries to edit .mcp.json (runtime).
Matches “1. Yes” / “1. Proceed” / “1. Allow” + “.mcp.json” + “Enter to confirm”.
- Return type:
- scitex_agent_container.runtimes.prompts._detect_press_enter_continue(content)[source]
Generic ‘Press Enter to continue’ runtime pause (context-window warning, etc).
Uses a strict last-5-lines window to avoid scrollback false positives (per pane-state-patterns.md: classify against last 5 visible lines only). Excluded: active tool calls and numbered radio selectors.
- Return type:
- scitex_agent_container.runtimes.prompts._detect_file_trust(content)[source]
‘Do you trust the files in this folder?’ prompt (first-run or new cwd).
May appear when –dangerously-skip-permissions was not propagated to a subshell. Matches the LEGACY y/n text variant; the new radio-selector variant is handled by
_detect_file_trust_radio().- Return type:
- scitex_agent_container.runtimes.prompts._detect_file_trust_radio(content)[source]
Radio-selector variant of the file-trust prompt.
Claude Code (>= ~2.1.x) asks “Is this a project you created or one you trust?” with numbered options instead of the legacy y/n text prompt. Appears on the first launch in any un-trusted workdir — including every throwaway tempdir the Haiku integration test uses.
Matches the exact option strings to avoid firing on the bypass-permissions dialog (which also says “Enter to confirm”).
- Return type:
- scitex_agent_container.runtimes.prompts._detect_external_imports(content)[source]
External CLAUDE.md file imports prompt.
Appears when
CLAUDE.md(or.claude/CLAUDE.md) contains@<absolute-path>imports pointing OUTSIDE the agent’s workdir. Triggered by the at-import skill-injection mode (sac PR #74) when skills live in~/.claude/skills/or the package source trees rather than the workspace itself.- Return type:
- Matches:
“Allow external CLAUDE.md file imports?” “1. Yes, allow external imports” “Enter to confirm”
- scitex_agent_container.runtimes.prompts._detect_login_method(content)[source]
First-run login-method picker on a fresh HOME.
Appears when Claude Code can’t find OAuth credentials at
~/.claude/.credentials.json. Even withANTHROPIC_API_KEYset in env, the 2.1.x CLI still asks which auth mode to use before it checks the env var. Blocks startup until dismissed.Matches the exact option strings to avoid false positives on any user message that happens to say “login method”.
- Return type:
- scitex_agent_container.runtimes.prompts._detect_theme_selection(content)[source]
First-run theme selection prompt.
Appears only on a fresh HOME (no
~/.claude/saved theme). On dev machines it never shows, but in CI (a clean ubuntu VM) this is the first thing Claude Code asks. Blocks every downstream startup prompt until acknowledged.Matches the radio variant: “Choose the text style…” + numbered options starting with “1. Auto (match terminal)”.
- Return type:
- scitex_agent_container.runtimes.prompts._detect_compose_pending_unsent(content)[source]
Detect unsent text sitting in the Claude Code compose buffer.
The classifier in
agent_meta._classify_pane_statematches❯[ \t\xa0]+\S(non-whitespace after the prompt marker on the same line), meaning the user has typed something but not yet pressed Enter. We mirror that pattern here so the prompts system can submit it via a plain Enter keystroke.The gap MUST include U+00A0 NO-BREAK SPACE: Claude Code’s Ink TUI renders the prompt as
❯\xa0[Pasted text …](an NBSP, not an ASCII space). The earlier❯[ \t]+pattern silently missed it, so a multi-line startup_prompt paste was never detected as pending and the boot-drain /_verify_submittedresend never fired — the agent sat idle with its instructions pasted-but-unsent (proj-scitex-dev 2026-06-23).Excluded: lines that are just the decorative separator below an empty prompt — those contain only whitespace after
❯.- Return type:
- scitex_agent_container.runtimes.prompts._detect_resume_session(content)[source]
Long-session resume picker shown by
claude --continue/--resume.When the session being resumed is large, Claude Code interposes a three-way picker before the REPL opens:
This session is <N>h <M>m old and <K>k tokens. Resuming the full session will consume a substantial portion ... ❯ 1. Resume from summary (recommended) 2. Resume full session as-is 3. Don't ask me again
The sac TUI boot uses
--continueprecisely to resume the FULL prior context, so we always pick option 2 (“Resume full session as-is”). Left unhandled this modal BLOCKS boot: the startup_prompt paste lands inside the picker and the boot-drain hangs (lead-retirement, 2026-06-25). Must out-rankcompose-pending-unsent— the modal’s❯ 1.line also matches that detector — hence priority 1.- Return type:
- scitex_agent_container.runtimes.prompts._detect_done(content)[source]
Check if claude is at the main input prompt (all TUI prompts done).
The status bar shows “bypass permissions” when ready.
- Return type:
- scitex_agent_container.runtimes.prompts.register_prompt(handler)[source]
Add a custom prompt handler to the registry.
- Return type:
- scitex_agent_container.runtimes.prompts._LIVE_WINDOW_LINES = 15
How many non-empty rows of a captured pane count as the LIVE region for modal detection. See
_recent_tail()for the false-positive this guards against; sized with headroom over the tallest verified real modal capture (_LIVE_BYPASS_PANEintest_prompts.py, 9 non-empty rows).
- scitex_agent_container.runtimes.prompts._recent_tail(content, lines=15)[source]
Return the last
linesnon-empty rows ofcontent.Scopes modal detection to the LIVE region of a captured pane instead of the entire accumulated snapshot
tmux capture-pane -p(no-S) returns the whole VISIBLE viewport, not just the freshest output. Once Claude Code’s Ink TUI has moved a first-run modal’s rendered text further up the screen — dismissed, but not yet scrolled past the viewport — an unscoped substring match can still fire on it long after the modal is gone, sending its registered digit+``Enter`` into what is now the plain, empty compose box: cardsac-tui-stray-1-submitted-on-boot— a spurious"1"gets typed and submitted as a user message during boot (observed with thethinking-effort/file-trust-radio/theme-selectionhandlers, none of which scoped their match to the live screen)._LIVE_WINDOW_LINEScomfortably covers the tallest verified real modal capture with headroom, while still excluding an EARLIER, already-dismissed modal’s text once enough new output has rendered below it. Mirrors_detect_press_enter_continue()’s existing last-N-lines window (same fix shape, wider here because these handlers need a whole title+options+footer block, not one banner line) and_tui_compose._compose_pending_live()’s bottom-anchored scoping for the analogous Enter-drop bug (sac-tui-enter-drop-on-boot).- Return type:
- scitex_agent_container.runtimes.prompts.detect_and_respond(content, accepted, send_keys_fn)[source]
Check content against all handlers, respond to the first match.
- scitex_agent_container.runtimes.prompts.detect(content)[source]
Return the NAME of the first matching prompt handler (by priority), or None when no known modal is on screen. Detect-only — sends nothing.
Pairs with
respond_modal()so a drain loop can RESPOND, settle, then re-detect to VERIFY the modal actually cleared — instead of firing keystrokes once and assuming success. Claude’s Ink TUI drops keys sent mid-render, so a fire-and-forget send silently leaves the modal up; the detect/respond/verify cycle is the no-silent-fallback fix.Matches against
_recent_tail()(the LIVE region), not the rawcontent— see its docstring for the stray-boot-submit false positive this prevents.
- scitex_agent_container.runtimes.prompts.respond_modal(name, send_keys_fn)[source]
Send the registered keystrokes for the handler named
name.Returns True iff a handler with that name exists (its keys were sent), False otherwise. The caller MUST verify the modal cleared (re-capture +
detect()) and resend on the render race — a single send is not guaranteed to land.- Return type: