Overview
Listed attributed artifacts retain their observation time, allowing hosts to build privacy-safe time cohorts without reaching around the store contract.
@absolutejs/outcomesv0.2.4betaAIOutcome feedback loop for AI agents: typed artifact features, attribution-joined stats, and rendered evidence that makes agent context better per user.
An outcome feedback loop that makes an AI agent measurably better per user without training anything. Agents produce artifacts (outreach emails, generated pages, drafts) and things happen to them (opens, replies, conversions); recording both sides with typed features frozen at production time lets the agent’s context improve every week, with receipts you can show users. You define artifact kinds and outcome events once, and the package derives the ledger contract, attribution-joined stats, and an evidence block your own AI call distills into a per-user memo.
bun add @absolutejs/outcomesListed attributed artifacts retain their observation time, allowing hosts to build privacy-safe time cohorts without reaching around the store contract.
The outcome feedback loop that makes an AI agent measurably better per user — without training anything.
Used by the hosted AbsoluteJS.ai platform and available as a standalone outcomes package.
An agent produces artifacts (outreach emails, generated pages, drafts). Things happen to them (opens, replies, conversions, meetings). If you record both sides with typed features frozen at production time, the agent's context can get smarter every week — and you can show users the receipts.
You define the vocabulary once — artifact kinds with typed, bucketable features, and an ordered list of outcome events — and the package derives:
The ledger contract (OutcomeStore): record artifacts + outcomes,
attribution is the join on your artifact id.
The stats (computeOutcomeStats): each outcome's rate overall, per
feature bucket, and per experiment variant when present (the A/B bolt-on). Below your minSample it reports not-ready, so hosts stay quiet instead of showing confident noise — the cold-start contract.
The evidence (renderEvidence): a compact text block your OWN AI call
distills into a "what works for you" memo that conditions future generations. The package never calls a model itself.
Same machinery for an outreach copilot (features: subject length, tone; outcomes: replies) and an AI website builder (features: hero copy length, layout; outcomes: conversions from your analytics beacon).
The optional @absolutejs/outcomes/drizzle entry exports a typed Postgres schema and store. It works with any Drizzle PgAsyncDatabase, including a Neon-backed database, and never creates schema at application runtime:
Include outcomesDrizzleSchema in your Drizzle migration schema. The same entry exports OutcomeArtifactInsertSchema, OutcomeArtifactSelectSchema, OutcomeEventInsertSchema, and OutcomeEventSelectSchema, generated with Drizzle-TypeBox directly from those tables so route contracts never restate database shapes.
Outcome writes require ownerId; both memory and Drizzle stores no-op unless the artifact belongs to that owner. The contract-2 AI tools expose the same owner/resource binding for host policy enforcement.
defineOutcomeVocabulary declares artifact kinds with typed, bucketable features (number buckets, closed string sets, booleans) and an ordered list of outcome events.
The OutcomeStore contract records artifacts and outcomes; attribution is simply the join on your artifact id, with createMemoryOutcomeStore included for tests.
computeOutcomeStats reports each outcome’s rate overall, per feature bucket, and per experiment variant when present, so A/B experiments bolt on without extra machinery.
Below your minSample threshold stats report not-ready, so hosts stay quiet instead of showing confident noise; the cold-start contract is explicit.
renderEvidence produces a compact text block your own AI call distills into a "what works for you" memo that conditions future generations; the package never calls a model itself.
Outcomes turn production results into evidence without allowing mutable features to rewrite history.
Outcomes
Listed attributed artifacts retain their observation time, allowing hosts to build privacy-safe time cohorts without reaching around the store contract.
An agent produces artifacts (outreach emails, generated pages, drafts). Things happen to them (opens, replies, conversions, meetings). If you record both sides with typed features frozen at production time, the agent's context can get smarter every week — and you can show users the receipts.
The optional @absolutejs/outcomes/drizzle entry exports a typed Postgres schema and store. It works with any Drizzle PgAsyncDatabase, including a Neon-backed database, and never creates schema at application runtime:
Hardening checklist
Follow in order
The optional @absolutejs/outcomes/drizzle entry exports a typed Postgres schema and store. It works with any Drizzle PgAsyncDatabase, including a Neon-backed database, and never creates schema at application runtime:
import {
createDrizzleOutcomeStore,
outcomesDrizzleSchema
} from '@absolutejs/outcomes/drizzle';
const store = createDrizzleOutcomeStore({ db });An agent produces artifacts (outreach emails, generated pages, drafts). Things happen to them (opens, replies, conversions, meetings). If you record both sides with typed features frozen at production time, the agent's context can get smarter every week — and you can show users the receipts.
import {
computeOutcomeStats,
defineOutcomeVocabulary,
renderEvidence
} from '@absolutejs/outcomes';
const vocabulary = defineOutcomeVocabulary({
artifacts: {
outreach_email: {
label: 'Outreach email',
features: {
subjectWords: {
type: 'number',
buckets: [
{ label: 'short', max: 7 },
{ label: 'medium', max: 12 }
],
overflowLabel: 'long'
},
mode: { type: 'string', values: ['outreach', 'followup'] },
hasQuestion: { type: 'boolean' }
}
}
},
outcomes: ['opened', 'replied', 'meeting_scheduled']
});
// At production time: store.recordArtifact({ id: sendId, ownerId, kind, features })
// From your signal hooks: store.recordOutcome({ artifactId: sendId, outcome: "replied", ownerId })
const rows = await store.listArtifactsWithOutcomes(
ownerId,
'outreach_email',
since
);
const stats = computeOutcomeStats(vocabulary, 'outreach_email', rows, {
minSample: 10
});
if (stats.ready) {
const memo = await yourAiCall(
`Distill what works:\n${renderEvidence(stats)}`
);
// …feed `memo` into every future draft; show `stats` in your UI.
}Define the vocabulary, record artifacts and outcomes, then turn attribution-joined stats into evidence your own AI call distills.
import {
computeOutcomeStats,
defineOutcomeVocabulary,
renderEvidence
} from '@absolutejs/outcomes';
const vocabulary = defineOutcomeVocabulary({
artifacts: {
outreach_email: {
label: 'Outreach email',
features: {
subjectWords: {
type: 'number',
buckets: [
{ label: 'short', max: 7 },
{ label: 'medium', max: 12 }
],
overflowLabel: 'long'
},
mode: { type: 'string', values: ['outreach', 'followup'] },
hasQuestion: { type: 'boolean' }
}
}
},
outcomes: ['opened', 'replied', 'meeting_scheduled']
});
// At production time:
// store.recordArtifact({ id: sendId, ownerId, kind, features })
// From your signal hooks:
// store.recordOutcome({ artifactId: sendId, outcome: 'replied' })
const rows = await store.listArtifactsWithOutcomes(
ownerId,
'outreach_email',
since
);
const stats = computeOutcomeStats(vocabulary, 'outreach_email', rows, {
minSample: 10
});
if (stats.ready) {
const memo = await yourAiCall(
`Distill what works:\n${renderEvidence(stats)}`
);
// ...feed memo into every future draft; show stats in your UI.
}Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.
Current package surface
Import surface · click to copy