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#
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:
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:
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:
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.
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.
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.
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.
