Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/api-ai.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"llmsFull": "https://mail.significanthobbies.com/llms-full.txt",
"sitemap": "https://mail.significanthobbies.com/sitemap.xml",
"robots": "https://mail.significanthobbies.com/robots.txt",
"openapi": "https://mail.significanthobbies.com/openapi.json",
"markdown": {
"suffix": ".md",
"negotiation": true
Expand Down
8 changes: 8 additions & 0 deletions public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@
## Machine surfaces

- [Agent catalog](https://mail.significanthobbies.com/api/ai): JSON inventory of public surfaces
- [OpenAPI spec](https://mail.significanthobbies.com/openapi.json): Machine-readable API description
- [Homepage markdown](https://mail.significanthobbies.com/index.md): Product brief without JS
- [This index](https://mail.significanthobbies.com/llms.txt)

## When to use this

- Searching a Gmail inbox locally with semantic similarity without server-side storage
- Browsing and filtering emails with client-side ML embeddings (privacy-first)
- Understanding the privacy architecture of a local-first email workspace
- Checking FAQ about semantic search, subscriptions, and sender analytics

## Optional

- [Foundry](https://sassmaker.com): Parent fleet showcase
208 changes: 178 additions & 30 deletions src/agent-edge.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const AGENT_SURFACE = {
"alternateName": "Email Manager",
"url": "https://mail.significanthobbies.com",
"llmsFullTxt": "# Kinetic — full agent brief\n\nGmail workspace with local semantic search — private email tooling.\nFormerly \"Email Manager\" (retired alias).\n\n## Index\n\n# Kinetic\n\nGmail workspace with local semantic search.\n\n## Privacy\n\nMailbox content is private. Agents should only use public product description surfaces.\n\n## Agent entrypoints\n\n- https://mail.significanthobbies.com/llms.txt\n- https://mail.significanthobbies.com/api/ai\n- https://mail.significanthobbies.com/index.md\n\n## Product links\n\n- Home: https://mail.significanthobbies.com/ — App shell\n\n## Machine surfaces\n\n- https://mail.significanthobbies.com/llms.txt\n- https://mail.significanthobbies.com/llms-full.txt\n- https://mail.significanthobbies.com/api/ai\n- https://mail.significanthobbies.com/index.md\n- https://mail.significanthobbies.com/sitemap.xml\n- https://mail.significanthobbies.com/robots.txt\n\n## Contact / fleet\n\n- Fleet: https://sassmaker.com\n- Agent email for directory verification: sarthakagrawal@agentmail.to\n",
"llmsTxt": "# Kinetic\n\n> Gmail workspace with local semantic search — private email tooling.\n> Formerly \"Email Manager\" (retired alias).\n\n## Product\n\n- [Home](https://mail.significanthobbies.com/): App shell\n\n## Machine surfaces\n\n- [Agent catalog](https://mail.significanthobbies.com/api/ai): JSON inventory of public surfaces\n- [Homepage markdown](https://mail.significanthobbies.com/index.md): Product brief without JS\n- [This index](https://mail.significanthobbies.com/llms.txt)\n\n## Optional\n\n- [Foundry](https://sassmaker.com): Parent fleet showcase\n",
"llmsTxt": "# Kinetic\n\n> Gmail workspace with local semantic search — private email tooling.\n> Formerly \"Email Manager\" (retired alias).\n\n## Product\n\n- [Home](https://mail.significanthobbies.com/): App shell\n\n## Machine surfaces\n\n- [Agent catalog](https://mail.significanthobbies.com/api/ai): JSON inventory of public surfaces\n- [OpenAPI spec](https://mail.significanthobbies.com/openapi.json): Machine-readable API description\n- [Homepage markdown](https://mail.significanthobbies.com/index.md): Product brief without JS\n- [This index](https://mail.significanthobbies.com/llms.txt)\n\n## When to use this\n\n- Searching a Gmail inbox locally with semantic similarity without server-side storage\n- Browsing and filtering emails with client-side ML embeddings (privacy-first)\n- Understanding the privacy architecture of a local-first email workspace\n- Checking FAQ about semantic search, subscriptions, and sender analytics\n\n## Optional\n\n- [Foundry](https://sassmaker.com): Parent fleet showcase\n",
"indexMd": "# Kinetic\n\nGmail workspace with local semantic search.\n\n## Privacy\n\nMailbox content is private. Agents should only use public product description surfaces.\n\n## Agent entrypoints\n\n- https://mail.significanthobbies.com/llms.txt\n- https://mail.significanthobbies.com/api/ai\n- https://mail.significanthobbies.com/index.md\n",
"catalog": {
"name": "Kinetic",
Expand All @@ -26,6 +26,7 @@ export const AGENT_SURFACE = {
"llmsFull": "https://mail.significanthobbies.com/llms-full.txt",
"sitemap": "https://mail.significanthobbies.com/sitemap.xml",
"robots": "https://mail.significanthobbies.com/robots.txt",
"openapi": "https://mail.significanthobbies.com/openapi.json",
"markdown": {
"suffix": ".md",
"negotiation": true
Expand Down Expand Up @@ -60,6 +61,162 @@ export const AGENT_SURFACE = {
}
};

const OPENAPI_SPEC = {
openapi: '3.1.0',
info: {
title: 'Kinetic public API',
version: '1.0.0',
description:
'Gmail workspace with local semantic search. The public web API exposes read-only agent surfaces: the agent catalog, llms.txt, sitemap, and markdown alternates. Mailbox content is private.',
contact: { name: 'Kinetic', url: 'https://mail.significanthobbies.com' },
},
servers: [{ url: 'https://mail.significanthobbies.com' }],
tags: [{ name: 'agent-surfaces', description: 'Machine-readable public surfaces' }],
paths: {
'/api/ai': {
get: {
operationId: 'getAgentCatalog',
tags: ['agent-surfaces'],
summary: 'Agent catalog',
description: 'JSON inventory of public agent surfaces.',
responses: {
200: {
description: 'Agent catalog',
content: {
'application/json': { schema: { $ref: '#/components/schemas/AgentCatalog' } },
},
},
},
},
},
'/llms.txt': {
get: {
operationId: 'getLlmsTxt',
tags: ['agent-surfaces'],
summary: 'llms.txt index',
responses: { 200: { description: 'Markdown index', content: { 'text/plain': {} } } },
},
},
'/llms-full.txt': {
get: {
operationId: 'getLlmsFullTxt',
tags: ['agent-surfaces'],
summary: 'Full agent brief',
responses: { 200: { description: 'Markdown brief', content: { 'text/plain': {} } } },
},
},
'/sitemap.xml': {
get: {
operationId: 'getSitemap',
tags: ['agent-surfaces'],
summary: 'Sitemap',
responses: { 200: { description: 'XML sitemap', content: { 'application/xml': {} } } },
},
},
'/openapi.json': {
get: {
operationId: 'getOpenApiSpec',
tags: ['agent-surfaces'],
summary: 'OpenAPI specification',
description: 'This document.',
responses: {
200: { description: 'OpenAPI 3.1 spec', content: { 'application/json': {} } },
},
},
},
},
components: {
schemas: {
AgentCatalog: {
type: 'object',
properties: {
name: { type: 'string' },
version: { type: 'string' },
url: { type: 'string', format: 'uri' },
llms: { type: 'string', format: 'uri' },
llmsFull: { type: 'string', format: 'uri' },
sitemap: { type: 'string', format: 'uri' },
robots: { type: 'string', format: 'uri' },
openapi: { type: 'string', format: 'uri' },
markdown: {
type: 'object',
properties: { suffix: { type: 'string' }, negotiation: { type: 'boolean' } },
},
},
},
},
},
};

function jsonError(status, code, message, path) {
return new Response(JSON.stringify({ error: { code, message, path } }), {
status,
headers: {
'content-type': 'application/json; charset=utf-8',
'cache-control': 'no-store',
'access-control-allow-origin': '*',
},
});
}

function markdown404(pathname, origin) {
const body = `# 404 — Not Found

\`${pathname}\` does not exist on ${origin}.

## Where to look next

- [Home](${origin}/)
- [Sitemap](${origin}/sitemap.xml)
- [Agent index](${origin}/llms.txt)
- [Full agent brief](${origin}/llms-full.txt)
- [Agent catalog (JSON)](${origin}/api/ai)
`;
return new Response(body, {
status: 404,
headers: {
'content-type': 'text/markdown; charset=utf-8',
'cache-control': 'no-store',
'x-content-type-options': 'nosniff',
},
});
}

const TEXT_ROUTES = {
'/llms.txt': () => text(AGENT_SURFACE.llmsTxt, 'text/plain; charset=utf-8'),
'/index.md': () => text(AGENT_SURFACE.indexMd, 'text/markdown; charset=utf-8'),
};

function buildCatalog(url) {
const rebind = (v) => (v ? String(v).replace(AGENT_SURFACE.url, url.origin) : v);
return {
...AGENT_SURFACE.catalog,
url: url.origin,
llms: `${url.origin}/llms.txt`,
llmsFull: `${url.origin}/llms-full.txt`,
sitemap: AGENT_SURFACE.catalog.sitemap
? rebind(AGENT_SURFACE.catalog.sitemap)
: `${url.origin}/sitemap.xml`,
openapi: `${url.origin}/openapi.json`,
surfaces: (AGENT_SURFACE.catalog.surfaces || []).map((s) => ({
...s,
url: rebind(s.url),
md: rebind(s.md),
})),
};
}

function openApiResponse() {
return new Response(JSON.stringify(OPENAPI_SPEC, null, 2), {
status: 200,
headers: {
'content-type': 'application/json; charset=utf-8',
'access-control-allow-origin': '*',
'cache-control': 'public, max-age=3600',
},
});
}

/**
* @param {Request} request
* @returns {Response | null}
Expand All @@ -69,40 +226,31 @@ export function handleAgentEdge(request) {
const url = new URL(request.url);
const path = url.pathname === '' ? '/' : url.pathname;

if (path === '/llms.txt') {
return text(AGENT_SURFACE.llmsTxt, 'text/plain; charset=utf-8');
}
if (path === '/openapi.json' || path === '/openapi.yaml') return openApiResponse();

const textRoute = TEXT_ROUTES[path];
if (textRoute) return textRoute();

if (path === '/llms-full.txt' && AGENT_SURFACE.llmsFullTxt) {
return text(AGENT_SURFACE.llmsFullTxt, 'text/plain; charset=utf-8');
}
if (path === '/index.md') {
return text(AGENT_SURFACE.indexMd, 'text/markdown; charset=utf-8');
}
if (path === '/api/ai') {
// Re-bind origin so preview/custom domains stay correct
const catalog = {
...AGENT_SURFACE.catalog,
url: url.origin,
llms: `${url.origin}/llms.txt`,
llmsFull: `${url.origin}/llms-full.txt`,
sitemap: AGENT_SURFACE.catalog.sitemap
? String(AGENT_SURFACE.catalog.sitemap).replace(AGENT_SURFACE.url, url.origin)
: `${url.origin}/sitemap.xml`,
surfaces: (AGENT_SURFACE.catalog.surfaces || []).map((s) => ({
...s,
url: s.url ? String(s.url).replace(AGENT_SURFACE.url, url.origin) : s.url,
md: s.md ? String(s.md).replace(AGENT_SURFACE.url, url.origin) : s.md,
})),
};
return json(catalog);

if (path === '/api/ai') return json(buildCatalog(url));

if (path.startsWith('/api/')) {
return jsonError(404, 'not_found', `Unknown API path: ${path}`, path);
}

// Homepage markdown negotiation
if ((path === '/' || path === '') && wantsMarkdown(request)) {
return text(AGENT_SURFACE.indexMd, 'text/markdown; charset=utf-8', {
Link: '</index.md>; rel="alternate"; type="text/markdown"',
Vary: 'Accept',
});
if (wantsMarkdown(request)) {
if (path === '/' || path === '') {
return text(AGENT_SURFACE.indexMd, 'text/markdown; charset=utf-8', {
Link: '</index.md>; rel="alternate"; type="text/markdown"',
Vary: 'Accept',
});
}
if (!path.includes('.')) {
return markdown404(path, url.origin);
}
}

return null;
Expand Down