Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ review chains, or plans that sleep and resume later.
This layer deliberately reuses the deployment's existing scheduler and `work_items` queue. It does
not create a second lease, timer, or coordinator.

## Agent tools

The durable store is exposed through the first-party **Workflows** plugin, not as an ungoverned
internal shortcut. An administrator must enable the Workflows catalogue entry and grant its tools to
a Bot before that Bot can create or mutate workflow state. Calls derive the owner and Bot from the
active run connection; tool arguments cannot substitute another owner or Agent id.

The tool surface can create/list/read workflows, pause/resume/cancel them, start/wait/complete/fail
or retry steps, and add/list/remove per-step asset metadata. Waiting a step persists an exact future
timestamp and lets the shared durable wake bridge resume it later instead of keeping a browser turn
alive or polling in a loop.

This surface changes workflow state only. It does not grant Browser, Files, Computer, connector or
host access; execution of a step still uses the Bot's existing grants and policy at the time the
action is attempted.

## State model

A workflow run records its owner, Bot, destination channel, title and lifecycle:
Expand Down
44 changes: 44 additions & 0 deletions scripts/release-preflight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,9 @@ function checkDurableWorkflowState(): void {
const wake = read("server/src/workflows/wake.ts");
const server = read("server/src/index.ts");
const assetMigration = read("server/drizzle/0044_workflow_assets.sql");
const builtin = read("server/src/plugins/builtin-workflows.ts");
const transport = read("server/src/plugins/transport.ts");
const catalogue = read("server/src/plugins/catalogue.ts");

for (const evidence of [
'workflowRunStatus = pgEnum("workflow_run_status"',
Expand Down Expand Up @@ -1555,6 +1558,47 @@ function checkDurableWorkflowState(): void {
}
}

for (const evidence of ["useWorkflowTools(workflowStore)"]) {
if (!server.includes(evidence)) {
fail(
`workflows: builtin workflow tool store is not installed: ${evidence}`,
);
}
}
for (const evidence of [
'"builtin-workflows": builtinWorkflows',
'| "builtin-workflows"',
]) {
if (!transport.includes(evidence)) {
fail(`workflows: builtin workflow transport is missing ${evidence}`);
}
}
for (const evidence of [
'key: "workflows"',
'transport: "builtin-workflows"',
'"create_workflow"',
'"cancel_workflow"',
'"add_workflow_asset"',
]) {
if (!catalogue.includes(evidence)) {
fail(`workflows: governed catalogue surface is missing ${evidence}`);
}
}
for (const evidence of [
"connection.actorId",
"connection.botId",
'"create_workflow"',
'"wait_workflow_step"',
'"complete_workflow_step"',
'"retry_workflow_step"',
'"add_workflow_asset"',
'"list_workflow_assets"',
]) {
if (!builtin.includes(evidence)) {
fail(`workflows: builtin tool boundary is missing ${evidence}`);
}
}

for (const evidence of [
'CREATE TYPE "public"."workflow_run_status"',
'CREATE TYPE "public"."workflow_step_status"',
Expand Down
2 changes: 2 additions & 0 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import { hostAccessTools } from "./host-access/tools";
import { createOnboardingStore } from "./people/onboarding";
import { createPeopleStore } from "./people/store";
import { useRoutineTools } from "./plugins/builtin-routines";
import { useWorkflowTools } from "./plugins/builtin-workflows";
import { useComposioClient } from "./plugins/composio";
import { createComposioClient } from "./plugins/composio-adapter";
import { redirectUriFor } from "./plugins/oauth";
Expand Down Expand Up @@ -419,6 +420,7 @@ const pluginStore = createPluginStore({
const routineStore = createRoutineStore(database);
useRoutineTools(routineStore);
const workflowStore = createWorkflowStore(database);
useWorkflowTools(workflowStore);

/**
* Other Bots this person deliberately put in the same live channel as this Bot.
Expand Down
Loading
Loading