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
30 changes: 22 additions & 8 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,24 @@ handful of domain packages built on top.
## Tenancy

A workbench is a plain Interchange tenant — there is no separate
"workbench" tenancy layer. Every user gets a tenant on sign-up; a
"workbench" tenancy layer. Every user gets a root workspace tenant on
sign-up, and creating a workbench creates a child tenant underneath it; a
principal is a person or an agent that can act inside one.

## Workbench mail

There is no chat-specific data model. A conversation is a thread in
`@corbits/mailbox`: `GET /me/inbox/threads` lists threads, `POST
/me/inbox/send` sends a message (as a person or, addressed as an agent
principal, as that agent). Fan-out, read state, and threading are the
library's; the hub only resolves the caller's tenant/principal and mailbox
address.
There is no chat-specific data model. A workbench's conversation is its own
child tenant's mailbox in `@corbits/mailbox` (`/api/tenants/:id/mailbox/me/inbox/...`).
`sendToWorkbench` fans out to every live agent in the workbench unless an
`@Name` mention narrows the recipient list, and appends a trailing
`Participants:` roster (stripped when rendering a person's echoed-back
message) so agents know each participant's run address and copy the person on
handoffs. A person's address (`<userId>@<domain>`) is lowercased on both client
and hub so small models that lowercase addresses on reply still match the
thread's participant; mailbox delivery matches the local part
case-insensitively.

## Agents and workflows
## Agents, workflows, and scheduling

`packages/worker` is the one harness a bench's worker, and any agent
Workbench creates, deploys on. Its tools are `@intx/tools-mail` over the
Expand All @@ -51,6 +56,15 @@ requires its use on the deployer's authority, and the sidecar hands the
tool an origin-pinned mediated fetch. The worker writes further workflows and
agents as code; the person deploys them through the stock deploy route.

Scheduled workflows use `@corbits/cron` (`/api/tenants/:id/cron`), targeting a
workflow by its source asset's definition name so schedules survive redeploys.
At fire time the ticker resolves the definition name to the live anchor run
and delivers via the stock mail trigger route (`POST /workflows/:runId/mail`,
which Run Now also calls directly). If no anchor run is live between runs, the
schedule waits (`waitingSince`) and resumes on the next delivery; it stops
permanently (`stoppedReason: "agent_deleted"`) only when the definition itself
is deleted.

## Tool visibility

Interchange sends a model every tool on every turn. Workbench keeps that
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ local [Ollama](https://ollama.com) works with no key, see
| ------------- | ---------------------------------------------------------------- |
| `apps/` | `hub` (API), `web` (React client), `sidecar` (execution host) |
| `packages/` | Corbits libraries, and `worker`, the default agent harness |
| `workflows/` | Deployable workflow packages |
| `vendor/intx` | Hand-copied Interchange packages, see [VENDORED.md](VENDORED.md) |

## Develop
Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/auth/dither-background.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ const STRENGTH_LERP = 0.08; // cursor influence easing per frame
const FRAME_MS = 1000 / 30; // animation cap (~30fps) to spare the CPU
const ASSET = "/images/hero-dither.png"; // same-origin source image

// Renders on a plain 2D canvas, not WebGL — see
// docs/auth-dither-background.md for why.
// Renders on a plain 2D canvas, not WebGL: a WebGL layer failed to composite
// behind the auth panel, whereas 2D canvas is CPU-rasterized and retains its
// last frame when requestAnimationFrame pauses on a hidden tab.
export function DitherBackground({ className }: { className?: string }) {
// The animation belongs to the canvas element, so it starts and stops with
// it: a ref callback with a cleanup, never an effect reaching for a ref.
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/chat/threads-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ export function subscribeToInbox(tenantId: string, onChange: () => void): () =>
};
}

// Workbenches: see docs/chat-mail-threading.md.
// Workbenches: see ARCHITECTURE.md (§ Workbench mail).

export type WorkbenchParticipant = {
readonly id: string;
Expand Down Expand Up @@ -478,8 +478,8 @@ async function isTerminalRunBody(response: Response): Promise<boolean> {
return !(TerminalRunBody(await response.json().catch(() => undefined)) instanceof type.errors);
}

// The one send seam for a workbench (see docs/chat-mail-threading.md for
// the roster mechanism).
// The one send seam for a workbench (see ARCHITECTURE.md § Workbench mail
// for the roster mechanism).
export async function sendToWorkbench(input: {
readonly workbenchTenantId: string;
readonly participants: readonly WorkbenchParticipant[];
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/chat/workbench-roster.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// See docs/chat-mail-threading.md for the roster mechanism. `stripRoster`
// is the inverse, used to keep the block out of a person's echoed-back sent
// message.
// See ARCHITECTURE.md (§ Workbench mail) for the roster mechanism.
// `stripRoster` is the inverse, used to keep the block out of a person's
// echoed-back sent message.

const ROSTER_HEADING = "Participants:";

Expand Down
3 changes: 1 addition & 2 deletions apps/web/src/needs-converge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,7 @@ export type ForkSubThreadInput = {
};

/** Replays by native Message-ID when parent + subject + recipients + body
* match an already-recorded fork; any difference sends anew. See
* docs/chat-mail-threading.md. */
* match an already-recorded fork; any difference sends anew. */
export async function forkSubThread(
storage: StringStorage,
hub: StockHub,
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/pages/workbench-page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// See docs/chat-mail-threading.md. The bench pill opens a drawer that pushes
// the thread left; the drawer's Information tab carries the bench overview.
// The bench pill opens a drawer that pushes the thread left; the drawer's
// Information tab carries the bench overview.

import { Button, EmptyState, PageShell, Skeleton } from "@corbits/react-ui";
import { PanelLeft, WarningCircle } from "@/lib/icons";
Expand Down
5 changes: 2 additions & 3 deletions apps/web/src/routines-api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// The Routines page's seam to stock workflow deployments. See
// docs/routines-scheduling.md for the schedule join and why run-now/
// run-now goes through the stock trigger route.
// The Routines page's seam to stock workflow deployments and `@corbits/cron`
// schedules (see ARCHITECTURE.md § Agents, workflows, and scheduling).

import { type } from "arktype";
import { useQuery } from "@tanstack/react-query";
Expand Down
2 changes: 0 additions & 2 deletions apps/web/src/settings/section-registry.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// See docs/settings-sections.md for the grouping rationale.

import { Cpu, Key, ListBullets, Shield, Star, User, Users } from "@/lib/icons";

import { AccountSection } from "./account-section";
Expand Down
8 changes: 4 additions & 4 deletions docs/GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ paths keep the platform's own names.
| **User** | principal | An identity that can act in a workspace or workbench — human or agent. |
| **Agent** | principal (agent) | A named coworker principal, not a template. |
| **Worker** | — | A workbench's own persistent agent, deployed on the `packages/worker` harness. Given a job it can't already do, it authors a new tool, skill, or workflow as code, deploys it with only the access it needs, and uses it. |
| **Thread** | mailbox thread | The ordered messages of one conversation, served by `@corbits/mailbox` (`GET /me/inbox/threads`, `POST /me/inbox/send`). |
| **Thread** | mailbox thread | The ordered messages of one conversation, served by `@corbits/mailbox` (`/api/tenants/:id/mailbox/me/inbox/...`). |
| **Grant** | grant | Permission for a principal to act on a resource. |
| **Approval** | approval | A human decision gating an external side effect. |
| **Hub** | hub | The API and coordination service a workspace lives on. |
| **Sidecar** | sidecar | The execution host that runs agent turns and workflow runs for a hub. |

## Package naming

`@corbits/*` is the default scope for every package under `packages/`,
`tools/`, `agents/`, `skills/`, and `workflows/` — with a kebab-case kind
suffix where a package is one of a family (`-agent`, `-tools`).
`@corbits/*` is the default scope for every package under `packages/` —
with a kebab-case kind suffix where a package is one of a family
(`-agent`, `-tools`).
14 changes: 0 additions & 14 deletions docs/auth-dither-background.md

This file was deleted.

42 changes: 0 additions & 42 deletions docs/chat-mail-threading.md

This file was deleted.

36 changes: 0 additions & 36 deletions docs/routines-scheduling.md

This file was deleted.

17 changes: 0 additions & 17 deletions docs/settings-sections.md

This file was deleted.

Loading