Skip to content

feat(conversation): add aioncore conversation create for agent-driven conversation creation - #977

Merged
piorpua merged 6 commits into
mainfrom
feat/conversation-create
Sep 9, 2026
Merged

feat(conversation): add aioncore conversation create for agent-driven conversation creation#977
piorpua merged 6 commits into
mainfrom
feat/conversation-create

Conversation

@piorpua

@piorpua piorpua commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Lets an agent create a new conversation for the same user from inside its own conversation, so "open a fresh conversation for this task and hand it over" no longer needs the user to create and name an empty conversation by hand first.

  • Agent-facing CLIaioncore conversation capabilities | create, payload over stdin ({ name, workspace?, assistant_id? }), wired to one runtime route that authenticates with the existing per-conversation helper token (no new scope, no change to token issuance).
  • Inherit by default — the new conversation reuses the caller's workspace and assistant (aionrs conversations also inherit the stored model). Both can be overridden: an explicit workspace must be an absolute path to an existing directory; an explicit assistant_id must name an existing, enabled assistant.
  • aionrs assistant override resolves a provider server-side — the assistant's default model id is matched against the first enabled provider that lists it (same rule as team provisioning). No match is a hard assistant_model_unresolved; the caller's model is never used as a silent fallback.
  • Synchronous — a success: true envelope means the row, its assistant snapshot, and the ACP session row are persisted and conversation.listChanged(created) has been broadcast, so the id can be used immediately (for example as a session send-message target). The runtime starts lazily on the first message, exactly like a manually created empty conversation.
  • Independent of cross-session messaging — separate command family, descriptor registry, error-code family, envelope type, and auto-inject skill (conversation-create). Neither skill references the other; combining "create, then deliver" is left to the agent.
  • No frontend change — the sidebar already refreshes on conversation.listChanged.

Design decisions worth a reviewer's attention

  • A dedicated runtime route instead of the ordinary POST /api/conversations. Inheriting from the caller requires knowing who the caller is; the route takes it from the token-bound x-aionui-conversation-id header, so the body cannot name a different caller and the agent never has to assemble a full CreateConversationRequest (the aionrs model shape in particular).
  • All validation runs before ConversationService::create is called, in a fixed order (caller row → team → name → workspace → assistant), so a rejection never leaves a half-built conversation behind and a request matching several rejections always returns the same code.
  • Delegates to the existing create for everything after resolution — assistant snapshot, skills, MCP, rules, workspace existence check — rather than re-implementing any of it. custom_workspace: true is passed only because create strips it; the non-empty extra.workspace is what suppresses temp-dir provisioning.
  • Snapshot inheritance falls back to the legacy type + extra.{backend, agent_id, agent_source} triple when the caller has no snapshot or its assistant definition has since been deleted, so the new conversation still mirrors the caller instead of failing with an error the agent cannot act on.
  • The CLI deliberately duplicates cmd_session.rs's envelope/stdin plumbing instead of abstracting it: the two families have different envelope and error-code types, and a generic layer is not worth it until a third family appears.
  • Logging (info level): one conversation created by agent line per success with from_conversation_id / conversation_id / inheritance / workspace_inherited / model_resolution / backend, and one agent conversation create refused warn per rejection with from_conversation_id / error_code / outcome. Conversation names, workspace paths, model ids, and header values are never logged.
  • Skill checkbox semantics are unchanged from aionui-config / cron: unticking conversation-create removes the agent's guidance, not the server's willingness to answer a valid runtime token. The command family also appears in the top-level aioncore capabilities index, so an agent can still discover it there.

Migration

None.

Verified in a development environment

Beyond unit, integration, and binary end-to-end tests, the feature was exercised in a dev build against real agent CLIs (direct-CLI and ACP backends), with each observation cross-checked against backend logs and the persisted rows:

  • Create with only name from a claude-backed conversation: workspace and assistant snapshot inherited, source=aionui, name_source unset, one listChanged(created) broadcast; the agent then delivered a task to the new conversation via session send-message and received the reply, with the new conversation's runtime starting lazily on that first message.
  • Create with only name from an aionrs conversation: the stored model (provider + model) inherited verbatim.
  • Explicit absolute workspace persisted as given, with no temp workspace provisioned.
  • Explicit assistant_id for an ACP assistant: backend and agent taken from that assistant, no top-level model.
  • Explicit assistant_id for an aionrs assistant: model.provider_id resolved to the single enabled provider offering its default model.
  • Rejections, each with the documented code, HTTP status, CLI exit code 3, and no new row: team caller (caller_is_team), unknown assistant (assistant_not_found), non-existent absolute workspace (workspace_unavailable, directory not created), relative workspace (workspace_not_absolute). The last two required invoking the CLI directly, because the skill rules made the agent pre-check them itself.
  • Skill unticked: the agent no longer received the skill but still found the command through aioncore capabilities, consistent with the other built-in CLI families.
  • Log hygiene: no conversation name, workspace path, model id, or header value at info level or above.

Not exercised in the dev build (covered by service-level integration tests): assistant_disabled, assistant_model_unresolved, and the legacy-triple fallback, each of which needs a specially constructed configuration.

Screenshots are omitted: the observable behavior is a new sidebar entry plus log and database state, all of which is captured by the automated tests and the log/DB checks above.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --all-targets -- -D warnings on aionui-api-types, aionui-conversation, aionui-app
  • cargo test on the three affected crates (new: registry/error-code unit tests; service-level inherit/override/bad-path tests; route-level auth/status/code tests; real-router e2e; real-binary CLI e2e; skill content guards)
  • Full workspace pre-push gate via just push
  • Manual end-to-end verification in a dev build (above)

@piorpua
piorpua merged commit 23daff5 into main Sep 9, 2026
6 checks passed
@piorpua
piorpua deleted the feat/conversation-create branch September 9, 2026 06:03
piorpua pushed a commit that referenced this pull request Sep 9, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.2.2](v0.2.1...v0.2.2)
(2026-09-09)


### Features

* **conversation:** add aioncore conversation create for agent-driven
conversation creation
([#977](#977))
([23daff5](23daff5))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant