Problem
Channel default skills can run in registration_agent_key mode, and the registration Agent Key is already used for Ornn/private skill access, connected-service discovery, and downstream proxy tools. However, a workflow llm_call fails when executed from a no-login channel turn:
llm_request_failed: NyxID authentication required for provider 'nyxid'. Please sign in.
Observed failure point:
src/Aevatar.AI.LLMProviders.NyxId/NyxIdLLMProvider.cs
ResolveAccessTokenWithSource() only accepts:
CallerContext.Credentials.NyxIdBearer
LlmControl.NyxIdAccessToken
- host accessor token
It does not accept a registration Agent Key credential, so channel no-init workflows containing llm_call require user login even when the registration already has a ready Agent Key.
Expected behavior
For channel turns configured with:
"credential_source_mode": "registration_agent_key"
workflow llm_call should be able to call the NyxID LLM route using the registration Agent Key, subject to NyxID Agent Key scopes and allowed service grants.
The Agent Key should not be treated as a user login bearer identity. It should remain a registration-owned credential, with audit context preserving:
- registration id
- scope id
- channel platform
- conversation id
- sender id
- Agent Key id/prefix, not raw secret
Relevant existing behavior
The repository already has a similar pattern for managed Codex:
src/Aevatar.AI.Infrastructure.ChronoSandbox/NyxIdCodeExecutionPort.cs
That code explicitly keeps Agent Keys as bearer caller credentials when calling NyxID:
// Agent Keys must remain bearer caller credentials here. NyxID authenticates
// them and forwards the same Authorization value to Chrono on admitted routes.
This pattern should be reused or abstracted instead of inventing a second credential transport.
NyxID route
NyxIdLLMProvider uses OpenAI-compatible routing through NyxID:
Gateway:
Or configured/proxy route:
/api/v1/proxy/s/{serviceSlug}
/api/v1/proxy/{serviceId}
The active registration Agent Key can already include chrono-llm-public in allowed_services, so the intended practical route can be:
/api/v1/proxy/s/chrono-llm-public
or gateway if NyxID confirms gateway Agent Key admission.
Proposed implementation
- Extend the LLM credential model
Add a typed credential source for NyxID Agent Key, rather than stuffing Agent Key into NyxIdAccessToken.
Example shape:
CallerContext.Credentials.NyxIdAgentKey
CallerContext.Credentials.NyxIdCredentialKind = AgentKey
Preserve existing bearer behavior.
- Resolve registration Agent Key before workflow
llm_call
When channel runtime starts a workflow from a registration with credential_source_mode=registration_agent_key, resolve the stored ChannelNyxIdAgentKey secret reference.
Attach it to the workflow run caller context or LLM caller context as typed Agent Key material.
Do not expose the raw key in logs, metadata bags, read models, or tool receipts.
- Update
NyxIdLLMProvider
Replace ResolveAccessTokenWithSource() with a typed credential resolver.
Support:
- user bearer
- LLM control bearer
- registration Agent Key
- host accessor token
For Agent Key, send it to NyxID using the same bearer credential transport used by managed Codex:
Authorization: Bearer <agent-key>
Log only credential kind/source and length/prefix-safe metadata.
- Reuse or extract common NyxID credential transport
Current Codex path has Agent Key-specific proxy handling. Extract common logic if possible, for example:
- normalize NyxID credential
- classify credential kind
- build Authorization header
- preserve exact UserService selector
Avoid duplicating "Agent Key as bearer caller credential" logic in both Codex and LLM provider.
- Route selection
Prefer configured LLM proxy route when available, especially chrono-llm-public, because it can be allowlisted on Agent Keys like other services.
Keep gateway support if NyxID confirms /api/v1/llm/gateway/v1 admits Agent Keys.
If gateway rejects Agent Key, return a clear diagnostic:
nyxid_llm_agent_key_not_admitted
rather than falling back to "Please sign in."
Acceptance criteria
- A Telegram/default skill workflow with
registration_agent_key can execute an llm_call without sender /init.
- The same workflow still works for sender-bound/user bearer mode.
- If the Agent Key lacks LLM service permission, failure is explicit and classified as authorization, not login required.
- Existing Codex Agent Key behavior remains unchanged.
- Logs show credential source as
registration-agent-key but never log the raw key.
- Tests cover:
NyxIdLLMProvider resolves bearer credentials as before.
NyxIdLLMProvider resolves Agent Key credentials and sends them as bearer caller credentials.
- channel workflow
llm_call receives registration credential context.
- missing LLM grant produces a clear authorization failure.
- no credential still produces the existing sign-in-required failure.
Notes
Do not model this as sender login. The subject is the registration/bot authority, not the Telegram sender. Sender identity should remain trigger/audit context only.
Problem
Channel default skills can run in
registration_agent_keymode, and the registration Agent Key is already used for Ornn/private skill access, connected-service discovery, and downstream proxy tools. However, a workflowllm_callfails when executed from a no-login channel turn:Observed failure point:
src/Aevatar.AI.LLMProviders.NyxId/NyxIdLLMProvider.csResolveAccessTokenWithSource()only accepts:CallerContext.Credentials.NyxIdBearerLlmControl.NyxIdAccessTokenIt does not accept a registration Agent Key credential, so channel no-init workflows containing
llm_callrequire user login even when the registration already has a ready Agent Key.Expected behavior
For channel turns configured with:
workflow
llm_callshould be able to call the NyxID LLM route using the registration Agent Key, subject to NyxID Agent Key scopes and allowed service grants.The Agent Key should not be treated as a user login bearer identity. It should remain a registration-owned credential, with audit context preserving:
Relevant existing behavior
The repository already has a similar pattern for managed Codex:
src/Aevatar.AI.Infrastructure.ChronoSandbox/NyxIdCodeExecutionPort.csThat code explicitly keeps Agent Keys as bearer caller credentials when calling NyxID:
This pattern should be reused or abstracted instead of inventing a second credential transport.
NyxID route
NyxIdLLMProvideruses OpenAI-compatible routing through NyxID:Gateway:
Or configured/proxy route:
The active registration Agent Key can already include
chrono-llm-publicinallowed_services, so the intended practical route can be:or gateway if NyxID confirms gateway Agent Key admission.
Proposed implementation
Add a typed credential source for NyxID Agent Key, rather than stuffing Agent Key into
NyxIdAccessToken.Example shape:
Preserve existing bearer behavior.
llm_callWhen channel runtime starts a workflow from a registration with
credential_source_mode=registration_agent_key, resolve the storedChannelNyxIdAgentKeysecret reference.Attach it to the workflow run caller context or LLM caller context as typed Agent Key material.
Do not expose the raw key in logs, metadata bags, read models, or tool receipts.
NyxIdLLMProviderReplace
ResolveAccessTokenWithSource()with a typed credential resolver.Support:
For Agent Key, send it to NyxID using the same bearer credential transport used by managed Codex:
Log only credential kind/source and length/prefix-safe metadata.
Current Codex path has Agent Key-specific proxy handling. Extract common logic if possible, for example:
Avoid duplicating "Agent Key as bearer caller credential" logic in both Codex and LLM provider.
Prefer configured LLM proxy route when available, especially
chrono-llm-public, because it can be allowlisted on Agent Keys like other services.Keep gateway support if NyxID confirms
/api/v1/llm/gateway/v1admits Agent Keys.If gateway rejects Agent Key, return a clear diagnostic:
rather than falling back to "Please sign in."
Acceptance criteria
registration_agent_keycan execute anllm_callwithout sender/init.registration-agent-keybut never log the raw key.NyxIdLLMProviderresolves bearer credentials as before.NyxIdLLMProviderresolves Agent Key credentials and sends them as bearer caller credentials.llm_callreceives registration credential context.Notes
Do not model this as sender login. The subject is the registration/bot authority, not the Telegram sender. Sender identity should remain trigger/audit context only.