Quickstart

By the end of this page you will have run two agents: a one-shot agent that returns a single model response, and an agent that calls a tool you wrote and writes a file you keep.

Before you start#

  • Node.js 20 or newer. Check with node --version.
  • A Coresource API key. Create one in the console.

Step 1: Install and authenticate#

Shell
npm install @coresourceai/sdk
export CORESOURCE_API_KEY='your-api-key'

The SDK is ESM-only, so use import, not require. Every entry point reads CORESOURCE_API_KEY from the environment.

To pass the key explicitly instead, point at another deployment, or supply your own fetch, see Authentication.

Step 2: Run a one-shot agent#

A one-shot agent returns a single model response with no tools or managed runtime. Use it when the model needs to answer once rather than perform iterative work.

TypeScript
import { createAgent, isCoreSourceError } from '@coresourceai/sdk';

const answerer = createAgent({
  type: 'one-shot',
  systemPrompt: 'Answer clearly and concisely.',
  model: 'deepseek/deepseek-v4-flash'
});

try {
  const result = await answerer.run('Explain three-way invoice matching.');
  console.log(result.text);
  console.log(`${result.usage.totalTokens ?? 0} tokens`);
} catch (error) {
  if (isCoreSourceError(error)) {
    console.error(`${error.code} (${error.status}): ${error.message}`);
  } else {
    throw error;
  }
} finally {
  await answerer.close();
}

Three things are worth noticing.

createAgent() is synchronous and lazy. It does no network work until run(). Nothing is validated against the server at construction time.

close() belongs in a finally. It releases resources held by the handle and makes further use of it an error. It is especially important when an agent or saga uses custom tools, runtime tools, local skills, or local MCP, because those capabilities keep an SDK-hosted connection to your process open.

API, network, and timeout failures use typed errors. isCoreSourceError narrows those failures and exposes code, status, and retriable. Local configuration and lifecycle mistakes can instead throw TypeError or Error, so the else branch keeps unknown failures visible.

Step 3: Run an agent with your own tool#

An agent iterates: it calls tools, reads the results, and decides what to do next until the work is done. Tools you define run in your process. Coresource routes the model's call back to your handler, so the agent can reach your database or internal API without you handing over any credentials.

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

const invoices = new Map([
  ['INV-123', { vendor: 'Acme GmbH', amountEur: 82_400, status: 'unpaid' }]
]);

const analyst = createAgent({
  type: 'agent',
  systemPrompt: 'Use the available business data. Do not guess invoice values.',
  model: 'deepseek/deepseek-v4-flash',
  tools: [
    {
      name: 'lookup_invoice',
      description: 'Look up an invoice by its ID.',
      inputSchema: {
        type: 'object',
        properties: { id: { type: 'string', description: 'Invoice ID, e.g. INV-123' } },
        required: ['id'],
        additionalProperties: false
      },
      handler: (input) => {
        const invoice = invoices.get(String(input.id));
        return invoice ?? { error: 'not_found' };
      }
    }
  ],
  outputs: ['summary.md']
});

try {
  const result = await analyst.run('Summarise invoice INV-123 and flag it if it exceeds EUR 50,000.');
  console.log(result.text);

  for (const artifact of result.artifacts) {
    console.log(`--- ${artifact.name} ---`);
    console.log(await artifact.text());
  }
} finally {
  await analyst.close();
}

inputSchema is ordinary JSON Schema and it is what the model sees, so the description fields are part of your prompt engineering, not decoration. outputs: ['summary.md'] declares a file the agent should produce; it comes back in result.artifacts with text() and download() on it.

Step 4: Watch a run as it happens#

run() waits for the final result, which is fine for a script and wrong for anything a person is watching. start() returns as soon as the run exists, and events() streams what it is doing.

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

const agent = createAgent({ type: 'agent', systemPrompt: 'Be methodical.' });

const run = await agent.start('Audit the pricing page for inconsistencies.');
console.log(`run ${run.id} started`);

for await (const event of run.events()) {
  console.log(event.type, event.data);
}

const result = await run.result();
console.log(result.status, result.text);

await agent.close();

The event stream and the run are separate. If the stream drops on a quiet stretch, the run keeps going. Reconnect through the handle, or retrieve the run by id through the HTTP API. Runs using only hosted capabilities can continue after your process exits. A run that needs one of your custom tools must keep the SDK process serving that tool connected when the tool is called.

What you built#

StepTypeKey idea
2one-shotOne response, no tools, no runtime
3agentTools run in your process; artifacts come back
4agentstart() + events() for visible progress