Skip to content

Add Tilde-backed Routines for recurring agent work #30

Description

@danielblignaut

Outcome

Add repository-authored recurring agent work based on the public Eve schedules model, but call the OpenBot product concept a Routine.

Tilde already supports cron-triggered agent invocations, so Tilde—not Vercel, Electron, control-service, or a process-local timer—must own production scheduling and signed delivery. OpenBot discovers and validates routine source, reconciles it through the public Tilde API, and presents execution status without duplicating the scheduler.

Terminology and supported subset

  • Routine is the OpenBot product, UI, API, and authored-source term.
  • Cron schedule is the provider-level cadence supplied to Tilde.
  • A routine run is an ordinary signed Tilde invocation of the selected primary agent, with routine trigger metadata.
  • Do not expose a second user-facing “Schedule” resource.

The first slice supports Eve's recurring prompt/task form, not its channel-handoff handler form. Eve handlers use to(channel, target) and waitUntil(...); OpenBot does not own channel adapters because Tilde owns ChatKit ingress, delivery, and history. If a future public Tilde contract supports a durable handler workflow, add it through a separate decision rather than emulating channels locally.

Target layout

After #19:

configuration/agent/
└── routines/
    ├── daily-digest.md
    └── maintenance/
        └── cleanup.ts

Routines are root-agent only. configuration/agent/subagents/<id>/routines/ is rejected; a routine invokes the primary agent, which can delegate to a subagent through #15.

The path-relative filename is the stable routine slug: routines/maintenance/cleanup.ts becomes maintenance/cleanup.

Authored contract

Support two equivalent forms:

import { defineRoutine } from "@openbot/agent-service-provider/routines";

export default defineRoutine({
  cron: "0 9 * * 1-5",
  prompt: "Review outstanding work and prepare the weekday digest.",
});
---
cron: "0 9 * * 1-5"
---

Review outstanding work and prepare the weekday digest.
  • defineRoutine is a typed pass-through and accepts exactly one recurring prompt.
  • Cron uses a validated standard five-field expression with minute granularity and UTC semantics for the first release.
  • Prompt bodies are repository source and must not contain credentials, one-time links, or raw PII.
  • TypeScript routines are declarative. Do not execute arbitrary module code during discovery or deployment planning.
  • Discovery is recursive, deterministic, path-contained, and rejects duplicate/unsafe slugs, symlinks, invalid cron, empty prompts, unsupported fields, and subagent routines.
  • Include routine files and the normalized desired-state manifest in agent/deployment content digests.

Do not support both schedules/ and routines/ indefinitely. If an Eve-shaped schedules/ directory is found, fail with a migration message explaining OpenBot's deliberate Routine terminology and Tilde ownership.

Architecture

1. One normalized routine model

The agent-service package owns a provider-neutral RoutineDefinition containing only OpenBot domain data:

  • stable agent ID and routine slug;
  • cron expression and explicit timezone semantics;
  • bounded prompt content or content digest;
  • enabled state;
  • optional non-secret labels;
  • deterministic desired-state digest.

Markdown and TypeScript inputs normalize to the same model. Control-service, deployment planning, documentation, and tests consume that model instead of re-parsing source.

2. Tilde owns scheduling and invocation

Extend the narrow Tilde resource provisioner in #26 to reconcile each normalized routine against Tilde's public cron-job agent-invocation API using the generated client/SDK contract. Do not guess routes or handwrite fetch calls.

Tilde remains authoritative for:

  • cron evaluation and next-run calculation;
  • enabled/disabled runtime state;
  • signed invocation delivery;
  • invocation identity, retries, and run status;
  • creation or selection of the ChatKit session used for the run.

OpenBot stores only the source mapping, remote resource ID, last-applied digest, and non-secret status needed for planning, drift reporting, and reconciliation.

Do not generate Vercel Cron entries. Local and Vercel agent-service artifacts expose the same ordinary signed agent endpoint; Tilde invokes whichever deployed endpoint is registered for the agent.

3. Invocation contract

A routine invocation must enter the same authenticated agent path as any other Tilde run and carry validated metadata such as routine ID/slug, scheduled time, invocation ID, attempt, and trigger kind. Provider-specific wire fields stay inside the Tilde adapter.

  • Verify the Tilde signature before loading history, tools, prompts, or secrets.
  • Convert the routine prompt into the canonical inbound message/event format used by Stream directly from clients to agent endpoints and sync Tilde events asynchronously #25.
  • Expose a safe runtime trigger descriptor such as transport.kind = "tilde-routine"; do not fabricate a channel.
  • Run the normal prompt, tools, hooks, usage accounting, cancellation, and terminal-event flow.
  • Record enough non-secret correlation metadata to trace one scheduled tick through invocation, agent turn, tool work, and terminal status.

4. Delivery, concurrency, and idempotency

  • Treat scheduler delivery as at least once unless the public Tilde contract guarantees otherwise.
  • Use Tilde's stable invocation ID as the turn idempotency key so a retry cannot start duplicate model work or external side effects.
  • Apply the registered agent's concurrency policy (queue, interrupt, or queue_and_batch) rather than inventing a second routine-specific queue.
  • Define misfire behavior for delayed ticks and overlapping executions. The first release should not backfill an unbounded number of missed runs.
  • A changed cron/prompt updates the existing remote routine by stable ownership identity instead of creating a duplicate.
  • Deletion is ownership-safe and explicit; retain remote jobs not provably owned by the installation.

5. Sessions and output

Default each routine run to a new Tilde ChatKit session so independent runs do not accumulate unbounded conversational history. Preserve stable routine and invocation metadata for cross-run audit and memory lookup.

The routine is fire-and-forget from the owner's perspective, but its output remains a canonical Tilde run result and can use tools, update memory, write to the Computer, or explicitly send messages through Tilde-provided tools. It cannot park waiting for a human, CAPTCHA, OAuth, WebAuthn, or another interactive handoff in the first slice; those cases should fail with a clear status and link to #21 where applicable.

Do not silently post every routine result into an arbitrary chat channel. Delivery to an owner or external system must be an explicit tool/action available to the agent.

6. Owner experience and operations

Add an owner-facing Routines view backed by control-service/Tilde status APIs:

  • routine name, agent, normalized cadence, and UTC indication;
  • enabled/reconciled/drifted/error state;
  • next scheduled run and last terminal run;
  • redacted error summary and correlation ID;
  • explicit Run now for testing, using an authenticated Tilde invocation rather than a local dev-only route.

Repository source remains authoritative for cadence, prompt, and enabled state. The first UI is inspect/run-now only; editing or pausing should update source through the code-forge workflow in #14 rather than creating silent remote drift.

7. Local development

Provide a CLI command such as openbot routine run <slug> that:

  1. discovers and validates the same normalized routine;
  2. requires an initialized target agent;
  3. asks Tilde to invoke it once through the authenticated production-equivalent path;
  4. returns session/turn correlation IDs and optionally follows terminal status.

Do not run a background cron daemon in openbot dev. This keeps local behavior deterministic and avoids double firing while Tilde is active.

Delivery slices

Acceptance criteria

  • Nested .ts and .md routines normalize to deterministic slugs and the same typed model.
  • Invalid cron, empty prompts, unsafe paths, symlinks, unsupported handler/channel fields, schedules/, and subagent routines fail with actionable errors.
  • Deploy dry-run reports create/update/no-op/retain/delete without mutation or prompt/secret leakage.
  • Repeated deployment is idempotent and a changed definition updates one Tilde cron job rather than creating another.
  • No Vercel Cron or process-local scheduler is generated.
  • A scheduled tick reaches the signed primary-agent endpoint, runs the authored prompt, and produces one traceable terminal Tilde run.
  • Duplicate delivery does not create duplicate model turns or external side effects under the documented idempotency contract.
  • Agent concurrency, delayed/missed ticks, overlapping runs, endpoint failure, timeout, cancellation, and retry behavior have focused integration tests.
  • Each run uses the documented session strategy and does not silently publish output to a channel.
  • Hooks identify a routine trigger without receiving a fabricated channel context.
  • openbot routine run <slug> and the UI's Run now action exercise the same authenticated path as production scheduling.
  • Runtime secrets, webhook keys, raw tool output, and sensitive prompt content are absent from plan output, control state, logs, and routine status APIs.

Dependencies

Open decisions

  • Whether Tilde's public API exposes an immutable invocation idempotency key and explicit misfire policy, or whether OpenBot must supply them through the generated client contract.
  • Whether a future routine may opt into a stable session key for longitudinal work, with explicit history bounds.
  • Whether repository-owned enabled: false is needed in the first schema or deletion alone is sufficient.
  • Whether dynamic/user-created routines belong in a later control-state feature rather than this source-authored contract.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions