Skip to content

Commit cfc6105

Browse files
docs(skills): frame interchange run modes as a control-plane ladder and add hub sharing, chat, and handoff
1 parent 4d46e14 commit cfc6105

4 files changed

Lines changed: 75 additions & 16 deletions

File tree

‎plugins/corbits-skills/skills/interchange-client-apps/SKILL.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,24 @@ The flagship example is Corbits Solution Builder (`corbitsdev/corbits-solution-b
2626
6. **Deploy.** `deployWorkflow(transport, tenantId, { source: { kind: "asset", assetId, package: { format: "source", commitSha } }, entry, sourceOfferingIds, defaultSourceOfferingId })`. The chain freezes at deploy, so a model switch is a new deployment. A deploy needs a non-empty chain and a sidecar provisioner.
2727
7. **Run.** `triggerWorkflowRun`, then fold `readWorkflowRunEvents` (or `createRunSession`, which polls every 2 s). Answer waits with `deliverWorkflowSignal(transport, tenantId, runId, { runId, signalName, signalId, payload })`. A repeated `signalId` with a different payload is a 409. Resolve tool approvals with `POST /approvals/:id/approve` or `/reject`, which needs `approval:*` / `resolve`.
2828

29+
## Sharing a hub between apps
30+
31+
- Tenant slugs are one global namespace per hub, so prefix them: an app root tenant (`<appid>`) and child tenants per project (`<appid>-<project>-<id>`). Solution Builder finds its root by slug in `GET /api/me/principals` and creates it if absent. A slug clash returns 409, so re-resolve instead of failing.
32+
- Catalog, providers, OAuth clients, credentials, and placement policy inherit down the tenant chain. Assets, roles, principals, and grants do not. Put shared keys in a parent tenant, and keep per-project assets, deployments, artifacts, and mail in a child.
33+
- Do not put two apps in one tenant unless every asset and credential name is prefixed. Names are unique per tenant.
34+
- Apps share the hub's provisioner pool, not a sidecar. Every deployment gets its own sidecar allocation.
35+
- Every `ensure*` step adopts what exists and never overwrites. The tenant create is the only step that is not idempotent.
36+
37+
## Chat is mail
38+
39+
Chat is not a stock hub feature. The host mounts `@corbits/mailbox` (`use_skill corbits-hub-libs`).
40+
41+
- A person's turn is a mail send to the run address (`<deploymentId>@<tenant.domain>`). The mailbox `deliver` hook calls the hub's `POST /workflows/:runId/mail`. The first message fires the run, and later ones resume an `onTrigger` section.
42+
- Replies arrive in the person's inbox over mailbox SSE (`/mailbox/me/inbox/events`). Threads follow `In-Reply-To` and `References`.
43+
- Mail carries plain text only. Progress, tool calls, and approvals come from polled run events (`readWorkflowRunEvents`) and `/approvals`.
44+
- `ChatThread` takes `messages` as a prop. You write the adapter from mail rows plus run events.
45+
- Agent model context lives in the sidecar's agent repo and is not the transcript.
46+
2947
## Rules that save time
3048

3149
- Only the browser transport ships. Outside a browser, supply your own: base URL, cookie jar, JSON content type, `ApiError` mapping, empty 202 and 204 bodies. In process you can call `hub.app.fetch(new Request(origin + path))` with the session cookie.

‎plugins/corbits-skills/skills/interchange-embed-hub/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ user-invocable: false
66

77
# Embedding an Interchange hub
88

9-
Use this when your app owns the hub process: a desktop host, a single binary, or a server that mounts Interchange next to its own routes. For what the app then does over HTTP, see `use_skill interchange-client-apps`.
9+
An embedded hub is a local control plane. `runLocal` has no control plane, and a remote hub has one elsewhere. Client code is identical for embedded and remote, so build against `Transport` and a base URL (`use_skill interchange-run-modes` for when to pick which). Use this when your app owns the hub process: a desktop host, a single binary, or a server that mounts Interchange next to its own routes. For what the app then does over HTTP, see `use_skill interchange-client-apps`.
1010

1111
## Stock vs composed
1212

‎plugins/corbits-skills/skills/interchange-run-modes/SKILL.md‎

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,53 @@
11
---
22
name: interchange-run-modes
3-
description: Pick where an Interchange agent runs (in-process, local hub, remote hub plus sidecar) and how sidecars, credentials, and placement work.
3+
description: Pick where the Interchange control plane lives (none with runLocal, embedded hub, remote hub) and how sidecars, credentials, and placement work.
44
user-invocable: false
55
---
66

77
# Interchange run modes
88

9-
Pick the smallest mode that meets the need.
9+
The question is where the control plane lives. Your workflow and agent code is the same in every mode.
1010

11-
| Situation | Mode |
12-
| ----------------------------------------------------- | -------------------------------------------------------------- |
13-
| One agent inside your own CLI or app, one user | In-process `createAgent` with an isogit `contextDir` |
14-
| Coding agent over a local tree | In-process plus `@intx/tools-posix` (and LSP), set `toolCwd` |
15-
| Test a workflow | `runLocal` with scripted inference |
16-
| Durable steps, crash resume, approvals, audit | Hub plus sidecar |
17-
| Multiple users or tenants, grants, pushed credentials | Hub required |
18-
| Host behind NAT, on a laptop, or firewalled | Sidecar dials out to the hub |
19-
| "Desktop" personal agent | In-process, or a local hub that spawns a local sidecar process |
20-
| Untrusted workload | Provisioner that declares an `isolation:*` capability |
11+
| Tier | Control plane | Use it when |
12+
| ----------------------------- | ------------------------------- | ----------------------------------------------------------------------------- |
13+
| `runLocal` / in-process agent | none | Scripts, CLI tools, CI, tests, dev loops, one user, run ends with the process |
14+
| Embedded hub | in your app process | You need hub features but want one local binary (desktop app) |
15+
| Remote hub | a hosted hub, sidecars dial out | Shared team, runs must outlive any one machine, work placed on other hosts |
2116

22-
There is no dedicated desktop harness in the repo. Desktop means the local-process mode. Container, VM, Workers, and browser hosts are in progress, not shipped.
17+
Embedded and remote differ only in a base URL. An app written against the hub `Transport` switches by config (`use_skill interchange-embed-hub`, `use_skill interchange-client-apps`). Moving up from `runLocal` is a deploy, not a rewrite, so start without a hub unless you need one of the reasons below.
2318

24-
## In-process (no hub)
19+
## Skip the hub when
2520

26-
See `use_skill interchange-agents`. There is no tenancy, remote credential push, or multi-user auth. You supply `authorize` and audit yourself.
21+
- Nothing to stand up: no database, keys, auth origin, or sidecar processes, and a cold start in milliseconds.
22+
- State is inspectable and disposable (a directory), with no second source of truth.
23+
- Tests and CI want hermetic runs with scripted inference and no ambient credentials.
24+
- One person starts the run and watches it end.
25+
- HITL is fine while the process lives: `run.signal(name, payload, signalId)` answers an `awaitSignal`.
26+
- You want to avoid depending on the alpha hub surface.
27+
28+
## Want a control plane when
29+
30+
- Waits must outlive the process (approvals, `awaitSignal`, timers): the hub parks, persists, and resumes after a crash. `runLocal` has no durability and a crashed run is terminal.
31+
- Credentials are shared and rotated: the hub seals provider keys and freezes a failover chain at deploy.
32+
- Triggers run while you are away: mail, cron, webhooks.
33+
- Several principals need grants, an audit trail, approvals routes, and spend accounting.
34+
- Work should run in a sandbox or on another host (sidecar placement).
35+
- Runs need addresses so people and agents can mail them.
36+
- A deploy must be pinned to a commit.
37+
38+
## runLocal
39+
40+
`runLocal(definition, { authorize, hasUpstreamSignalResolver, ... })` returns `{ runId, complete, cancel, signal }`. `authorize` and `hasUpstreamSignalResolver` are required; pass `true` for a top-level run you will signal.
41+
42+
- Supported: signals, authorize, actions (need `actionResolver`), loops (need `loopFns`), inline child workflows, timers, an in-memory event log in `RunResult.events`, cancel, and `resumeFromEvents` if you feed the log back yourself.
43+
- Not supported: persistence, crash resume, drain, hub approval rows or `/approvals` routes, mail and triggers, tools, hub credentials, placement.
44+
- The default step invoker is a stub that returns `{ output: null }`. To run a model, pass an `invokeStep` that builds `createAgent` and calls `agent.send`.
45+
- An untimed `awaitSignal` inside a `childWorkflow` fails the child, because nothing can answer it.
46+
- Unverified: whether `onTrigger` sections run locally.
47+
48+
For a single agent without a workflow, use in-process `createAgent` with a git-backed store and `@intx/tools-posix` (`use_skill interchange-agents`). You supply `authorize` and audit.
49+
50+
Desktop means a local process: in-process, or an embedded hub that spawns a local sidecar process. There is no dedicated desktop harness. Container, VM, Workers, and browser hosts are in progress, not shipped.
2751

2852
## Hub plus sidecar
2953

‎plugins/corbits-skills/skills/interchange-workflows/SKILL.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,23 @@ const result = await run.complete; // { runId, terminalStatus, outputs }
8585

8686
`POST /api/tenants/:t/workflows/deployments` installs, probes (on an airlocked sidecar), gates on the operator's approval set, freezes, then provisions a sidecar. Trigger with `/:runId/mail`, answer waits with `/:runId/signals`. The in-tree hub registers no provisioner, so it cannot deploy until one is injected: `use_skill interchange-run-modes`.
8787

88+
## Handoff between workflows
89+
90+
There is no primitive that starts or signals another deployment from inside a workflow. Choose by need:
91+
92+
| Need | Use |
93+
| ---------------------------------------- | ------------------------------------------------------------ |
94+
| Human turn into a run | Mail to the run address |
95+
| Human decision into a parked run | `deliverWorkflowSignal` with a stable `signalId` |
96+
| Agent to agent, conversational | `mail_send` to the peer's run address (unverified: triggers) |
97+
| Bounded sub-task, no human | `childWorkflow` (an untimed park inside fails the child) |
98+
| Rework loop with a human gate in one run | `loop` with `awaitSignal` in the body |
99+
| Many human turns in one living run | `onTrigger` section |
100+
| Large or durable content | Artifact or memory reference plus sha256, not the payload |
101+
| Start a different deployment | The client or host (ensure deploy, then trigger) |
102+
103+
Solution Builder orchestrates from the client: it mails per-stage specialists, sends `project.decision` signals to one project workflow, and passes artifact references. On redeploy it starts a new generation and replays recorded decisions through it.
104+
88105
## Need more
89106

90107
- Full pitfall list: upstream `docs/WORKFLOW_AUTHORING.md` and `examples/workflow-quickstart`.

0 commit comments

Comments
 (0)