Saved agents

The unit of composition on Coresource is the saved agent: a definition stored and versioned on the server, addressable by id, runnable by anything that can make an HTTP request.

A multi-agent workflow is several saved agents run in sequence or in parallel, with output handed from one to the next. There is no orchestration DSL to learn and no agent-to-agent protocol to implement. The composition lives in your code, where you can debug it.

Why saved agents#

An agent definition created with createAgent() exists only in your process, although any durable run it starts remains on the platform. A saved agent makes the definition itself a durable resource:

  • Addressable. It has an id. Anything holding that id can run it.
  • Versioned. Updates create immutable definition versions, and a run can pin to one.
  • Shared. Several services can run the same definition without duplicating its config.
  • Language-agnostic. It is reachable over HTTP, so a Python service can run an agent a TypeScript service defined.

That last point is what makes them the composition primitive. A workflow built from saved agents is not a TypeScript construct. It is a set of server-side resources your orchestration code happens to call.

Coming soon: authoring and managing saved agents directly in the console. Today they are created through the SDK or the HTTP API, as below.

Creating one#

TypeScript
import { CoreSource } from '@coresourceai/sdk';

const coresource = new CoreSource();

const researcher = await coresource.agents.create({
  name: 'invoice-researcher',
  type: 'saga',
  systemPrompt: 'Validate claims using primary sources. Cite every material claim.',
  model: 'openai/gpt-5.5',
  skills: ['anthropics/skills/skills/pdf'],
  outputs: ['report.md']
});

console.log(researcher.id);

const result = await researcher.run('Validate these invoice-management benchmarks.');
console.log(result.status, result.text);

The returned handle carries the operations you would expect:

TypeScript
import { CoreSource } from '@coresourceai/sdk';

const coresource = new CoreSource();
const agent = await coresource.agents.retrieve('agt_123');

const result = await agent.run('Do the work.');
const updated = await agent.update({ description: 'Primary-source invoice analyst' });
const versions = await agent.versions();

console.log(versions.length);

create() returns a handle typed to the config you passed, so a saga config gives you a handle with start() on it. retrieve() cannot know the type ahead of time and returns a union. Narrow on type before reaching for start(), which one-shot agents do not have:

TypeScript
import { CoreSource } from '@coresourceai/sdk';

const coresource = new CoreSource();
const agent = await coresource.agents.retrieve('agt_123');

if (agent.type === 'one-shot') {
  const result = await agent.run('Answer the question.');
  console.log(result.text);
} else {
  const run = await agent.start('Do the longer work.');
  console.log(run.id);
}

Or skip the handle entirely and use the resource, which takes an id directly:

TypeScript
import { CoreSource } from '@coresourceai/sdk';

const coresource = new CoreSource();

const result = await coresource.agents.run('agt_123', 'Do the work.');
const run = await coresource.agents.start('agt_123', 'Do the longer work.');

What cannot be saved#

Function tool handlers and local skill bundles are process-local. A server-side definition has no way to call back into a process that may not exist. Including either throws a TypeError.

TypeScript
import { CoreSource } from '@coresourceai/sdk';

const coresource = new CoreSource();

// Throws: a handler cannot be serialised into a stored definition.
// await coresource.agents.create({
//   name: 'broken',
//   type: 'agent',
//   tools: [{ name: 'lookup', handler: () => null }]
// });

For an agent needing your own tools, either keep it in-process with createAgent(), or expose those tools as an MCP server the saved definition can reference by connection id. The second is what makes a tool-using agent shareable.

Versioning#

Updates create immutable versions. Pin a run to one when you need a stable definition. Model output can still vary between runs.

TypeScript
import { CoreSource } from '@coresourceai/sdk';

const coresource = new CoreSource();

// Optimistic concurrency: fails with a ConflictError if someone else
// updated the definition since you read version 3.
const updated = await coresource.agents.update(
  'agt_123',
  { systemPrompt: 'Validate claims using primary sources. Prefer 2024+ sources.' },
  { expectedVersion: 3 }
);

// Pin a run to a known-good definition rather than tracking latest.
const result = await coresource.agents.run('agt_123', 'Validate the benchmarks.', {
  agentVersion: 3
});

Pin production traffic to a version and promote deliberately. Running "latest" means a prompt edit ships to production the moment someone saves it.

Invoking over HTTP#

A saved agent is runnable with an HTTP request. For one-shot saved agents, the response uses the OpenAI Responses shape.

Shell
curl https://api.coresource.ai/v1/agents/agt_123/runs \
  -H "Authorization: Bearer $CORESOURCE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: nightly-2026-07-29" \
  -d '{
    "prompt": "Validate these invoice-management benchmarks.",
    "input": { "quarter": "Q2" }
  }'

This makes a saved agent callable from a Python service, workflow engine, or any other HTTP client. The request still uses the Coresource path and prompt body shown above; OpenAI-compatible response parsers can consume the one-shot response, but an OpenAI client is not a drop-in caller for this custom endpoint.

See HTTP API for the full surface.