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
2 changes: 1 addition & 1 deletion docs/PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ the file path and parse details.

The TUI has an extensible slash-command framework. Built-ins include `/help` (shortcut + command overlay), `/model` (models-only picker for connected accounts; **Alt+A** or `/connect` adds a provider), `/settings`, `/permissions`, `/plugins`, `/clear`, `/new`, `/compact` (fold conversation context now, optional trailing instructions to the summarizer; does not wait for the 60% occupancy governor; idle success shows the fold and does not start a new turn), `/mcp` (enable, disable, or remove servers), `/handoff [optional instructions]` (folds context through the shared operator pipeline, then immediately starts the next turn with the instructions as the inbound content — default copy when omitted; unlike `/compact`, which stops after the fold, handoff always re-infers, so the operator can pivot goals without `/clear`; a handoff issued mid-tool-batch queues behind the in-flight batch and whichever boundary fires first runs the single fold), and `/yolo` (`/yolo [on|off|toggle]`, bare `/yolo` toggles), plus a `/<name>` command per available workflow. `/yolo` persists skip-permissions to the active settings file. That file is the user-global `~/.corbits/settings.json` by default, making the setting machine-wide; explicit `--config <path>` selects a different active file, and `/yolo` writes that file. An ordinary TUI launch without the same `--config` returns to the user-global source and does not modify the custom file. `--dangerously-skip-permissions` and `--yolo` are process-only aliases. Secret-guard and authz still apply. When a session starts with its active persisted setting already on, the TUI and `corbits exec` warn that permission prompts are disabled by saved settings at the active settings path and direct the operator to edit that file to re-enable them. Plugins can register additional commands.

**Default skills** exist out of the gate as first-party slash **actions**, not director names: `/implement`, `/plan`, `/refactor`, `/review`, `/pull-request`, `/issue`, `/docs`, `/interview`. Each one is a how-to playbook — the slash sends the skill body to the primary, which follows the steps. Skills do not assign identity or route the fleet; that stays on director system prompts. `/review` classifies the target first, then dispatches a selected fleet; `/pull-request` finds or opens the PR for the current branch, and `/review` takes a PR target and reviews it from a worktree; `/docs` is how to maintain PRODUCT / ARCHITECTURE / IMPLEMENTATION; `/implement` takes a plan and a ticket to a pushed branch (worktree, per-commit planner/build/reviewer loop, whole-branch review, push, hand off to `/pull-request`) — it does not steal planning from `/plan`. Substantial coder work consumes a planner / `/plan` plan first; tiny parent-DIY stays plan-optional. `/plan` authors an eng change plan (files, AC, non-goals, risks, ordered steps) and does not implement. `/issue` finds or creates the tracker issue: Linear MCP when available; otherwise it `ask_operator`s for the platform (GitHub etc.) and persists `Preferred issue tracker` in `.corbits/MEMORY.md` (GitHub via `gh issue create`). There is no first-party dispatch skill: the dispatch director orchestrates natively. `typescript` stays `use_skill` only (`user-invocable: false`); `git-worktrees` is a background library that is not listed for `use_skill`. Designer and the other specialists are not slashes; they remain closed directors via `spawn_agent(agent=…)`. There is no catch-all worker. Slash names are also available to the model via `skill_search` (descriptions) then `use_skill` (body). Disable the catalog in `/plugins` (`corbits-skills`) if you want them gone.
**Default skills** exist out of the gate as first-party slash **actions**, not director names: `/implement`, `/plan`, `/refactor`, `/review`, `/pull-request`, `/issue`, `/docs`, `/interview`. Each one is a how-to playbook — the slash sends the skill body to the primary, which follows the steps. Skills do not assign identity or route the fleet; that stays on director system prompts. Background reference skills (`typescript`, `git-worktrees`, and the `interchange` hub with its `interchange-agents`, `interchange-workflows`, `interchange-run-modes`, `interchange-embed-hub`, `interchange-hub-setup`, `interchange-hub-api`, `interchange-chat`, and `interchange-client-apps` references) have no slash; the model loads them with `skill_search` and `use_skill`, and each one ends with "need more, go here" pointers to the next skill in the chain. `/review` classifies the target first, then dispatches a selected fleet; `/pull-request` finds or opens the PR for the current branch, and `/review` takes a PR target and reviews it from a worktree; `/docs` is how to maintain PRODUCT / ARCHITECTURE / IMPLEMENTATION; `/implement` takes a plan and a ticket to a pushed branch (worktree, per-commit planner/build/reviewer loop, whole-branch review, push, hand off to `/pull-request`) — it does not steal planning from `/plan`. Substantial coder work consumes a planner / `/plan` plan first; tiny parent-DIY stays plan-optional. `/plan` authors an eng change plan (files, AC, non-goals, risks, ordered steps) and does not implement. `/issue` finds or creates the tracker issue: Linear MCP when available; otherwise it `ask_operator`s for the platform (GitHub etc.) and persists `Preferred issue tracker` in `.corbits/MEMORY.md` (GitHub via `gh issue create`). There is no first-party dispatch skill: the dispatch director orchestrates natively. `typescript` stays `use_skill` only (`user-invocable: false`); `git-worktrees` is a background library that is not listed for `use_skill`. Designer and the other specialists are not slashes; they remain closed directors via `spawn_agent(agent=…)`. There is no catch-all worker. Slash names are also available to the model via `skill_search` (descriptions) then `use_skill` (body). Disable the catalog in `/plugins` (`corbits-skills`) if you want them gone.

Providers are **models-first**: there is no standalone `/login` command. `/model` opens a **models-only list** (Recent, Favorites, then connected provider/model rows) — type-to-filter owns printable keys, so Connect is never a bare letter. **Alt+A** or `/connect` opens a dedicated add-provider selector over every first-class kind (OpenAI dual-path ChatGPT OAuth or API key, xAI, OpenCode Zen, Anthropic, Google, OpenCode Go, Z.AI Coding Plan, Ollama, Custom), each annotated with its live account count and never filtered out for “already connected.” **Alt+F** toggles favorite on the highlighted model. **Alt+D** persists the highlighted pair as the default without switching the live session. Advanced provider drill-down (edit/delete/tiers) stays on the advanced surface, not a bare printable key while the model list is filtering. OAuth providers open their existing browser login with a named account step so multiple accounts per kind coexist (`codex/work`, …). API-key providers use the same named-instance step before the key (auth-only form: instance name + key + fixed catalog base URL), so personal and team keys land as distinct catalog rows (`openai/default`, `anthropic/work`, …); reusing a name re-keys that instance after confirm. Custom remains a free-form single endpoint (full manual form). Successful connect refreshes the catalog and reopens the model list focused on the new account’s default model. OpenCode Go lists models from the live `/zen/go/v1/models` catalog (packaged seed on fetch failure), routes each by its protocol metadata (chat completions, OpenAI responses, or Anthropic messages) and can show subscription usage in the status bar when active (rolling 5h / weekly / monthly windows when the usage API responds; omitted on auth or network failure). When Go returns a quota or rate-limit error — including some HTTP 400 responses that carry limit payloads — Corbits classifies them so quota aborts cleanly and short provider rate limits remain retryable. On a free-tier or subscription quota hit, wait for the window to reset or use OpenCode Zen free models.

Expand Down
2 changes: 1 addition & 1 deletion docs/TELEMETRY.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ director ids from `DIRECTOR_IDS` (and the legacy `worker` alias) are reported
by id; project-defined or marketplace profile ids become `custom`.
`skill_used` carries `skill_name`: a first-party skill name reportable by
name from the closed `corbits-skills` allowlist in `src/telemetry/classify.ts`
(the slash workflows plus `git-worktrees`, `typescript`), or `custom` for anything else, including bundled background
(the slash workflows plus the background skills `git-worktrees`, `typescript`, and the `interchange` chain), or `custom` for anything else, including bundled background
skills outside the list. Unknown, project-local, and plugin-authored skill
names are never transmitted; `skill_name` is the only identifying-adjacent
property the event can carry.
Expand Down
2 changes: 1 addition & 1 deletion plugins/corbits-skills/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"name": "Corbits Skills",
"kind": "command",
"defaultEnabled": true,
"description": "Default operator skills and slash commands (implement, refactor, review, pull-request, issue, docs, interview, plan)."
"description": "Default operator skills and slash commands (implement, refactor, review, pull-request, issue, docs, interview, plan) and background reference skills (git-worktrees, typescript, interchange)."
}
100 changes: 100 additions & 0 deletions plugins/corbits-skills/skills/interchange-agents/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
name: interchange-agents
description: Build and embed an Interchange agent in code with tools, inference sources, and tests. Load when writing createAgent or defineAgent code.
user-invocable: false
---

# Interchange agents

Use `@intx/agent` when you want an agent inside your own program, CLI, or service. No hub is required.

## Minimal agent

```ts
import {
createAgent,
createDefaultDirectorRegistry,
createStaticCredentialResolver,
defineAgent,
type BaseEnv,
} from "@intx/agent";
import { noopAuditStore, permissiveAuthorize } from "@intx/agent/testing";
import { createIsogitStore } from "@intx/storage-isogit/node";

const storage = await createIsogitStore(contextDir); // directory must exist
const def = defineAgent({
id: "my-agent",
systemPrompt: "You are a helpful assistant.",
tools: [],
capabilities: [],
inference: { sources: [{ provider: source.provider, model: source.model }] },
});
const env: BaseEnv = {
sources: [source], // { id, provider, baseURL, credentialId, model }
defaultSource: source.id,
storage,
workdir: contextDir,
audit: noopAuditStore(),
authorize: permissiveAuthorize(),
directors: createDefaultDirectorRegistry(),
readCurrentMaterial: createStaticCredentialResolver({
[source.credentialId]: apiKey,
}),
};
const agent = await createAgent(def, env);
try {
const result = await agent.send(prompt);
if (result.type !== "reply")
throw new Error(`suspended on ${result.correlationId}`);
console.log(result.reply);
} finally {
await agent.close();
}
```

- `send()` returns a union. Anything other than `reply` means the agent suspended on a gate (approval, pending tool) and needs an inbound message with that `correlationId`.
- `permissiveAuthorize` and `noopAuditStore` are test stubs. A product supplies a real `authorize` built on `@intx/authz` and a real audit store.
- Re-running on the same `contextDir` resumes the conversation. Clone the directory and reset `HEAD` to rewind.

## Tools

```ts
import { defineTool } from "@intx/agent";
import { posix, type PosixToolEnv } from "@intx/tools-posix/sidecar-bundle";

let disposeTools: (() => Promise<void>) | undefined;
const posixTools = defineTool<PosixToolEnv>({
id: posix.id,
requires: posix.requires,
definitions: posix.definitions,
factory: (env) => {
const b = posix(env);
disposeTools = b.dispose;
return b;
},
});
// def.tools = [posixTools]; env gains toolCwd (tree the model edits)
```

`toolCwd` is what the model reads and edits. `workdir` is the isogit storage boundary. They are different. Tool bundle lifetimes belong to the caller: dispose on close and on failed `createAgent`. LSP is a plugin passed as `{ ...env, plugins: [lsp(env)] }`. A tool can return a `pendingMarker` to open a correlation gate.

## Inference

- List several `sources` for failover across providers and protocols. `agent.setSource()` hot-swaps without interrupting a call.
- Extra providers (OpenAI Responses, Codex, xAI, Ollama) come from Corbits packages: `use_skill corbits-inference`.
- Oversize tool output spills to a blob with a `tool-output://` URI (`sizeCapMaxChars`).

## Test

Use `@intx/inference-testing` for scripted replies and to swap `fetch`. Assert on tool_result content, not just absence of crashes.

## Examples to copy (upstream `examples/`)

`agent-quickstart` (start here), `coding-agent` (posix plus LSP), `agent-resume`, `agent-rewind`, `agent-audit-log`, `agent-blob-spill`, `agent-rich-tool`, `agent-structured-payload`, `agent-multi-provider`, `agent-gemini-image`, `posix-demo` and `ring-demo` (multi-agent mail). Run with `bun --conditions=intx-src run src/cli.ts`.

## Need more

- Reactor, directors, compaction, failover: upstream `docs/INFERENCE.md`.
- Durable multi-step work: `use_skill interchange-workflows`.
- Running it somewhere other than your process: `use_skill interchange-run-modes`.
- Back to the overview: `use_skill interchange`.
47 changes: 47 additions & 0 deletions plugins/corbits-skills/skills/interchange-chat/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
name: interchange-chat
description: Build chat, run progress, signals, and approvals for an Interchange workflow in a UI. Load when a person talks to a deployed agent or workflow, or when showing run status.
user-invocable: false
---

# Chat and run observation

Chat on Interchange is mail. People and agents share one mail bus, and a deployed run has an address. Progress is a separate feed.

## Pick the feed

| Need | Source |
| -------------------------------- | -------------------------------------------------------------------------------------- |
| Person sends a message | Mail to `<deploymentId>@<tenant.domain>` (`triggerWorkflowRun` or the mailbox `send`) |
| Agent reply text | The person's inbox: mailbox routes and SSE (`@corbits/mailbox`) |
| Status, steps, parked waits | Poll run events (`readWorkflowRunEvents`, or `createRunSession` which polls every 2 s) |
| Tool calls, reasoning | Run events or `GET T/workflows/runs/:runId/turns`, never mail (mail is plain text) |
| Human decision for a waiting run | `deliverWorkflowSignal` with a stable `signalId` |
| Tool approval | `GET T/approvals`, then `POST T/approvals/:id/approve` or `/reject` |

## How a turn flows

1. The first mail to a run address fires the run. The hub treats inbound mail to a run address as the trigger, whether it came from the HTTP route or an agent's `mail_send`.
2. While the run lives, later mail resumes its `onTrigger` section as input. A terminal run cannot be re-fired (409).
3. Replies land in the sender's inbox and publish on the mailbox event bus. Threads follow `In-Reply-To` and `References`.

## What the host must provide

The stock hub has no mailbox routes. The host mounts `@corbits/mailbox` at `T/mailbox`, wraps the hub's `persistMail` so agent mail reaches inboxes, and supplies a `deliver` that sends run-addressed mail through `POST T/workflows/:runId/mail` with the caller's session. See `use_skill corbits-hub-libs`. Workbench `apps/hub/src/mailbox-send.ts` is the reference. The default event bus is in memory, so use a shared one for multiple replicas.

## UI

`@corbits/react-ui` `ChatThread` takes `messages` as a prop: `{ id, role: "user" | "agent" | "system", parts, createdAt }` where parts are text, reasoning, tool, or file. Write the adapter that maps inbox rows to text parts and run events to tool and reasoning parts. A person's address is the auth user id at the tenant domain, lowercased.

## Cautions

- `onTrigger` body runs can silently skip when a second top-level run of the same deployment reaches the section (upstream known limitation, INTR-552). Do not build one run per conversation on `onTrigger` without testing a second run.
- An untimed park (`awaitSignal`, or an `approval: "ask"` tool) inside a `childWorkflow` is refused. Put human waits in the top-level run or in a `loop` body.
- Agent model context lives in the sidecar's agent repo. It is not the transcript, and compaction does not touch the mail.

## Need more

- Setting up the hub and deploying: `use_skill interchange-hub-setup`.
- Mounting mailbox, artifacts, memory: `use_skill corbits-hub-libs`.
- UI components: `use_skill corbits-ui-apps`.
- Handoff between workflows: `use_skill interchange-workflows`.
40 changes: 40 additions & 0 deletions plugins/corbits-skills/skills/interchange-client-apps/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
name: interchange-client-apps
description: Decide how an app builds on an Interchange hub it drives itself, and which skill to load next. Load when building a client-driven app, a desktop agent product, or a UI over a hub.
user-invocable: false
---

# Client-driven apps

A client-driven app owns its own setup: it creates its tenants, auth, grants, providers, workflow assets, and deploys over the hub's HTTP API, using stock `@intx/*` and `@corbits/*` packages. The hub stays vanilla. Reference app: Corbits Solution Builder (`corbitsdev/corbits-solution-builder`, `packages/installer`), with workbench (`corbitsdev/workbench`) as the server-composition reference.

## Find your task

| You are... | Load |
| ---------------------------------------------------- | ----------------------- |
| Deciding whether you need a hub at all | `interchange-run-modes` |
| Starting the hub inside your app, or pointing at one | `interchange-embed-hub` |
| Writing the app's first-run install and deploys | `interchange-hub-setup` |
| Calling a route and need its body | `interchange-hub-api` |
| Building chat, run status, signals, approvals | `interchange-chat` |
| Writing the workflow the app deploys | `interchange-workflows` |
| Mounting mailbox, artifacts, memory, cron | `corbits-hub-libs` |
| Building the UI | `corbits-ui-apps` |

## Shape

- **Host**, once per launch: embedded or remote hub, migrations, keys, mount, sidecar provisioner.
- **Client**, browser or script: everything else through one `Transport`.
- **`install()`**: idempotent, runs on first launch and after changes, and imports no database code.
- **Config**: one hub-URL setting switches embedded to remote with no client change.

## Rules that save time

- Start without a hub if you only need to run a workflow in one process (`runLocal`). Move up when waits must outlive the process, credentials are shared, or triggers run while you are away.
- Deploy per project or stage, when the work first needs it. Do not provision everything at install.
- A person's chat turn is mail to the run address, not a hub chat API.
- The REST API has no service token. An app acting as "system" holds a user account and keeps its cookie.

## Need more

- Overview: `use_skill interchange`. Package catalog: `use_skill corbits`.
Loading
Loading