Skip to content

Chat & Setup (Zero-Touch ChatGPT Integration)

assync setup and assync chat are the zero-touch path to asking ChatGPT a question about a registered Assync project: no second terminal, no scripts/run-mcp-server.sh run by hand, no export OPENAI_API_KEY. Both are built directly on top of the same local Agents SDK client documented in Local Assync Agent (OpenAI Agents SDK) -- same config resolution, same MCP startup, same read-only tool filter, same agents Runner. assync-agent itself is unchanged; assync chat reuses its underlying client code rather than duplicating it.

assync setup                 assync chat "..."
 ↓                             ↓
Store API key (Keychain)     Assync starts the MCP server itself
Pick a default project       Assync starts the OpenAI Agent
                              Tool filter restricts it to 7 read-only tools
                              Request runs, answer prints
                              Assync cleans up -- no leftover process

assync setup

Run once per machine:

.venv/bin/assync setup

This is an interactive wizard that:

  1. Checks whether an OpenAI API key is already available (environment variable or Keychain). If not, prompts for one (hidden input) and, only after your explicit confirmation, stores it in the macOS Keychain (service assync.openai) -- never in a file inside this repository, never in shell history, never in an Assync record.
  2. Lets you pick a default project id from your registered projects (assync project list) and saves it to your existing user config file (~/.config/assync/config.json, the same file assync config already manages) under the agent.default_project_id key.
  3. Runs the same environment diagnostics as assync setup doctor (below) and prints the result.
  4. Optionally -- only if you confirm -- sends one real, billed OpenAI API request ("Reply with the single word OK.") to prove the key actually works end-to-end.

--smoke-test makes step 4 available; without it, setup never makes a network call.

Credential storage

The API key is stored via /usr/bin/security add-generic-password, using its documented non-argv-exposing form (-w as the final argument, value supplied over stdin, never as a command-line argument) -- so it never appears in ps, shell history, or process listings. Retrieval at request time uses security find-generic-password -w, again never exposing the value as an argument.

Resolution order, every time a key is needed (assync chat, assync setup doctor, assync-agent):

  1. OPENAI_API_KEY environment variable, if set -- wins unconditionally, fully backward compatible with existing assync-agent usage.
  2. macOS Keychain (assync.openai / OPENAI_API_KEY), if the environment variable is absent.
  3. Otherwise: OPENAI_API_KEY_REQUIRED, and no request is made.

assync setup doctor

Read-only, zero-mutation environment diagnostics -- safe to run any time, never starts a real conversation:

.venv/bin/assync setup doctor
.venv/bin/assync setup doctor --json

Every check measures the real running environment; none of them infer or guess:

Check What it measures
executable_shadowing Whether assync on PATH resolves (by real path, not by name) to this repository's own .venv/bin/assync, or to a different, shadowing install elsewhere on PATH.
architecture Three independent, real measurements -- the interpreter's own architecture, a real compiled native dependency's architecture (file(1) on an actual .so in this venv), and whether the process is running under Rosetta 2 translation (sysctl -n sysctl.proc_translated, the documented way to detect this on Apple Silicon). Classified as NATIVE_ARCHITECTURE, ROSETTA_ACTIVE (a working setup, reported as a warning, never as broken), or ARCHITECTURE_MISMATCH (a genuinely broken, mixed-architecture venv).
credential Presence and source only (SET / UNSET / INVALID, environment_variable / keychain / none) -- the key value itself never appears in the report, and doctor never makes a live API call to confirm the key works (verified_against_api is always NOT_CHECKED; that's what --smoke-test in assync setup is for).
default_project Whether the configured default project id is actually registered (assync project list).
agents_sdk_available Whether the openai-agents package is installed.
runtime_validation_availability Whether assync.runtime.service.validate_runtime is importable -- a static availability probe only; it is never invoked, since an ad hoc chat question commonly has no ACTIVE Mission (see Runtime binding below).
mcp_handshake A real stdio connection to the local Assync MCP server and a real tools/list call.
required_tools_present Whether all 7 required read-only tools are present in that real tools/list result.
process_cleanup Whether the MCP server subprocess used for the handshake check above is actually gone afterward (a real process-list check, not an assumption).

overall_status is READY, READY_WITH_WARNINGS, or REPAIR_REQUIRED (exit code 8 in the last case) -- the same three-way result assync chat --doctor uses internally, described next.

assync chat

.venv/bin/assync chat "Where does project_assync currently stand?"
.venv/bin/assync chat --project-id project_other "Show completed missions"
.venv/bin/assync chat            # prompts for one question interactively
.venv/bin/assync chat --doctor "..."   # runs the doctor checks first

One-shot only -- there is no REPL, no /exit, no multi-turn conversation. Each invocation is a single independent question and a single independent Agent run, exactly like assync-agent. Assync itself:

  1. Resolves the API key (environment, then Keychain).
  2. Starts the local Assync MCP server as a subprocess.
  3. Builds the Agent, restricted to the same 7 read-only tools as assync-agent (enforced by the Agents SDK's own tool filter, not just prompt text -- see Read-only guarantee).
  4. Runs the question.
  5. Cleans up the MCP subprocess -- proven with a real subprocess.Popen
  6. SIGINT test and real init-failure/early-exit/timeout tests in tests/test_agent_client_mcp.py, not merely asserting a context manager's __aexit__ was called.
  7. Prints only the final answer to stdout; a Ctrl+C during a run exits cleanly with code 130, no raw traceback.

--doctor runs exactly the checks described above before the request -- so if assync chat isn't working, you don't have to separately run assync setup doctor to find out why. If the result is REPAIR_REQUIRED, the request is never attempted.

Runtime binding

assync chat deliberately does not create or reference a RuntimeAssignment or RuntimeSession (see assync runtime --help for that separate, Mission-bound subsystem). An ad hoc chat question commonly has no ACTIVE Mission, and recording a partially-governed or misleading runtime entry for it would be worse than recording nothing. This is CHAT_RUNTIME_BINDING_DEFERRED_TO_P7B -- a deliberate, explicit scope boundary, not an oversight. Security for the read-only guarantee rests entirely on the already-enforced MCP tool allowlist, identically to assync-agent.

assync-agent is unchanged

assync-agent (the original local Agents SDK client) is not deprecated, removed, or redirected by any of this -- it keeps working exactly as before, including for anyone who prefers exporting OPENAI_API_KEY manually and running the MCP server themselves. assync chat is an additional, zero-touch entry point built on the same underlying client code, not a replacement.

Known limitations

  • No conversation memory across invocations -- same as assync-agent.
  • assync setup's interactive wizard requires a real terminal (hidden input for the API key, confirmation prompts); it is not scriptable. Everything it does can also be done by hand: export OPENAI_API_KEY (or store it in the Keychain yourself) and pick a project id with assync config set agent.default_project_id <id>.