ailo.life 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # ailo.life
2
+
3
+ Add verifiable AI outputs to your agent. Every call gets a tamper-proof verify code; recipients can verify at ailo.life/verify.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install ailo.life
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ```ts
14
+ // .env
15
+ AILO_VERIFY_API_KEY=sk_xxx
16
+
17
+ // agent.ts
18
+ import { Verify } from 'ailo.life';
19
+
20
+ const verify = Verify.init();
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ### Single-turn
26
+
27
+ ```ts
28
+ export async function handleQuery(query: string) {
29
+ const { result, verifyCode } = await verify.trace('support', async (trace) => {
30
+ const response = await openai.chat.completions.create({ ... });
31
+ trace.llmCall({ model: 'gpt-4o', promptTokens: 150, completionTokens: 80 });
32
+ trace.toolCall('send_email', { to: 'user@example.com' });
33
+ return response.choices[0].message.content;
34
+ });
35
+
36
+ return result + '\n\n' + verify.embed(verifyCode);
37
+ }
38
+ ```
39
+
40
+ ### Multi-turn (conversationId)
41
+
42
+ ```ts
43
+ export async function handleTurn(conversationId: string, message: string) {
44
+ const { result, verifyCode } = await verify.trace('support', { conversationId }, async (trace) => {
45
+ // ... LLM + tool calls
46
+ return response;
47
+ });
48
+ return result + '\n\n' + verify.embed(verifyCode);
49
+ }
50
+ ```
51
+
52
+ ### With customerId (optional)
53
+
54
+ ```ts
55
+ const { result, verifyCode, sessionHash } = await verify.trace('support', {
56
+ conversationId: 'conv_abc',
57
+ customerId: 'cust_123',
58
+ }, async (trace) => { ... });
59
+ ```
60
+
61
+ ## Verification
62
+
63
+ - **Each call**: Has its own `verifyCode` (e.g. `100X2K9MPN`). Verify at ailo.life/verify?code=...
64
+ - **Privacy**: Verify page shows only the verified event (not the full chain). Chain integrity is attested server-side. `sessionHash` for programmatic checks.
65
+
66
+ ## Testing & Publishing
67
+
68
+ ### Testing locally
69
+
70
+ 1. **Dry-run mode** — Use `Verify.init({ dryRun: true })` to skip API calls in tests:
71
+ ```ts
72
+ const verify = Verify.init({ dryRun: true });
73
+ const { result, verifyCode } = await verify.trace('test', async (trace) => {
74
+ trace.llmCall({ model: 'gpt-4o', promptTokens: 10, completionTokens: 5 });
75
+ return 'ok';
76
+ });
77
+ // verifyCode will be mock value like "100DRY0000"
78
+ ```
79
+
80
+ 2. **Link locally** — From the monorepo root: `cd packages/verify && npm link`. In your test app: `npm link ailo.life`.
81
+
82
+ 3. **Run build** — `cd packages/verify && npm run build` (runs `tsc`).
83
+
84
+ ### Publishing to npm
85
+
86
+ 1. **Login**: `npm login` (create account at npmjs.com if needed).
87
+ 2. **Version**: Bump in `package.json` or run `npm version patch|minor|major`.
88
+ 3. **Publish**: `cd packages/verify && npm publish`. For scoped packages, first publish: `npm publish --access public`.
89
+ 4. **Verify**: `npm view ailo.life` to confirm the package is live.
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Ailo Verify SDK — Add verifiable AI outputs to your agent.
3
+ *
4
+ * Wrap your agent with trace() to mint verify codes. Embed codes in output
5
+ * so recipients can verify at ailo.life/verify.
6
+ */
7
+ export interface TraceEvent {
8
+ type: string;
9
+ content: string;
10
+ metadata?: Record<string, unknown>;
11
+ }
12
+ export interface TraceOptions {
13
+ conversationId?: string;
14
+ customerId?: string;
15
+ }
16
+ export interface EmbedOptions {
17
+ /** Base URL for the verify link (default: SDK baseUrl) */
18
+ baseUrl?: string;
19
+ /** Output format: 'plain' | 'markdown' | 'html' */
20
+ format?: "plain" | "markdown" | "html";
21
+ /** Custom label text (default: "Verify") */
22
+ text?: string;
23
+ }
24
+ export interface InitOptions {
25
+ apiKey?: string;
26
+ baseUrl?: string;
27
+ /** When true, skip API calls and return mock codes. Use in tests. */
28
+ dryRun?: boolean;
29
+ }
30
+ export interface IngestResponse {
31
+ verifyCodes: string[];
32
+ lastVerifyCode: string | null;
33
+ runVerifyCode?: string;
34
+ sessionHash: string | null;
35
+ }
36
+ export interface TraceResult<T> {
37
+ result: T;
38
+ verifyCodes: string[];
39
+ verifyCode: string | null;
40
+ runVerifyCode: string | null;
41
+ sessionHash: string | null;
42
+ }
43
+ /** Known error codes for programmatic handling */
44
+ export declare const AiloVerifyErrorCode: {
45
+ readonly INVALID_API_KEY: "AILO_VERIFY_INVALID_API_KEY";
46
+ readonly REVOKED_API_KEY: "AILO_VERIFY_REVOKED_API_KEY";
47
+ readonly RATE_LIMITED: "AILO_VERIFY_RATE_LIMITED";
48
+ readonly NETWORK_ERROR: "AILO_VERIFY_NETWORK_ERROR";
49
+ readonly SERVER_ERROR: "AILO_VERIFY_SERVER_ERROR";
50
+ };
51
+ export declare class AiloVerifyError extends Error {
52
+ readonly code: string;
53
+ readonly status?: number | undefined;
54
+ constructor(message: string, code: string, status?: number | undefined);
55
+ }
56
+ export declare class Verify {
57
+ private apiKey;
58
+ private baseUrl;
59
+ private dryRun;
60
+ private constructor();
61
+ static init(options?: InitOptions): Verify;
62
+ /**
63
+ * Wrap your agent function. Buffers events and POSTs to Ailo at the end.
64
+ * Returns { result, verifyCodes, verifyCode, runVerifyCode, sessionHash }.
65
+ */
66
+ trace<T>(_agentName: string, callbackOrOptions: TraceOptions | ((trace: Trace) => Promise<T>), callback?: (trace: Trace) => Promise<T>): Promise<TraceResult<T>>;
67
+ private postWithRetry;
68
+ private delay;
69
+ /**
70
+ * Return footer string for embedding in output.
71
+ * @param verifyCode - Use lastVerifyCode, runVerifyCode, or a specific event's code
72
+ * @param options - baseUrl, format ('plain' | 'markdown' | 'html'), text
73
+ */
74
+ embed(verifyCode: string | null, options?: EmbedOptions | string): string;
75
+ }
76
+ export interface Trace {
77
+ llmCall: (meta: {
78
+ model?: string;
79
+ promptTokens?: number;
80
+ completionTokens?: number;
81
+ }) => void;
82
+ toolCall: (name: string, meta?: Record<string, unknown>) => void;
83
+ agentResponse: (content: string | object) => void;
84
+ event: (type: string, content: string, meta?: Record<string, unknown>) => void;
85
+ }
package/dist/index.js ADDED
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ /**
3
+ * Ailo Verify SDK — Add verifiable AI outputs to your agent.
4
+ *
5
+ * Wrap your agent with trace() to mint verify codes. Embed codes in output
6
+ * so recipients can verify at ailo.life/verify.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.Verify = exports.AiloVerifyError = exports.AiloVerifyErrorCode = void 0;
10
+ const DEFAULT_BASE_URL = "https://ailo.life";
11
+ /** Known error codes for programmatic handling */
12
+ exports.AiloVerifyErrorCode = {
13
+ INVALID_API_KEY: "AILO_VERIFY_INVALID_API_KEY",
14
+ REVOKED_API_KEY: "AILO_VERIFY_REVOKED_API_KEY",
15
+ RATE_LIMITED: "AILO_VERIFY_RATE_LIMITED",
16
+ NETWORK_ERROR: "AILO_VERIFY_NETWORK_ERROR",
17
+ SERVER_ERROR: "AILO_VERIFY_SERVER_ERROR",
18
+ };
19
+ class AiloVerifyError extends Error {
20
+ constructor(message, code, status) {
21
+ super(message);
22
+ this.code = code;
23
+ this.status = status;
24
+ this.name = "AiloVerifyError";
25
+ }
26
+ }
27
+ exports.AiloVerifyError = AiloVerifyError;
28
+ class Verify {
29
+ constructor(apiKey, baseUrl, dryRun) {
30
+ this.apiKey = apiKey;
31
+ this.baseUrl = baseUrl;
32
+ this.dryRun = dryRun;
33
+ }
34
+ static init(options) {
35
+ const env = typeof globalThis.process !== "undefined"
36
+ ? globalThis.process.env
37
+ : undefined;
38
+ const apiKey = options?.apiKey ?? env?.AILO_VERIFY_API_KEY ?? "";
39
+ const dryRun = options?.dryRun ?? false;
40
+ if (!dryRun && !apiKey) {
41
+ throw new Error("AILO_VERIFY_API_KEY is required. Set it in env or pass to init().");
42
+ }
43
+ return new Verify(apiKey || "dry-run", options?.baseUrl ?? DEFAULT_BASE_URL, dryRun);
44
+ }
45
+ /**
46
+ * Wrap your agent function. Buffers events and POSTs to Ailo at the end.
47
+ * Returns { result, verifyCodes, verifyCode, runVerifyCode, sessionHash }.
48
+ */
49
+ async trace(_agentName, callbackOrOptions, callback) {
50
+ const opts = typeof callbackOrOptions === "function" ? {} : callbackOrOptions ?? {};
51
+ const fn = typeof callbackOrOptions === "function" ? callbackOrOptions : callback;
52
+ if (!fn)
53
+ throw new Error("trace() requires a callback function");
54
+ const sessionId = (typeof crypto !== "undefined" && crypto.randomUUID?.()) ??
55
+ `sess_${Date.now()}_${Math.random().toString(36).slice(2)}`;
56
+ const events = [];
57
+ const trace = {
58
+ llmCall: (meta) => {
59
+ events.push({
60
+ type: "llm_call",
61
+ content: meta.model ?? "unknown",
62
+ metadata: meta,
63
+ });
64
+ },
65
+ toolCall: (name, meta) => {
66
+ events.push({
67
+ type: "tool_call",
68
+ content: name,
69
+ metadata: meta,
70
+ });
71
+ },
72
+ agentResponse: (content) => {
73
+ events.push({
74
+ type: "agent_response",
75
+ content: typeof content === "string" ? content : JSON.stringify(content),
76
+ });
77
+ },
78
+ event: (type, content, meta) => {
79
+ events.push({ type, content, metadata: meta });
80
+ },
81
+ };
82
+ const result = await fn(trace);
83
+ let verifyCodes = [];
84
+ let verifyCode = null;
85
+ let runVerifyCode = null;
86
+ let sessionHash = null;
87
+ if (events.length > 0) {
88
+ if (this.dryRun) {
89
+ verifyCodes = events.map((_, i) => `100DRY${String(i).padStart(4, "0")}`);
90
+ verifyCode = verifyCodes[verifyCodes.length - 1] ?? null;
91
+ runVerifyCode = `100DRYRUN0`;
92
+ sessionHash = "dry-run-session-hash";
93
+ }
94
+ else {
95
+ const body = {
96
+ sessionId,
97
+ events,
98
+ };
99
+ if (opts.conversationId)
100
+ body.conversationId = opts.conversationId;
101
+ if (opts.customerId)
102
+ body.customerId = opts.customerId;
103
+ const data = await this.postWithRetry(body);
104
+ verifyCodes = data.verifyCodes ?? [];
105
+ verifyCode = data.lastVerifyCode ?? null;
106
+ runVerifyCode = data.runVerifyCode ?? null;
107
+ sessionHash = data.sessionHash ?? null;
108
+ }
109
+ }
110
+ return {
111
+ result,
112
+ verifyCodes,
113
+ verifyCode,
114
+ runVerifyCode,
115
+ sessionHash,
116
+ };
117
+ }
118
+ async postWithRetry(body, maxRetries = 2) {
119
+ let lastError = null;
120
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
121
+ try {
122
+ const res = await fetch(`${this.baseUrl}/api/verify/sdk/events`, {
123
+ method: "POST",
124
+ headers: {
125
+ "Content-Type": "application/json",
126
+ Authorization: `Bearer ${this.apiKey}`,
127
+ },
128
+ body: JSON.stringify(body),
129
+ });
130
+ const data = (await res.json().catch(() => ({})));
131
+ if (!res.ok) {
132
+ const msg = data.error ?? res.statusText;
133
+ if (res.status === 401) {
134
+ if (msg.toLowerCase().includes("revoked")) {
135
+ throw new AiloVerifyError(msg, exports.AiloVerifyErrorCode.REVOKED_API_KEY, res.status);
136
+ }
137
+ throw new AiloVerifyError(msg, exports.AiloVerifyErrorCode.INVALID_API_KEY, res.status);
138
+ }
139
+ if (res.status === 429) {
140
+ throw new AiloVerifyError(msg, exports.AiloVerifyErrorCode.RATE_LIMITED, res.status);
141
+ }
142
+ if (res.status >= 500 && attempt < maxRetries) {
143
+ lastError = new AiloVerifyError(msg, exports.AiloVerifyErrorCode.SERVER_ERROR, res.status);
144
+ await this.delay(500 * (attempt + 1));
145
+ continue;
146
+ }
147
+ throw new AiloVerifyError(msg, exports.AiloVerifyErrorCode.SERVER_ERROR, res.status);
148
+ }
149
+ return data;
150
+ }
151
+ catch (err) {
152
+ if (err instanceof AiloVerifyError)
153
+ throw err;
154
+ if (attempt < maxRetries) {
155
+ lastError = err instanceof Error ? err : new Error(String(err));
156
+ await this.delay(500 * (attempt + 1));
157
+ continue;
158
+ }
159
+ const msg = err instanceof Error ? err.message : String(err);
160
+ throw new AiloVerifyError(msg, exports.AiloVerifyErrorCode.NETWORK_ERROR);
161
+ }
162
+ }
163
+ throw lastError ?? new AiloVerifyError("Request failed", exports.AiloVerifyErrorCode.NETWORK_ERROR);
164
+ }
165
+ delay(ms) {
166
+ return new Promise((r) => setTimeout(r, ms));
167
+ }
168
+ /**
169
+ * Return footer string for embedding in output.
170
+ * @param verifyCode - Use lastVerifyCode, runVerifyCode, or a specific event's code
171
+ * @param options - baseUrl, format ('plain' | 'markdown' | 'html'), text
172
+ */
173
+ embed(verifyCode, options) {
174
+ if (!verifyCode)
175
+ return "";
176
+ const opts = typeof options === "string" ? { baseUrl: options } : options ?? {};
177
+ const url = opts.baseUrl ?? this.baseUrl;
178
+ const base = `${url}/api/verify?code=${encodeURIComponent(verifyCode)}`;
179
+ const text = opts.text ?? "Verify";
180
+ switch (opts.format) {
181
+ case "markdown":
182
+ return `[${text}](${base})`;
183
+ case "html":
184
+ return `<a href="${base}">${text}</a>`;
185
+ default:
186
+ return `${text}: ${base}`;
187
+ }
188
+ }
189
+ }
190
+ exports.Verify = Verify;
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "ailo.life",
3
+ "version": "1.0.0",
4
+ "description": "Ailo Verify SDK — add verifiable AI outputs to your agent",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": ["dist", "README.md"],
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "prepublishOnly": "npm run build"
11
+ },
12
+ "keywords": ["ailo", "verify", "ai", "agent", "compliance"],
13
+ "author": "Ailo",
14
+ "license": "MIT",
15
+ "devDependencies": {
16
+ "@types/node": "^20.0.0",
17
+ "typescript": "^5.0.0"
18
+ }
19
+ }