Aevatar automation
Aevatar scheduling & workflow automation: scheduled_agent_creator (cron/one-shot) for independent scheduled Ornn skill agents and reminders — NOT an alias for Studio Team member automation — the long-running task automation playbook, workflow creation semantics (Scope Workflow vs Ornn publish), agent_builder lifecycle, the typed required_nyx_services contract (exact user_service_id is the authorization; a slug snapshot never substitutes), dedicated Agent Key semantics (vault-held raw material, borrowed durable reference late-resolved per use, exact non-wildcard grants, ~90-day projected expiry, reauthorize-then-revoke, independent NyxID and Vault revocation tracks), accepted-is-admission-only receipts, and a token_expired triage protocol that reads credentialSourceKind before assuming any TTL.From its SKILL.md
npx -y skills add ChronoAIProject/NyxID --skill aevatar-automationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
17.2 KB, as published. Nobody here has run it
scheduled_agent_creator (scheduled Ornn skill agents)
Use scheduled_agent_creator to create a new caller-owned scheduled automation agent from an Ornn skill reference, or to create a single delayed reminder.
This tool is for an independent scheduled Ornn skill agent or a one-shot reminder. It is NOT an alias for Studio Team member automation. If the thing being scheduled is an already-bound Team member workflow, the canonical path is aevatar_schedule_member_workflow or /api/scopes/{scopeId}/teams/{teamId}/members/{memberId}/automations — see aevatar-scheduler. Do not route a Team member here, and do not route an independent skill agent to generic /api/schedules.
For recurring automation, set schedule_mode="cron" and provide skill_ref, schedule_cron, and schedule_timezone; optional LLM tuning fields are allowed. If the loaded skill body will call connected NyxID services through nyxid_proxy beyond Ornn and the Lark outbound channel, declare them with the typed requirement contract:
required_nyx_services[] = { user_service_id, service_slug_snapshot }
The exact user_service_id is the authorization; the slug snapshot is an integrity/display value and can never substitute for it. nyx_user_service_id identifies the exact outbound provider where applicable. The older required_service_slugs contract has been removed — do not recommend it, and never infer a UserService ID from a slug, a vendor name, or a catalog service ID (a connected UserService ID and a catalog service ID are different identifiers and are not interchangeable).
If the scheduled agent needs deterministic fire-date fields, its execution prompt may use the same
fire-time Chat template as other schedules: {{@schedule.run_date}}, run_year, run_month,
days_until_month_end, fire_at_utc, and timezone. The prompt must then be valid JSON and each
placeholder must be inside a JSON string value. The values come from the authoritative logical fire
and configured IANA timezone; catch-up keeps the original occurrence and run-now uses the manual
instant. Unknown placeholders or invalid JSON fail closed. A manual run is not cron-rearm proof.
For one-shot delayed reminders such as "remind me in 10 minutes" or "later today tell me ...", set schedule_mode="one_shot" and provide exactly one of delay_seconds or run_at_utc, plus one_shot_message. Prefer delay_seconds when the user gave a relative delay. Do not use code_execute with sleep, timers, polling loops, or long-running scripts for delayed one-shot requests; durable delivery must go through scheduled_agent_creator. Do not publish an Ornn skill just to send a one-shot natural-language reminder unless the user explicitly asks for reusable automation or the reminder requires a real skill workflow.
Do not provide owner, scope, Lark target, Nyx provider slug, API key, service IDs, inline skill content, or outbound credential fields. This write command does not request remote approval; the tool derives context from the current authenticated/channel turn, mints a scoped NyxID key, and returns only an accepted receipt or a typed tool error.
skill_ref must be unversioned for now. A name@version reference returns versioned_skill_ref_not_supported_yet.
The scheduled agent's credential is a dedicated Agent Key
A scheduled_agent_creator agent does not run on a replayed user bearer, and it does not
run on a fire-time broker token. The tool derives caller and ownership from trusted context, plans
and revalidates typed authorization, provisions a constrained dedicated Agent Key, stores the
raw material in the Vault, persists a typed reference, returns an accepted receipt, and delegates
later management to agent_builder.
Secret custody. Raw key material is written only to ISecretVault. Durable state, committed
events, read models, logs, tools, and public APIs expose only stable non-secret facts: a typed
credential reference, Agent Key ID, expiry, authorization fact, and credential generation. The
workflow borrows a durable credential reference; every operation that needs the credential
late-resolves it through the Vault at the moment of use, fail-closed for an absent, expired,
revoked, mismatched, or malformed reference. Never ask the user to paste the key, accept it in JSON,
print it, store it in an Ornn package, or reconstruct it from an ID.
Grants are exact and closed: allow_all_services = false, allow_all_nodes = false. Only the
targeted plan supplies allowed service IDs and node IDs.
Expiry. The scheduled agent's default Agent Key lifetime is 90 days, subject to its typed authorization policy and deployment configuration. Describe the projected expiry — never promise an eternal key, and never quote a minutes-scale TTL for this credential class.
Lifecycle. Adding a new external service dependency is a reauthorization, not an edit: new preflight → new confirmed plan → provision a replacement generation → commit it → only then revoke the old generation. Never mutate an existing key in place to widen its authority. Pause and resume preserve the active credential and are neither a revoke nor a reauthorization. Delete tracks NyxID key revocation and Vault secret revocation as independent durable tracks, and a retry reuses the original delete operation identity rather than synthesizing a new one.
202 / accepted is admission only
An accepted receipt or pending status proves command/effect admission — nothing more. It does not
prove credential issuance completed, the Vault write completed, activation committed, the read model
observed the new state, cron fired, or the workflow succeeded. Reread the canonical owner-scoped
record and require a newer authoritative stateVersion. Note that credential health
(authorizationStatus == active) and firing state (enabled == true) are independent
dimensions.
If authorization planning returns authorization_catalog_route_unresolved or
api_key_scope_plan_route_unresolved, treat it as non-retryable. Preserve the exact required
UserService IDs, stop refresh/retry loops, and repair or deactivate the unresolved route before
trying again. A required ID may merely share a catalog family with the bad active peer; do not
delete or reconnect unrelated services.
Token classes: verified lifetimes, not folklore
Never quote one class's lifetime for another. The 300-second broker TTL below belongs to the NyxID binding-exchange source used by generic schedules — it does not describe a dedicated Agent Key.
| Credential | Lifetime | Authority to cite |
|---|---|---|
Dedicated scheduled-invocation Agent Key (credentialSourceKind = scheduled_invocation_agent_key) | Policy-governed; scheduled agent default ~90 days | the record's credentialExpiresAtUtc + credentialGeneration |
| Generic-schedule caller credential via NyxID binding exchange at fire | 300 s, fixed | NyxID oauth_broker_service.rs BROKER_ACCESS_TTL_SECS |
| Interactive user login access token | deployment config JWT_ACCESS_TTL_SECS (code default 900 s; production instances often set hours) | decode the actual JWT: exp − iat |
| NyxID delegated / MCP delegation token | 300 s | NyxID crypto/jwt.rs |
| NyxID service-account token | SA_TOKEN_TTL_SECS (code default 3600 s) | NyxID config.rs |
Lark tenant_access_token | ≈2 h, cached and refreshed transparently by NyxID | NyxID Lark token-exchange service description |
| aevatar scope service token | TokenLifetimeMinutes (default 60 min) | aevatar Aevatar.Authentication.ScopeServiceTokens/ScopeServiceTokenOptions.cs |
Anti-hallucination protocol for token_expired / 401 in a scheduled run — complete these steps
before naming any cause:
- Never assert a token lifetime from memory. Read it: decode the JWT actually in hand
(
exp − iat; report only the numbers, never print the token), or cite the owning repo's constant from the table above. - Read
credentialSourceKindbefore anything else. This single field decides which diagnosis applies, and getting it wrong is the most common failure in this area:scheduled_invocation_agent_key⇒ a dedicated Agent Key. Do not diagnose a fixed five-minute broker expiry merely because the call was scheduled. Atoken_expiredsix minutes after fire on this source is not explained by the 300 s constant, and the ~5-6 minute coincidence is not evidence. Inspect instead: keycredentialExpiresAtUtc; Vault resolution and reference integrity; committed caller authority; the authorization fact; the exact service/node grants versus what the failing step actually called;credentialGeneration(did a reauthorization replace it mid-flight?);revocationPending/nyxIdRevocationStatus/vaultRevocationStatus; then the downstream service's own token.nyxid_binding_exchange(generic schedules) ⇒ the fire-time broker token is the right hypothesis; identify the actual exchanged token and use its verifiediat/exp.
- Identify the token class first. "The access token lives 8 hours" (login class) and "the run credential lives 5 minutes" (binding-exchange class) can both be true — about different tokens. A claim that conflates them is wrong even if each number is right.
- Correlate failure onset with FIRE TIME, not wall clock — but only once the source is known. For a binding-exchange source, failures beginning ~5 minutes after fire in every run ⇒ the fire-time-minted broker credential aged out. Failures at one fixed wall-clock moment across runs ⇒ something else (provider-side expiry or rotation).
- Separate blast radius. If ALL NyxID-authenticated steps fail after some instant, the run caller credential died. If only one step type fails while sibling proxied steps still succeed, suspect that step's own credential path instead.
- Report observed timestamps/status as fact and everything else as labeled inference. Any number you did not verify in this session is an inference.
- Never emit secret material in any diagnosis, log excerpt, or artifact: no raw Agent Key, bearer/access/refresh/delegation/service-account token, Vault reference or ciphertext, permission digest, unfiltered API-key inventory, or authorization header. Stable resource IDs may be reported for management or cleanup, never as secret material.
Long-running task automation playbook
Use this playbook when the user asks for a recurring, scheduled, monitored, or otherwise long-running task instead of a one-off answer. Typical triggers include: "每天...", "每周...", "each week...", "monitor X and tell me...", "定时...", "recurring", "keep watching", and "长期跟踪".
Workflow creation semantics
When a Lark user asks to create a workflow that should be runnable, page-visible, or invokable later by workflow id, create or update a Scope Workflow through the available Scope Workflow command tool path. Ornn publishing is for reusable templates/packages/exports; it does not make a workflow page-visible or runnable in Aevatar until the template is mounted/imported into Scope Workflow and the accepted/readmodel propagation contract says it is visible.
-
Recognize the request as automation.
- Do not answer with a one-shot summary if the user wants repeat runs.
- Do not ask the user to hand-write the skill package.
- Treat the future runner as a runnable Ornn skill, not a chat-only script.
-
Reuse before you author — search Ornn first.
- Before authoring anything, call
ornn_search_skillswith the task's distinctive capability keyword. Prefer a single strong keyword (deadline,attendance,reimbursement,digest,candidate); multi-word phrase queries match poorly, so if a phrase returns nothing, retry with one keyword ormode=semanticbefore concluding nothing exists. - A skill named like
<capability>-…-payload-builderis a reusable match even if its name is longer than what the user said; do not require an exact name. - If a returned skill already covers the request, load it with
use_skill, then go straight to negotiation and schedule it withscheduled_agent_creatorusing that existingskill_ref— no authoring or publishing needed. Do NOT author a duplicate of a skill that already exists. - Only author a new skill when the search returns no suitable match.
- Before authoring anything, call
-
Author a runnable skill package yourself.
- Build the package as an active playbook: the skill must collect data with its own tools, analyze the current facts, then deliver the result to Lark.
- For monitoring or digest jobs, use the loaded skill metadata and instructions to choose the monitoring or digest flow: fetch live data through
nyxid_proxyfor explicit connected services such asapi-github, derive the digest from current facts, then post the digest to the negotiated chat target. - Write
instructions_markdownas executable guidance, not passive description. Useworkflow_yamlsandscriptswhenever they make the flow deterministic or easier to reuse. - Keep the package typed:
name,description,version,category,instructions_markdown, plus anyworkflow_yamlsandscriptsthe run needs.
-
Negotiate schedule and output with an interactive Lark card.
- Use
reply_with_interactionto ask for the minimum missing details. - Ask for the execution cadence as a concrete schedule (
cronplus timezone), not vague wording. - Ask where the result should go: direct message or group chat.
- Ask for the output format: plain text or Feishu cloud doc.
- Prefill anything you can infer from the current conversation, and only ask for what is missing.
- If the user changes frequency, time, delivery target, or output format, reopen the same negotiation instead of scheduling against stale values.
- Use
-
Publish the skill, then schedule it.
- Call
ornn_publish_skillwith the assembled typed package. - If publish fails, inspect the diagnostics, fix the package, and retry.
- Ornn private skill publishing executes directly. Do not say it is waiting for remote approval unless a typed remote approval result explicitly says so.
- Do not tell the user a skill was submitted, uploaded, or published unless the
ornn_publish_skillcall actually returned a success receipt for that skill. - Once the skill is published successfully, call
scheduled_agent_creatorwith the publishedskill_ref, the agreedschedule_cron, the agreedschedule_timezone, andrequired_nyx_servicesentries carrying the exactuser_service_id(plus itsservice_slug_snapshot) for every connected service the skill body will call throughnyxid_proxy. - Carry the negotiated delivery/output choice into the runner's
execution_promptand outbound delivery setup; if the chosen delivery target differs from the current conversation, rebind it withagent_delivery_targetsusing the returnedagent_id. - For plain text output, the skill should send a concise digest back to Lark. For Feishu cloud doc output, the skill should create or update a document and return the link.
- Call
-
Recover cleanly.
- Publish failure means the package is wrong; refine and republish.
- User rejection or edits mean the negotiation is not stable yet; update the card and retry.
- If the user later wants a different cadence, treat it as a new negotiation for a new schedule rather than pretending the existing schedule changed automatically.
agent_builder (Day One persistent automation lifecycle)
agent_builder manages the lifecycle of agents the user has already created. It can list, inspect, run, pause, resume, and delete; it does not create agents.
| Intent | Slash command |
|---|---|
| List agents | /agents |
| Inspect one agent | /agent-status <agent_id> |
| Manual run | /run-agent <agent_id> |
| Pause schedule | /disable-agent <agent_id> |
| Resume schedule | /enable-agent <agent_id> |
| Delete (two-step) | /delete-agent <agent_id> confirm |
Tool semantics: disable_agent pauses scheduled execution without deleting; enable_agent resumes; delete_agent disables, revokes the NyxID API key, and tombstones the registry entry. The Nyx relay path handles these slash commands directly without an LLM round-trip — you typically only see these flows when the user asks for them in natural language.