Resources & Kinds

Load, save, and validate data by kind. Kinds are namespaced types (e.g. core.meeting.transcript) that the platform resolves to storage via resource policies and connectors.

Why resources and kinds?

Why it exists: So agents never need ad-hoc database access or connection strings. The platform owns storage resolution: you declare kinds and resource policies + connectors decide where data lives. That keeps data portable, auditable, and consistent across cloud and self-hosted.

How it helps: If you use resources and kinds, you get schema validation, policy-driven storage, and a single vocabulary for agents and connectors. If you bypass it and "just query my database," you take on connection management, schema drift, and no automatic resolution across deployments — and you lose the ability to swap storage (e.g. vault vs SaaS) without changing agent code.

What are kinds?

A kind is a dot-separated type name:core.email.messageagent.socratic.interview.sessionAgents declare which kinds they consume and produce in the manifest; the platform uses resource policies and connectors to decide where data lives. You never pick a database — you pick a kind.

ctx.resources

The execution context exposes ctx.resources, which wraps load_resources and save_resource.

  • load(kind, filter) — List resources by kind with optional filters (owner_did, connector_id, limit, cursor). Returns a paginated list.
  • save(kind, body) — Create or update a resource. Body includes data, owner_did, and optional uri, schema_id, metadata. Policy resolution decides which connector and store handle the write.
typescript
import { handler } from '@human/agent-sdk';
export default handler({
name: 'meeting-notes',
version: '1.0.0',
resources: {
consumes: [{ kind: 'core.meeting.transcript' }],
produces: [{ kind: 'core.meeting.notes' }],
},
execute: async (ctx, input) => {
// Load resources by kind; filter by owner_did, connector_id, etc.
const transcripts = await ctx.resources.load('core.meeting.transcript', {
owner_did: ctx.delegation.passportId,
limit: 10,
});
const notes = transcripts.data.map(t => summarize(t));
await ctx.resources.save('core.meeting.notes', {
data: { meeting_id: input.meetingId, notes },
owner_did: ctx.delegation.passportId,
});
return { count: notes.length };
},
});

Schema helpers

Use get_schema, get_effective_schema, and resolveAndValidate to fetch schema definitions and validate payloads before saving.

typescript
import { get_schema, get_effective_schema, resolveAndValidate } from '@human/agent-sdk';
// In your agent or a helper:
const schema = await get_schema('core.email.message.v1', ctx);
// Or resolve effective schema for an org + kind (respects org_schema_prefs):
const effective = await get_effective_schema(ctx.orgId, 'core.email.message', ctx);
// Validate payload before save:
const { valid, errors } = await resolveAndValidate('core.email.message', payload, ctx);
if (!valid) {
ctx.log.warn('Validation failed', { errors });
return { error: 'Invalid payload', errors };
}

Standalone helpers

When you have ctx, you can call load_resources and save_resource directly; behavior is the same.

typescript
// Standalone helpers (same behavior as ctx.resources when you have ctx):
import { load_resources, save_resource } from '@human/agent-sdk';
const list = await load_resources('core.meeting.transcript', { limit: 5 }, ctx);
const created = await save_resource('core.meeting.notes', { data: { ... }, owner_did }, ctx);

See also