Little documentation
Guides and APIBuild Agents

Agent Folders

Keep many agents in one repo, each a self-contained folder, with a CLI to scaffold and test them.

Little Harness: published on npm as little-harness@0.2.0-alpha.0 (2026-10-05). Sections labeled UNRELEASED describe source-only changes. Release status

A workspace lets you keep many harnesses in one repo, each as its own folder under agents/. A folder is loaded into a runnable harness by auto-discovering its tools/, skills/, and workflows/ — createHarness itself is unchanged, and inline createHarness(...) still works exactly as before.

Layout

The workspace marker little-harness.json ({ "agents": "agents" }) sits at the root; each agent is a folder under agents/, named by the agent's id:

little-harness.json
agent.ts
instructions.md
lookup_order.ts

agent.ts

The entry is a plain createHarness call. Tools, skills, and workflows are omitted here — they are discovered from the folder and merged in when the agent is loaded.

import { createHarness, localHost } from "@little-stack/harness";
import { anthropic } from "@ai-sdk/anthropic";

export default createHarness({
  model: anthropic("claude-opus-4-6"), // set the selected provider's required env vars
  system: "You are a support agent.", // or omit and use instructions.md
  host: localHost(),
});

When generated by little-harness init, the provider import and model id come from the AI SDK provider/model selected in the wizard — the anthropic provider is the default (openai in the unreleased source). The wizard currently offers 24 provider choices: the official AI SDK language-model providers plus a custom OpenAI-compatible API scaffold. You can also type a custom provider model id when the exact model is not listed.

If both agent.ts's system and a sibling instructions.md exist, agent.ts wins; if system is omitted, instructions.md becomes the system prompt.

Tools — tools/*.ts

Each file default-exports a normal AI SDK tool; the tool name is the filename without extension. Inline tools passed to createHarness win on a name collision.

// tools/lookup_order.ts
import { tool } from "ai";
import { z } from "zod";

export default tool({
  description: "Look up an order by id.",
  inputSchema: z.object({ orderId: z.string() }),
  execute: async ({ orderId }) => ({ orderId, status: "shipped" }),
});

Skills — skills/<name>/

Each subdirectory is one skill:

  • SKILL.md only → a local skill (skill("./skills/<name>")).
  • skill.ts (default-exports skill(...)) → a declared skill — used directly. This is how you pull in remote/GitHub skills, and it wins over a sibling SKILL.md.
// skills/frontend-design/skill.ts
import { skill } from "@little-stack/harness";
export default skill("https://github.com/anthropics/skills#v1.2.0", { skills: ["frontend-design"] });

Remote skills resolve, pin (by commit), and stage exactly as when passed inline to createHarness — at runtime local and remote skills are both staged under .agents/skills/<name>/.

Workflows — workflows/*.ts

Each file default-exports a HarnessWorkflow — typically a Little Workflow adapted with asHarnessWorkflow — and becomes one tool named from the workflow id. Discovered workflows are appended to any workflows passed in agent.ts. Test and spec files are skipped.

// workflows/refund.ts
import { asHarnessWorkflow, defineWorkflow } from "@little-stack/workflow";

const refund = defineWorkflow({ id: "billing.refund", /* input, output, model, tools */ });

export default asHarnessWorkflow(refund, { definitionIdentity: "billing.refund@1" });

A bare defineWorkflow(...) default export is rejected at load time: it has no executionMode or definitionIdentity. See Triggering a workflow from an agent for the tool result shape and workflowBudgets.

Schedules — schedules/*.ts

Each file default-exports defineSchedule({ cron, prompt }), a run of the agent on a timer; the file name is its id. loadHarness() ignores them; little-harness dev and start run them. See Serving An Agent.

// schedules/morning-backlog.ts
import { defineSchedule } from "@little-stack/harness";

export default defineSchedule({
  cron: "0 9 * * 1-5",
  timezone: "Asia/Hong_Kong",
  prompt: "Pre-approve every application received since yesterday.",
});

Connectors And UI

connectors/ is reserved for connection layers such as Slack, Telegram, or web chat routes. It is ignored by loadHarness(). little-harness start serves each one at /connectors/<id>; in your own server, use loadChatSdkConnector() or loadWebRichConnector() from @little-stack/harness/connectors/runtime when a route wants to expose an agent through a chat surface. String connector ids still work from the runtime subpath; the loaders import discovery dynamically when needed. Use @little-stack/harness/connectors when the route directly calls discovery helpers such as discoverConnectors().

Simple connectors can be flat files like connectors/slack.ts. Use nested connector folders when the connector owns extra files:

connectors/
  slack/
    connector.ts
    tools/
      add_reaction.ts
    renderers/
      lookup_order.tsx

Connector tool extensions are scoped by folder, so connectors/slack/tools/add_reaction.ts extends the add_reaction tool only when the slack connector is active. The extension file does not repeat connector: "slack"; the folder name is the connector id. These tools are for real platform capabilities such as reactions or modals. Presentation-only web/json-render code belongs in ui/ or connector renderers, not in model-visible tools.

ui/ is an optional home for shared web UI types and components. It is useful when multiple web apps render the same agent-specific tool output parts.

CLI

npx @little-stack/harness@alpha init # guided workspace wizard
little-harness init --here    # scaffold into the current directory
little-harness new support    # add agents/support/ to an existing workspace
little-harness test support   # load the agent and chat with it interactively (REPL)
little-harness dev support    # serve it, restart on change, chat in the terminal
little-harness start support  # serve it: connectors, an HTTP API, schedules

test opens an interactive REPL: type a message, the agent streams its reply, and the conversation persists across turns. Assistant text, reasoning, tool calls, and tool results are printed as labeled blocks, with tool input and output payloads shown when available. Type :reset to clear the conversation or :exit to quit.

Loading from code

You can load an agent folder yourself (the CLI uses these under the hood):

import { loadHarness, loadWorkspace } from "@little-stack/harness";

const { agents } = await loadWorkspace();          // enumerate agents/*
const harness = await loadHarness("agents/support"); // a runnable Harness with tools, skills, and workflows merged

To run a folder's agent on a different host than the one its agent.ts configures — for example a throwaway data directory in a test or a script — pass host. The loaded harness then takes its sessions from that host:

import { loadHarness, localHost } from "@little-stack/harness";

const harness = await loadHarness("agents/support", {
  host: localHost({ dataDir: ".little-harness-test" }),
});

UNRELEASED: the host option exists only in the current source. In published 0.2.0-alpha.0, loadHarness(agentDir) takes no options.

On this page