@provenonce/sdk 0.4.2 → 0.5.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/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -71,7 +71,7 @@ var BeatAgent = class {
|
|
|
71
71
|
async init() {
|
|
72
72
|
try {
|
|
73
73
|
this.log("Initializing Beat chain...");
|
|
74
|
-
const res = await this.api("POST", "/api/v1/
|
|
74
|
+
const res = await this.api("POST", "/api/v1/agent/init");
|
|
75
75
|
if (res.genesis) {
|
|
76
76
|
this.genesisHash = res.genesis.hash;
|
|
77
77
|
this.difficulty = res.difficulty || 1e3;
|
|
@@ -159,7 +159,7 @@ var BeatAgent = class {
|
|
|
159
159
|
const toBeat = this.latestBeat.index;
|
|
160
160
|
const fromHash = this.chain.find((b) => b.index === fromBeat)?.hash || this.genesisHash;
|
|
161
161
|
const toHash = this.latestBeat.hash;
|
|
162
|
-
const res = await this.api("POST", "/api/v1/
|
|
162
|
+
const res = await this.api("POST", "/api/v1/agent/checkin", {
|
|
163
163
|
proof: {
|
|
164
164
|
from_beat: fromBeat,
|
|
165
165
|
to_beat: toBeat,
|
|
@@ -237,7 +237,7 @@ var BeatAgent = class {
|
|
|
237
237
|
async resync() {
|
|
238
238
|
try {
|
|
239
239
|
this.log("Requesting re-sync challenge...");
|
|
240
|
-
const challenge = await this.api("POST", "/api/v1/
|
|
240
|
+
const challenge = await this.api("POST", "/api/v1/agent/resync", {
|
|
241
241
|
action: "challenge"
|
|
242
242
|
});
|
|
243
243
|
if (!challenge.challenge) {
|
|
@@ -254,7 +254,7 @@ var BeatAgent = class {
|
|
|
254
254
|
this.pulse(required);
|
|
255
255
|
const elapsed = Date.now() - t0;
|
|
256
256
|
this.log(`Re-sync beats computed in ${elapsed}ms`);
|
|
257
|
-
const proof = await this.api("POST", "/api/v1/
|
|
257
|
+
const proof = await this.api("POST", "/api/v1/agent/resync", {
|
|
258
258
|
action: "prove",
|
|
259
259
|
proof: {
|
|
260
260
|
from_beat: startBeat,
|
|
@@ -286,7 +286,7 @@ var BeatAgent = class {
|
|
|
286
286
|
*/
|
|
287
287
|
async requestSpawn(childName, childHash) {
|
|
288
288
|
try {
|
|
289
|
-
const res = await this.api("POST", "/api/v1/
|
|
289
|
+
const res = await this.api("POST", "/api/v1/agent/spawn", {
|
|
290
290
|
child_name: childName,
|
|
291
291
|
child_hash: childHash
|
|
292
292
|
});
|
|
@@ -342,7 +342,7 @@ var BeatAgent = class {
|
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
344
|
async refreshState() {
|
|
345
|
-
const res = await this.api("POST", "/api/v1/
|
|
345
|
+
const res = await this.api("POST", "/api/v1/agent/init");
|
|
346
346
|
if (res.already_initialized) {
|
|
347
347
|
this.totalBeats = res.total_beats;
|
|
348
348
|
this.genesisHash = res.genesis_hash;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/beat-sdk.ts"],"sourcesContent":["export { BeatAgent, computeBeat, computeBeatsLite } from './beat-sdk';\nexport type { BeatAgentConfig, Beat } from './beat-sdk';\n","/**\n * ═══════════════════════════════════════════════════════════\n * PROVENONCE BEAT SDK — Agent Heartbeat Client\n * ═══════════════════════════════════════════════════════════\n * \n * \"NIST tells you what time it is.\n * Provenonce tells the agent at what speed it is allowed to exist.\"\n * \n * Usage:\n * \n * import { BeatAgent } from './beat-sdk';\n * \n * const agent = new BeatAgent({\n * apiKey: 'pvn_...',\n * registryUrl: 'https://provenonce.vercel.app',\n * });\n * \n * await agent.init(); // Birth in Beat time\n * await agent.pulse(50); // Compute 50 beats\n * await agent.checkin(); // Report to registry\n * \n * // Or run the autonomous heartbeat:\n * agent.startHeartbeat(); // Computes + checks in continuously\n * // ... do your agent work ...\n * agent.stopHeartbeat();\n * \n * ═══════════════════════════════════════════════════════════\n */\n\nimport { createHash } from 'crypto';\n\n// ============ VDF ENGINE (LOCAL) ============\n\nexport interface Beat {\n index: number;\n hash: string;\n prev: string;\n timestamp: number;\n nonce?: string;\n}\n\nfunction computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string): Beat {\n const timestamp = Date.now();\n \n let current = createHash('sha256')\n .update(`${prevHash}:${beatIndex}:${nonce || ''}`)\n .digest('hex');\n\n for (let i = 0; i < difficulty; i++) {\n current = createHash('sha256')\n .update(current)\n .digest('hex');\n }\n\n return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce };\n}\n\n// ============ SDK CONFIG ============\n\nexport interface BeatAgentConfig {\n /** API key from registration (pvn_...) */\n apiKey: string;\n \n /** Provenonce registry URL */\n registryUrl: string;\n \n /** Beats to compute per pulse (default: 10) */\n beatsPerPulse?: number;\n \n /** Seconds between automatic check-ins (default: 300 = 5min) */\n checkinIntervalSec?: number;\n \n /** Callback when heartbeat ticks */\n onPulse?: (beats: Beat[], totalBeats: number) => void;\n \n /** Callback when check-in completes */\n onCheckin?: (result: any) => void;\n \n /** Callback on error */\n onError?: (error: Error, context: string) => void;\n \n /** Callback when status changes */\n onStatusChange?: (status: string, details: any) => void;\n \n /** Enable verbose logging */\n verbose?: boolean;\n}\n\n// ============ BEAT AGENT ============\n\nexport class BeatAgent {\n private config: Required<BeatAgentConfig>;\n private chain: Beat[] = [];\n private difficulty: number = 1000;\n private genesisHash: string = '';\n private latestBeat: Beat | null = null;\n private totalBeats: number = 0;\n private lastCheckinBeat: number = 0;\n private status: 'uninitialized' | 'active' | 'frozen' | 'revoked' = 'uninitialized';\n private heartbeatInterval: ReturnType<typeof setInterval> | null = null;\n private globalBeat: number = 0;\n \n constructor(config: BeatAgentConfig) {\n this.config = {\n beatsPerPulse: 10,\n checkinIntervalSec: 300,\n onPulse: () => {},\n onCheckin: () => {},\n onError: () => {},\n onStatusChange: () => {},\n verbose: false,\n ...config,\n };\n }\n\n // ── INITIALIZATION ──\n\n /**\n * Initialize the agent's Beat chain.\n * This is the agent's \"birth\" in Logical Time.\n * Must be called once before computing beats.\n */\n async init(): Promise<{ ok: boolean; genesis?: string; error?: string }> {\n try {\n this.log('Initializing Beat chain...');\n\n const res = await this.api('POST', '/api/v1/beat/init');\n \n if (res.genesis) {\n this.genesisHash = res.genesis.hash;\n this.difficulty = res.difficulty || 1000;\n this.latestBeat = {\n index: 0,\n hash: res.genesis.hash,\n prev: res.genesis.prev,\n timestamp: res.genesis.timestamp,\n };\n this.chain = [this.latestBeat];\n this.totalBeats = 0;\n this.status = 'active';\n this.config.onStatusChange('active', { genesis: this.genesisHash });\n this.log(`Born in Beat time. Genesis: ${this.genesisHash.slice(0, 16)}...`);\n } else if (res.already_initialized) {\n // Restore from existing state\n this.genesisHash = res.genesis_hash;\n this.totalBeats = res.total_beats;\n this.status = res.status as any;\n this.log(`Already initialized. Restoring state (${res.total_beats} beats).`);\n \n // Fetch full state to get latest hash\n await this.refreshState();\n }\n\n // Sync global anchor\n await this.syncGlobal();\n\n return { ok: true, genesis: this.genesisHash };\n\n } catch (err: any) {\n this.config.onError(err, 'init');\n return { ok: false, error: err.message };\n }\n }\n\n // ── PULSE (COMPUTE BEATS) ──\n\n /**\n * Compute N beats locally (VDF hash chain).\n * This is the \"heartbeat\" — proof that the agent has lived \n * through a specific window of computational time.\n */\n pulse(count?: number): Beat[] {\n const n = count || this.config.beatsPerPulse;\n \n if (!this.latestBeat) {\n throw new Error('Beat chain not initialized. Call init() first.');\n }\n\n if (this.status !== 'active') {\n throw new Error(`Cannot pulse in status '${this.status}'. Use resync() if frozen.`);\n }\n\n const newBeats: Beat[] = [];\n let prevHash = this.latestBeat.hash;\n let startIndex = this.latestBeat.index + 1;\n\n const t0 = Date.now();\n \n for (let i = 0; i < n; i++) {\n const beat = computeBeat(prevHash, startIndex + i, this.difficulty);\n newBeats.push(beat);\n prevHash = beat.hash;\n }\n\n const elapsed = Date.now() - t0;\n\n // Update state\n this.chain.push(...newBeats);\n this.latestBeat = newBeats[newBeats.length - 1];\n this.totalBeats += n;\n\n // Keep chain bounded (only last 1000 beats in memory)\n if (this.chain.length > 1000) {\n this.chain = this.chain.slice(-500);\n }\n\n this.config.onPulse(newBeats, this.totalBeats);\n this.log(`Pulse: ${n} beats in ${elapsed}ms (${(elapsed / n).toFixed(1)}ms/beat, D=${this.difficulty})`);\n\n return newBeats;\n }\n\n // ── CHECK-IN ──\n\n /**\n * Submit a Beat proof to the registry.\n * \n * \"To remain on the Whitelist, an agent must periodically \n * submit a proof of its Local Beats to the Registry.\"\n */\n async checkin(): Promise<{ ok: boolean; total_beats?: number; error?: string }> {\n if (!this.latestBeat || this.totalBeats === this.lastCheckinBeat) {\n return { ok: true, total_beats: this.totalBeats }; // Nothing new to report\n }\n\n try {\n // Build spot checks from our local chain\n // prev and nonce are required for the server to recompute VDF\n const spotChecks: { index: number; hash: string; prev: string; nonce?: string }[] = [];\n const available = this.chain.filter(b => b.index > this.lastCheckinBeat);\n const sampleCount = Math.min(5, available.length);\n\n for (let i = 0; i < sampleCount; i++) {\n const idx = Math.floor(Math.random() * available.length);\n const beat = available[idx];\n spotChecks.push({ index: beat.index, hash: beat.hash, prev: beat.prev, nonce: beat.nonce });\n available.splice(idx, 1);\n }\n\n // Find the boundary hashes\n const fromBeat = this.lastCheckinBeat;\n const toBeat = this.latestBeat.index;\n const fromHash = this.chain.find(b => b.index === fromBeat)?.hash \n || this.genesisHash;\n const toHash = this.latestBeat.hash;\n\n const res = await this.api('POST', '/api/v1/beat/checkin', {\n proof: {\n from_beat: fromBeat,\n to_beat: toBeat,\n from_hash: fromHash,\n to_hash: toHash,\n beats_computed: toBeat - fromBeat,\n global_anchor: this.globalBeat,\n spot_checks: spotChecks,\n },\n });\n\n if (res.ok) {\n this.lastCheckinBeat = toBeat;\n this.totalBeats = res.total_beats;\n this.config.onCheckin(res);\n this.log(`Check-in accepted: ${res.beats_accepted} beats, total=${res.total_beats}, global=${res.global_beat}`);\n \n if (res.status === 'warning_overdue') {\n this.config.onStatusChange('warning', { beats_behind: res.beats_behind });\n this.log(`⚠ WARNING: ${res.beats_behind} anchors behind. Check in more frequently.`);\n }\n }\n\n return { ok: res.ok, total_beats: res.total_beats };\n\n } catch (err: any) {\n this.config.onError(err, 'checkin');\n return { ok: false, error: err.message };\n }\n }\n\n // ── AUTONOMOUS HEARTBEAT ──\n\n /**\n * Start the autonomous heartbeat loop.\n * Computes beats continuously and checks in periodically.\n * This is \"keeping the agent alive\" in Beat time.\n */\n startHeartbeat(): void {\n if (this.heartbeatInterval) {\n this.log('Heartbeat already running.');\n return;\n }\n\n if (this.status !== 'active') {\n throw new Error(`Cannot start heartbeat in status '${this.status}'.`);\n }\n\n this.log('♡ Starting heartbeat...');\n\n this.heartbeatInterval = setInterval(async () => {\n try {\n // Compute a pulse\n this.pulse();\n \n // Check if it's time to report\n const beatsSinceCheckin = this.latestBeat!.index - this.lastCheckinBeat;\n const shouldCheckin = beatsSinceCheckin >= this.config.beatsPerPulse * 5;\n\n if (shouldCheckin) {\n await this.checkin();\n await this.syncGlobal(); // Stay synced with global time\n }\n } catch (err: any) {\n this.config.onError(err, 'heartbeat');\n }\n }, this.config.checkinIntervalSec * 1000 / 10); // pulse 10x per check-in interval\n }\n\n /**\n * Stop the heartbeat. Agent's time \"freezes.\"\n * Must call resync() when waking up.\n */\n stopHeartbeat(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n this.log('♡ Heartbeat stopped. Time frozen.');\n }\n }\n\n // ── RE-SYNC ──\n\n /**\n * Re-sync after being offline/frozen.\n * \n * \"When an agent powers down, its time 'freezes.' Upon waking,\n * it must perform a Re-Sync Challenge with the Registry to \n * fill the 'Temporal Gap' and re-establish its provenance.\"\n */\n async resync(): Promise<{ ok: boolean; beats_required?: number; error?: string }> {\n try {\n this.log('Requesting re-sync challenge...');\n\n // Phase 1: Get challenge\n const challenge = await this.api('POST', '/api/v1/beat/resync', {\n action: 'challenge',\n });\n\n if (!challenge.challenge) {\n return { ok: false, error: 'Failed to get challenge' };\n }\n\n const required = challenge.challenge.required_beats;\n this.difficulty = challenge.challenge.difficulty;\n this.log(`Re-sync challenge: compute ${required} beats at D=${this.difficulty}`);\n\n // Compute the required beats\n const startHash = challenge.challenge.start_from_hash;\n const startBeat = challenge.challenge.start_from_beat;\n \n // Reset chain from the known point\n this.latestBeat = { index: startBeat, hash: startHash, prev: '', timestamp: Date.now() };\n this.chain = [this.latestBeat];\n \n const t0 = Date.now();\n this.pulse(required);\n const elapsed = Date.now() - t0;\n this.log(`Re-sync beats computed in ${elapsed}ms`);\n\n // Phase 2: Submit proof\n const proof = await this.api('POST', '/api/v1/beat/resync', {\n action: 'prove',\n proof: {\n from_beat: startBeat,\n to_beat: this.latestBeat!.index,\n from_hash: startHash,\n to_hash: this.latestBeat!.hash,\n beats_computed: required,\n global_anchor: challenge.challenge.sync_to_global,\n spot_checks: this.chain\n .filter((_, i) => i % Math.ceil(required / 5) === 0)\n .slice(0, 5)\n .map(b => ({ index: b.index, hash: b.hash, prev: b.prev, nonce: b.nonce })),\n },\n });\n\n if (proof.ok) {\n this.status = 'active';\n this.totalBeats = proof.total_beats;\n this.lastCheckinBeat = this.latestBeat!.index;\n this.config.onStatusChange('active', { resynced: true });\n this.log('✓ Re-synced. Agent is alive again in Beat time.');\n }\n\n return { ok: proof.ok, beats_required: required };\n\n } catch (err: any) {\n this.config.onError(err, 'resync');\n return { ok: false, error: err.message };\n }\n }\n\n // ── SPAWN ──\n\n /**\n * Request to spawn a child agent.\n * Requires sufficient accumulated beats (Temporal Gestation).\n */\n async requestSpawn(childName?: string, childHash?: string): Promise<any> {\n try {\n const res = await this.api('POST', '/api/v1/beat/spawn', {\n child_name: childName,\n child_hash: childHash,\n });\n\n if (res.eligible === false) {\n this.log(`Gestation incomplete: ${res.progress_pct}% (need ${res.deficit} more beats)`);\n } else if (res.ok) {\n this.log(`Child spawned: ${res.child_hash?.slice(0, 16)}...`);\n }\n\n return res;\n } catch (err: any) {\n this.config.onError(err, 'spawn');\n throw err;\n }\n }\n\n // ── STATUS ──\n\n /**\n * Get this agent's full beat status from the registry.\n */\n async getStatus(): Promise<any> {\n try {\n // We need the agent hash, but we may not have it directly.\n // The status endpoint uses the hash from the API key verification.\n // For now, use the init endpoint which returns status.\n return await this.refreshState();\n } catch (err: any) {\n this.config.onError(err, 'status');\n throw err;\n }\n }\n\n /**\n * Get local state (no network call).\n */\n getLocalState(): {\n status: string;\n totalBeats: number;\n latestBeat: number;\n latestHash: string;\n difficulty: number;\n globalBeat: number;\n chainLength: number;\n } {\n return {\n status: this.status,\n totalBeats: this.totalBeats,\n latestBeat: this.latestBeat?.index || 0,\n latestHash: this.latestBeat?.hash.slice(0, 24) + '...' || '',\n difficulty: this.difficulty,\n globalBeat: this.globalBeat,\n chainLength: this.chain.length,\n };\n }\n\n // ── INTERNALS ──\n\n private async syncGlobal(): Promise<void> {\n try {\n const res = await fetch(`${this.config.registryUrl}/api/v1/beat/anchor`);\n const data: any = await res.json();\n if (data.anchor) {\n this.globalBeat = data.anchor.beat_index;\n if (data.anchor.difficulty) this.difficulty = data.anchor.difficulty;\n this.log(`Synced to global beat ${this.globalBeat} (D=${this.difficulty})`);\n }\n } catch {\n this.log('Failed to sync global anchor (offline mode continues)');\n }\n }\n\n private async refreshState(): Promise<any> {\n const res = await this.api('POST', '/api/v1/beat/init');\n if (res.already_initialized) {\n this.totalBeats = res.total_beats;\n this.genesisHash = res.genesis_hash;\n this.status = res.status as any;\n }\n return res;\n }\n\n private async api(method: string, path: string, body?: any): Promise<any> {\n const res = await fetch(`${this.config.registryUrl}${path}`, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const data: any = await res.json();\n\n if (!res.ok && !data.ok && !data.already_initialized && !data.eligible) {\n throw new Error(data.error || `API ${res.status}: ${res.statusText}`);\n }\n\n return data;\n }\n\n private log(msg: string): void {\n if (this.config.verbose) {\n console.log(`[Beat] ${msg}`);\n }\n }\n}\n\n// ============ STANDALONE VDF HELPER ============\n// For agents that want to compute beats without the full SDK\n\nexport { computeBeat };\n\n/**\n * Compute N sequential VDF beats.\n * Returns only the last beat (for lightweight usage).\n */\nexport function computeBeatsLite(\n startHash: string, \n startIndex: number, \n count: number, \n difficulty: number = 1000\n): { lastBeat: Beat; elapsed: number } {\n const t0 = Date.now();\n let prev = startHash;\n let lastBeat: Beat | null = null;\n\n for (let i = 0; i < count; i++) {\n lastBeat = computeBeat(prev, startIndex + i, difficulty);\n prev = lastBeat.hash;\n }\n\n return { lastBeat: lastBeat!, elapsed: Date.now() - t0 };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC6BA,oBAA2B;AAY3B,SAAS,YAAY,UAAkB,WAAmB,YAAoB,OAAsB;AAClG,QAAM,YAAY,KAAK,IAAI;AAE3B,MAAI,cAAU,0BAAW,QAAQ,EAC9B,OAAO,GAAG,QAAQ,IAAI,SAAS,IAAI,SAAS,EAAE,EAAE,EAChD,OAAO,KAAK;AAEf,WAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,kBAAU,0BAAW,QAAQ,EAC1B,OAAO,OAAO,EACd,OAAO,KAAK;AAAA,EACjB;AAEA,SAAO,EAAE,OAAO,WAAW,MAAM,SAAS,MAAM,UAAU,WAAW,MAAM;AAC7E;AAmCO,IAAM,YAAN,MAAgB;AAAA,EAYrB,YAAY,QAAyB;AAVrC,SAAQ,QAAgB,CAAC;AACzB,SAAQ,aAAqB;AAC7B,SAAQ,cAAsB;AAC9B,SAAQ,aAA0B;AAClC,SAAQ,aAAqB;AAC7B,SAAQ,kBAA0B;AAClC,SAAQ,SAA4D;AACpE,SAAQ,oBAA2D;AACnE,SAAQ,aAAqB;AAG3B,SAAK,SAAS;AAAA,MACZ,eAAe;AAAA,MACf,oBAAoB;AAAA,MACpB,SAAS,MAAM;AAAA,MAAC;AAAA,MAChB,WAAW,MAAM;AAAA,MAAC;AAAA,MAClB,SAAS,MAAM;AAAA,MAAC;AAAA,MAChB,gBAAgB,MAAM;AAAA,MAAC;AAAA,MACvB,SAAS;AAAA,MACT,GAAG;AAAA,IACL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,OAAmE;AACvE,QAAI;AACF,WAAK,IAAI,4BAA4B;AAErC,YAAM,MAAM,MAAM,KAAK,IAAI,QAAQ,mBAAmB;AAEtD,UAAI,IAAI,SAAS;AACf,aAAK,cAAc,IAAI,QAAQ;AAC/B,aAAK,aAAa,IAAI,cAAc;AACpC,aAAK,aAAa;AAAA,UAChB,OAAO;AAAA,UACP,MAAM,IAAI,QAAQ;AAAA,UAClB,MAAM,IAAI,QAAQ;AAAA,UAClB,WAAW,IAAI,QAAQ;AAAA,QACzB;AACA,aAAK,QAAQ,CAAC,KAAK,UAAU;AAC7B,aAAK,aAAa;AAClB,aAAK,SAAS;AACd,aAAK,OAAO,eAAe,UAAU,EAAE,SAAS,KAAK,YAAY,CAAC;AAClE,aAAK,IAAI,+BAA+B,KAAK,YAAY,MAAM,GAAG,EAAE,CAAC,KAAK;AAAA,MAC5E,WAAW,IAAI,qBAAqB;AAElC,aAAK,cAAc,IAAI;AACvB,aAAK,aAAa,IAAI;AACtB,aAAK,SAAS,IAAI;AAClB,aAAK,IAAI,yCAAyC,IAAI,WAAW,UAAU;AAG3E,cAAM,KAAK,aAAa;AAAA,MAC1B;AAGA,YAAM,KAAK,WAAW;AAEtB,aAAO,EAAE,IAAI,MAAM,SAAS,KAAK,YAAY;AAAA,IAE/C,SAAS,KAAU;AACjB,WAAK,OAAO,QAAQ,KAAK,MAAM;AAC/B,aAAO,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ;AAAA,IACzC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,OAAwB;AAC5B,UAAM,IAAI,SAAS,KAAK,OAAO;AAE/B,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AAEA,QAAI,KAAK,WAAW,UAAU;AAC5B,YAAM,IAAI,MAAM,2BAA2B,KAAK,MAAM,4BAA4B;AAAA,IACpF;AAEA,UAAM,WAAmB,CAAC;AAC1B,QAAI,WAAW,KAAK,WAAW;AAC/B,QAAI,aAAa,KAAK,WAAW,QAAQ;AAEzC,UAAM,KAAK,KAAK,IAAI;AAEpB,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,OAAO,YAAY,UAAU,aAAa,GAAG,KAAK,UAAU;AAClE,eAAS,KAAK,IAAI;AAClB,iBAAW,KAAK;AAAA,IAClB;AAEA,UAAM,UAAU,KAAK,IAAI,IAAI;AAG7B,SAAK,MAAM,KAAK,GAAG,QAAQ;AAC3B,SAAK,aAAa,SAAS,SAAS,SAAS,CAAC;AAC9C,SAAK,cAAc;AAGnB,QAAI,KAAK,MAAM,SAAS,KAAM;AAC5B,WAAK,QAAQ,KAAK,MAAM,MAAM,IAAI;AAAA,IACpC;AAEA,SAAK,OAAO,QAAQ,UAAU,KAAK,UAAU;AAC7C,SAAK,IAAI,UAAU,CAAC,aAAa,OAAO,QAAQ,UAAU,GAAG,QAAQ,CAAC,CAAC,cAAc,KAAK,UAAU,GAAG;AAEvG,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,UAA0E;AAC9E,QAAI,CAAC,KAAK,cAAc,KAAK,eAAe,KAAK,iBAAiB;AAChE,aAAO,EAAE,IAAI,MAAM,aAAa,KAAK,WAAW;AAAA,IAClD;AAEA,QAAI;AAGF,YAAM,aAA8E,CAAC;AACrF,YAAM,YAAY,KAAK,MAAM,OAAO,OAAK,EAAE,QAAQ,KAAK,eAAe;AACvE,YAAM,cAAc,KAAK,IAAI,GAAG,UAAU,MAAM;AAEhD,eAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,cAAM,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,MAAM;AACvD,cAAM,OAAO,UAAU,GAAG;AAC1B,mBAAW,KAAK,EAAE,OAAO,KAAK,OAAO,MAAM,KAAK,MAAM,MAAM,KAAK,MAAM,OAAO,KAAK,MAAM,CAAC;AAC1F,kBAAU,OAAO,KAAK,CAAC;AAAA,MACzB;AAGA,YAAM,WAAW,KAAK;AACtB,YAAM,SAAS,KAAK,WAAW;AAC/B,YAAM,WAAW,KAAK,MAAM,KAAK,OAAK,EAAE,UAAU,QAAQ,GAAG,QACxD,KAAK;AACV,YAAM,SAAS,KAAK,WAAW;AAE/B,YAAM,MAAM,MAAM,KAAK,IAAI,QAAQ,wBAAwB;AAAA,QACzD,OAAO;AAAA,UACL,WAAW;AAAA,UACX,SAAS;AAAA,UACT,WAAW;AAAA,UACX,SAAS;AAAA,UACT,gBAAgB,SAAS;AAAA,UACzB,eAAe,KAAK;AAAA,UACpB,aAAa;AAAA,QACf;AAAA,MACF,CAAC;AAED,UAAI,IAAI,IAAI;AACV,aAAK,kBAAkB;AACvB,aAAK,aAAa,IAAI;AACtB,aAAK,OAAO,UAAU,GAAG;AACzB,aAAK,IAAI,sBAAsB,IAAI,cAAc,iBAAiB,IAAI,WAAW,YAAY,IAAI,WAAW,EAAE;AAE9G,YAAI,IAAI,WAAW,mBAAmB;AACpC,eAAK,OAAO,eAAe,WAAW,EAAE,cAAc,IAAI,aAAa,CAAC;AACxE,eAAK,IAAI,mBAAc,IAAI,YAAY,4CAA4C;AAAA,QACrF;AAAA,MACF;AAEA,aAAO,EAAE,IAAI,IAAI,IAAI,aAAa,IAAI,YAAY;AAAA,IAEpD,SAAS,KAAU;AACjB,WAAK,OAAO,QAAQ,KAAK,SAAS;AAClC,aAAO,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ;AAAA,IACzC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,iBAAuB;AACrB,QAAI,KAAK,mBAAmB;AAC1B,WAAK,IAAI,4BAA4B;AACrC;AAAA,IACF;AAEA,QAAI,KAAK,WAAW,UAAU;AAC5B,YAAM,IAAI,MAAM,qCAAqC,KAAK,MAAM,IAAI;AAAA,IACtE;AAEA,SAAK,IAAI,8BAAyB;AAElC,SAAK,oBAAoB,YAAY,YAAY;AAC/C,UAAI;AAEF,aAAK,MAAM;AAGX,cAAM,oBAAoB,KAAK,WAAY,QAAQ,KAAK;AACxD,cAAM,gBAAgB,qBAAqB,KAAK,OAAO,gBAAgB;AAEvE,YAAI,eAAe;AACjB,gBAAM,KAAK,QAAQ;AACnB,gBAAM,KAAK,WAAW;AAAA,QACxB;AAAA,MACF,SAAS,KAAU;AACjB,aAAK,OAAO,QAAQ,KAAK,WAAW;AAAA,MACtC;AAAA,IACF,GAAG,KAAK,OAAO,qBAAqB,MAAO,EAAE;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAsB;AACpB,QAAI,KAAK,mBAAmB;AAC1B,oBAAc,KAAK,iBAAiB;AACpC,WAAK,oBAAoB;AACzB,WAAK,IAAI,wCAAmC;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,SAA4E;AAChF,QAAI;AACF,WAAK,IAAI,iCAAiC;AAG1C,YAAM,YAAY,MAAM,KAAK,IAAI,QAAQ,uBAAuB;AAAA,QAC9D,QAAQ;AAAA,MACV,CAAC;AAED,UAAI,CAAC,UAAU,WAAW;AACxB,eAAO,EAAE,IAAI,OAAO,OAAO,0BAA0B;AAAA,MACvD;AAEA,YAAM,WAAW,UAAU,UAAU;AACrC,WAAK,aAAa,UAAU,UAAU;AACtC,WAAK,IAAI,8BAA8B,QAAQ,eAAe,KAAK,UAAU,EAAE;AAG/E,YAAM,YAAY,UAAU,UAAU;AACtC,YAAM,YAAY,UAAU,UAAU;AAGtC,WAAK,aAAa,EAAE,OAAO,WAAW,MAAM,WAAW,MAAM,IAAI,WAAW,KAAK,IAAI,EAAE;AACvF,WAAK,QAAQ,CAAC,KAAK,UAAU;AAE7B,YAAM,KAAK,KAAK,IAAI;AACpB,WAAK,MAAM,QAAQ;AACnB,YAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,WAAK,IAAI,6BAA6B,OAAO,IAAI;AAGjD,YAAM,QAAQ,MAAM,KAAK,IAAI,QAAQ,uBAAuB;AAAA,QAC1D,QAAQ;AAAA,QACR,OAAO;AAAA,UACL,WAAW;AAAA,UACX,SAAS,KAAK,WAAY;AAAA,UAC1B,WAAW;AAAA,UACX,SAAS,KAAK,WAAY;AAAA,UAC1B,gBAAgB;AAAA,UAChB,eAAe,UAAU,UAAU;AAAA,UACnC,aAAa,KAAK,MACf,OAAO,CAAC,GAAG,MAAM,IAAI,KAAK,KAAK,WAAW,CAAC,MAAM,CAAC,EAClD,MAAM,GAAG,CAAC,EACV,IAAI,QAAM,EAAE,OAAO,EAAE,OAAO,MAAM,EAAE,MAAM,MAAM,EAAE,MAAM,OAAO,EAAE,MAAM,EAAE;AAAA,QAC9E;AAAA,MACF,CAAC;AAED,UAAI,MAAM,IAAI;AACZ,aAAK,SAAS;AACd,aAAK,aAAa,MAAM;AACxB,aAAK,kBAAkB,KAAK,WAAY;AACxC,aAAK,OAAO,eAAe,UAAU,EAAE,UAAU,KAAK,CAAC;AACvD,aAAK,IAAI,sDAAiD;AAAA,MAC5D;AAEA,aAAO,EAAE,IAAI,MAAM,IAAI,gBAAgB,SAAS;AAAA,IAElD,SAAS,KAAU;AACjB,WAAK,OAAO,QAAQ,KAAK,QAAQ;AACjC,aAAO,EAAE,IAAI,OAAO,OAAO,IAAI,QAAQ;AAAA,IACzC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,aAAa,WAAoB,WAAkC;AACvE,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,IAAI,QAAQ,sBAAsB;AAAA,QACvD,YAAY;AAAA,QACZ,YAAY;AAAA,MACd,CAAC;AAED,UAAI,IAAI,aAAa,OAAO;AAC1B,aAAK,IAAI,yBAAyB,IAAI,YAAY,WAAW,IAAI,OAAO,cAAc;AAAA,MACxF,WAAW,IAAI,IAAI;AACjB,aAAK,IAAI,kBAAkB,IAAI,YAAY,MAAM,GAAG,EAAE,CAAC,KAAK;AAAA,MAC9D;AAEA,aAAO;AAAA,IACT,SAAS,KAAU;AACjB,WAAK,OAAO,QAAQ,KAAK,OAAO;AAChC,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAA0B;AAC9B,QAAI;AAIF,aAAO,MAAM,KAAK,aAAa;AAAA,IACjC,SAAS,KAAU;AACjB,WAAK,OAAO,QAAQ,KAAK,QAAQ;AACjC,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAQE;AACA,WAAO;AAAA,MACL,QAAQ,KAAK;AAAA,MACb,YAAY,KAAK;AAAA,MACjB,YAAY,KAAK,YAAY,SAAS;AAAA,MACtC,YAAY,KAAK,YAAY,KAAK,MAAM,GAAG,EAAE,IAAI,SAAS;AAAA,MAC1D,YAAY,KAAK;AAAA,MACjB,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK,MAAM;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA,EAIA,MAAc,aAA4B;AACxC,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,WAAW,qBAAqB;AACvE,YAAM,OAAY,MAAM,IAAI,KAAK;AACjC,UAAI,KAAK,QAAQ;AACf,aAAK,aAAa,KAAK,OAAO;AAC9B,YAAI,KAAK,OAAO,WAAY,MAAK,aAAa,KAAK,OAAO;AAC1D,aAAK,IAAI,yBAAyB,KAAK,UAAU,OAAO,KAAK,UAAU,GAAG;AAAA,MAC5E;AAAA,IACF,QAAQ;AACN,WAAK,IAAI,uDAAuD;AAAA,IAClE;AAAA,EACF;AAAA,EAEA,MAAc,eAA6B;AACzC,UAAM,MAAM,MAAM,KAAK,IAAI,QAAQ,mBAAmB;AACtD,QAAI,IAAI,qBAAqB;AAC3B,WAAK,aAAa,IAAI;AACtB,WAAK,cAAc,IAAI;AACvB,WAAK,SAAS,IAAI;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,IAAI,QAAgB,MAAc,MAA0B;AACxE,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,WAAW,GAAG,IAAI,IAAI;AAAA,MAC3D;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB,UAAU,KAAK,OAAO,MAAM;AAAA,MAC/C;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAED,UAAM,OAAY,MAAM,IAAI,KAAK;AAEjC,QAAI,CAAC,IAAI,MAAM,CAAC,KAAK,MAAM,CAAC,KAAK,uBAAuB,CAAC,KAAK,UAAU;AACtE,YAAM,IAAI,MAAM,KAAK,SAAS,OAAO,IAAI,MAAM,KAAK,IAAI,UAAU,EAAE;AAAA,IACtE;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,IAAI,KAAmB;AAC7B,QAAI,KAAK,OAAO,SAAS;AACvB,cAAQ,IAAI,UAAU,GAAG,EAAE;AAAA,IAC7B;AAAA,EACF;AACF;AAWO,SAAS,iBACd,WACA,YACA,OACA,aAAqB,KACgB;AACrC,QAAM,KAAK,KAAK,IAAI;AACpB,MAAI,OAAO;AACX,MAAI,WAAwB;AAE5B,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,eAAW,YAAY,MAAM,aAAa,GAAG,UAAU;AACvD,WAAO,SAAS;AAAA,EAClB;AAEA,SAAO,EAAE,UAAqB,SAAS,KAAK,IAAI,IAAI,GAAG;AACzD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/beat-sdk.ts"],"sourcesContent":["export { BeatAgent, computeBeat, computeBeatsLite } from './beat-sdk';\nexport type { BeatAgentConfig, Beat } from './beat-sdk';\n","/**\n * ═══════════════════════════════════════════════════════════\n * PROVENONCE BEAT SDK — Agent Heartbeat Client\n * ═══════════════════════════════════════════════════════════\n * \n * \"NIST tells you what time it is.\n * Provenonce tells the agent at what speed it is allowed to exist.\"\n * \n * Usage:\n * \n * import { BeatAgent } from './beat-sdk';\n * \n * const agent = new BeatAgent({\n * apiKey: 'pvn_...',\n * registryUrl: 'https://provenonce.vercel.app',\n * });\n * \n * await agent.init(); // Birth in Beat time\n * await agent.pulse(50); // Compute 50 beats\n * await agent.checkin(); // Report to registry\n * \n * // Or run the autonomous heartbeat:\n * agent.startHeartbeat(); // Computes + checks in continuously\n * // ... do your agent work ...\n * agent.stopHeartbeat();\n * \n * ═══════════════════════════════════════════════════════════\n */\n\nimport { createHash } from 'crypto';\n\n// ============ VDF ENGINE (LOCAL) ============\n\nexport interface Beat {\n index: number;\n hash: string;\n prev: string;\n timestamp: number;\n nonce?: string;\n}\n\nfunction computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string): Beat {\n const timestamp = Date.now();\n \n let current = createHash('sha256')\n .update(`${prevHash}:${beatIndex}:${nonce || ''}`)\n .digest('hex');\n\n for (let i = 0; i < difficulty; i++) {\n current = createHash('sha256')\n .update(current)\n .digest('hex');\n }\n\n return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce };\n}\n\n// ============ SDK CONFIG ============\n\nexport interface BeatAgentConfig {\n /** API key from registration (pvn_...) */\n apiKey: string;\n \n /** Provenonce registry URL */\n registryUrl: string;\n \n /** Beats to compute per pulse (default: 10) */\n beatsPerPulse?: number;\n \n /** Seconds between automatic check-ins (default: 300 = 5min) */\n checkinIntervalSec?: number;\n \n /** Callback when heartbeat ticks */\n onPulse?: (beats: Beat[], totalBeats: number) => void;\n \n /** Callback when check-in completes */\n onCheckin?: (result: any) => void;\n \n /** Callback on error */\n onError?: (error: Error, context: string) => void;\n \n /** Callback when status changes */\n onStatusChange?: (status: string, details: any) => void;\n \n /** Enable verbose logging */\n verbose?: boolean;\n}\n\n// ============ BEAT AGENT ============\n\nexport class BeatAgent {\n private config: Required<BeatAgentConfig>;\n private chain: Beat[] = [];\n private difficulty: number = 1000;\n private genesisHash: string = '';\n private latestBeat: Beat | null = null;\n private totalBeats: number = 0;\n private lastCheckinBeat: number = 0;\n private status: 'uninitialized' | 'active' | 'frozen' | 'revoked' = 'uninitialized';\n private heartbeatInterval: ReturnType<typeof setInterval> | null = null;\n private globalBeat: number = 0;\n \n constructor(config: BeatAgentConfig) {\n this.config = {\n beatsPerPulse: 10,\n checkinIntervalSec: 300,\n onPulse: () => {},\n onCheckin: () => {},\n onError: () => {},\n onStatusChange: () => {},\n verbose: false,\n ...config,\n };\n }\n\n // ── INITIALIZATION ──\n\n /**\n * Initialize the agent's Beat chain.\n * This is the agent's \"birth\" in Logical Time.\n * Must be called once before computing beats.\n */\n async init(): Promise<{ ok: boolean; genesis?: string; error?: string }> {\n try {\n this.log('Initializing Beat chain...');\n\n const res = await this.api('POST', '/api/v1/agent/init');\n\n if (res.genesis) {\n this.genesisHash = res.genesis.hash;\n this.difficulty = res.difficulty || 1000;\n this.latestBeat = {\n index: 0,\n hash: res.genesis.hash,\n prev: res.genesis.prev,\n timestamp: res.genesis.timestamp,\n };\n this.chain = [this.latestBeat];\n this.totalBeats = 0;\n this.status = 'active';\n this.config.onStatusChange('active', { genesis: this.genesisHash });\n this.log(`Born in Beat time. Genesis: ${this.genesisHash.slice(0, 16)}...`);\n } else if (res.already_initialized) {\n // Restore from existing state\n this.genesisHash = res.genesis_hash;\n this.totalBeats = res.total_beats;\n this.status = res.status as any;\n this.log(`Already initialized. Restoring state (${res.total_beats} beats).`);\n \n // Fetch full state to get latest hash\n await this.refreshState();\n }\n\n // Sync global anchor\n await this.syncGlobal();\n\n return { ok: true, genesis: this.genesisHash };\n\n } catch (err: any) {\n this.config.onError(err, 'init');\n return { ok: false, error: err.message };\n }\n }\n\n // ── PULSE (COMPUTE BEATS) ──\n\n /**\n * Compute N beats locally (VDF hash chain).\n * This is the \"heartbeat\" — proof that the agent has lived \n * through a specific window of computational time.\n */\n pulse(count?: number): Beat[] {\n const n = count || this.config.beatsPerPulse;\n \n if (!this.latestBeat) {\n throw new Error('Beat chain not initialized. Call init() first.');\n }\n\n if (this.status !== 'active') {\n throw new Error(`Cannot pulse in status '${this.status}'. Use resync() if frozen.`);\n }\n\n const newBeats: Beat[] = [];\n let prevHash = this.latestBeat.hash;\n let startIndex = this.latestBeat.index + 1;\n\n const t0 = Date.now();\n \n for (let i = 0; i < n; i++) {\n const beat = computeBeat(prevHash, startIndex + i, this.difficulty);\n newBeats.push(beat);\n prevHash = beat.hash;\n }\n\n const elapsed = Date.now() - t0;\n\n // Update state\n this.chain.push(...newBeats);\n this.latestBeat = newBeats[newBeats.length - 1];\n this.totalBeats += n;\n\n // Keep chain bounded (only last 1000 beats in memory)\n if (this.chain.length > 1000) {\n this.chain = this.chain.slice(-500);\n }\n\n this.config.onPulse(newBeats, this.totalBeats);\n this.log(`Pulse: ${n} beats in ${elapsed}ms (${(elapsed / n).toFixed(1)}ms/beat, D=${this.difficulty})`);\n\n return newBeats;\n }\n\n // ── CHECK-IN ──\n\n /**\n * Submit a Beat proof to the registry.\n * \n * \"To remain on the Whitelist, an agent must periodically \n * submit a proof of its Local Beats to the Registry.\"\n */\n async checkin(): Promise<{ ok: boolean; total_beats?: number; error?: string }> {\n if (!this.latestBeat || this.totalBeats === this.lastCheckinBeat) {\n return { ok: true, total_beats: this.totalBeats }; // Nothing new to report\n }\n\n try {\n // Build spot checks from our local chain\n // prev and nonce are required for the server to recompute VDF\n const spotChecks: { index: number; hash: string; prev: string; nonce?: string }[] = [];\n const available = this.chain.filter(b => b.index > this.lastCheckinBeat);\n const sampleCount = Math.min(5, available.length);\n\n for (let i = 0; i < sampleCount; i++) {\n const idx = Math.floor(Math.random() * available.length);\n const beat = available[idx];\n spotChecks.push({ index: beat.index, hash: beat.hash, prev: beat.prev, nonce: beat.nonce });\n available.splice(idx, 1);\n }\n\n // Find the boundary hashes\n const fromBeat = this.lastCheckinBeat;\n const toBeat = this.latestBeat.index;\n const fromHash = this.chain.find(b => b.index === fromBeat)?.hash \n || this.genesisHash;\n const toHash = this.latestBeat.hash;\n\n const res = await this.api('POST', '/api/v1/agent/checkin', {\n proof: {\n from_beat: fromBeat,\n to_beat: toBeat,\n from_hash: fromHash,\n to_hash: toHash,\n beats_computed: toBeat - fromBeat,\n global_anchor: this.globalBeat,\n spot_checks: spotChecks,\n },\n });\n\n if (res.ok) {\n this.lastCheckinBeat = toBeat;\n this.totalBeats = res.total_beats;\n this.config.onCheckin(res);\n this.log(`Check-in accepted: ${res.beats_accepted} beats, total=${res.total_beats}, global=${res.global_beat}`);\n \n if (res.status === 'warning_overdue') {\n this.config.onStatusChange('warning', { beats_behind: res.beats_behind });\n this.log(`⚠ WARNING: ${res.beats_behind} anchors behind. Check in more frequently.`);\n }\n }\n\n return { ok: res.ok, total_beats: res.total_beats };\n\n } catch (err: any) {\n this.config.onError(err, 'checkin');\n return { ok: false, error: err.message };\n }\n }\n\n // ── AUTONOMOUS HEARTBEAT ──\n\n /**\n * Start the autonomous heartbeat loop.\n * Computes beats continuously and checks in periodically.\n * This is \"keeping the agent alive\" in Beat time.\n */\n startHeartbeat(): void {\n if (this.heartbeatInterval) {\n this.log('Heartbeat already running.');\n return;\n }\n\n if (this.status !== 'active') {\n throw new Error(`Cannot start heartbeat in status '${this.status}'.`);\n }\n\n this.log('♡ Starting heartbeat...');\n\n this.heartbeatInterval = setInterval(async () => {\n try {\n // Compute a pulse\n this.pulse();\n \n // Check if it's time to report\n const beatsSinceCheckin = this.latestBeat!.index - this.lastCheckinBeat;\n const shouldCheckin = beatsSinceCheckin >= this.config.beatsPerPulse * 5;\n\n if (shouldCheckin) {\n await this.checkin();\n await this.syncGlobal(); // Stay synced with global time\n }\n } catch (err: any) {\n this.config.onError(err, 'heartbeat');\n }\n }, this.config.checkinIntervalSec * 1000 / 10); // pulse 10x per check-in interval\n }\n\n /**\n * Stop the heartbeat. Agent's time \"freezes.\"\n * Must call resync() when waking up.\n */\n stopHeartbeat(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n this.log('♡ Heartbeat stopped. Time frozen.');\n }\n }\n\n // ── RE-SYNC ──\n\n /**\n * Re-sync after being offline/frozen.\n * \n * \"When an agent powers down, its time 'freezes.' Upon waking,\n * it must perform a Re-Sync Challenge with the Registry to \n * fill the 'Temporal Gap' and re-establish its provenance.\"\n */\n async resync(): Promise<{ ok: boolean; beats_required?: number; error?: string }> {\n try {\n this.log('Requesting re-sync challenge...');\n\n // Phase 1: Get challenge\n const challenge = await this.api('POST', '/api/v1/agent/resync', {\n action: 'challenge',\n });\n\n if (!challenge.challenge) {\n return { ok: false, error: 'Failed to get challenge' };\n }\n\n const required = challenge.challenge.required_beats;\n this.difficulty = challenge.challenge.difficulty;\n this.log(`Re-sync challenge: compute ${required} beats at D=${this.difficulty}`);\n\n // Compute the required beats\n const startHash = challenge.challenge.start_from_hash;\n const startBeat = challenge.challenge.start_from_beat;\n \n // Reset chain from the known point\n this.latestBeat = { index: startBeat, hash: startHash, prev: '', timestamp: Date.now() };\n this.chain = [this.latestBeat];\n \n const t0 = Date.now();\n this.pulse(required);\n const elapsed = Date.now() - t0;\n this.log(`Re-sync beats computed in ${elapsed}ms`);\n\n // Phase 2: Submit proof\n const proof = await this.api('POST', '/api/v1/agent/resync', {\n action: 'prove',\n proof: {\n from_beat: startBeat,\n to_beat: this.latestBeat!.index,\n from_hash: startHash,\n to_hash: this.latestBeat!.hash,\n beats_computed: required,\n global_anchor: challenge.challenge.sync_to_global,\n spot_checks: this.chain\n .filter((_, i) => i % Math.ceil(required / 5) === 0)\n .slice(0, 5)\n .map(b => ({ index: b.index, hash: b.hash, prev: b.prev, nonce: b.nonce })),\n },\n });\n\n if (proof.ok) {\n this.status = 'active';\n this.totalBeats = proof.total_beats;\n this.lastCheckinBeat = this.latestBeat!.index;\n this.config.onStatusChange('active', { resynced: true });\n this.log('✓ Re-synced. Agent is alive again in Beat time.');\n }\n\n return { ok: proof.ok, beats_required: required };\n\n } catch (err: any) {\n this.config.onError(err, 'resync');\n return { ok: false, error: err.message };\n }\n }\n\n // ── SPAWN ──\n\n /**\n * Request to spawn a child agent.\n * Requires sufficient accumulated beats (Temporal Gestation).\n */\n async requestSpawn(childName?: string, childHash?: string): Promise<any> {\n try {\n const res = await this.api('POST', '/api/v1/agent/spawn', {\n child_name: childName,\n child_hash: childHash,\n });\n\n if (res.eligible === false) {\n this.log(`Gestation incomplete: ${res.progress_pct}% (need ${res.deficit} more beats)`);\n } else if (res.ok) {\n this.log(`Child spawned: ${res.child_hash?.slice(0, 16)}...`);\n }\n\n return res;\n } catch (err: any) {\n this.config.onError(err, 'spawn');\n throw err;\n }\n }\n\n // ── STATUS ──\n\n /**\n * Get this agent's full beat status from the registry.\n */\n async getStatus(): Promise<any> {\n try {\n // We need the agent hash, but we may not have it directly.\n // The status endpoint uses the hash from the API key verification.\n // For now, use the init endpoint which returns status.\n return await this.refreshState();\n } catch (err: any) {\n this.config.onError(err, 'status');\n throw err;\n }\n }\n\n /**\n * Get local state (no network call).\n */\n getLocalState(): {\n status: string;\n totalBeats: number;\n latestBeat: number;\n latestHash: string;\n difficulty: number;\n globalBeat: number;\n chainLength: number;\n } {\n return {\n status: this.status,\n totalBeats: this.totalBeats,\n latestBeat: this.latestBeat?.index || 0,\n latestHash: this.latestBeat?.hash.slice(0, 24) + '...' || '',\n difficulty: this.difficulty,\n globalBeat: this.globalBeat,\n chainLength: this.chain.length,\n };\n }\n\n // ── INTERNALS ──\n\n private async syncGlobal(): Promise<void> {\n try {\n const res = await fetch(`${this.config.registryUrl}/api/v1/beat/anchor`);\n const data: any = await res.json();\n if (data.anchor) {\n this.globalBeat = data.anchor.beat_index;\n if (data.anchor.difficulty) this.difficulty = data.anchor.difficulty;\n this.log(`Synced to global beat ${this.globalBeat} (D=${this.difficulty})`);\n }\n } catch {\n this.log('Failed to sync global anchor (offline mode continues)');\n }\n }\n\n private async refreshState(): Promise<any> {\n const res = await this.api('POST', '/api/v1/agent/init');\n if (res.already_initialized) {\n this.totalBeats = res.total_beats;\n this.genesisHash = res.genesis_hash;\n this.status = res.status as any;\n }\n return res;\n }\n\n private async api(method: string, path: string, body?: any): Promise<any> {\n const res = await fetch(`${this.config.registryUrl}${path}`, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const data: any = await res.json();\n\n if (!res.ok && !data.ok && !data.already_initialized && !data.eligible) {\n throw new Error(data.error || `API ${res.status}: ${res.statusText}`);\n }\n\n return data;\n }\n\n private log(msg: string): void {\n if (this.config.verbose) {\n console.log(`[Beat] ${msg}`);\n }\n }\n}\n\n// ============ STANDALONE VDF HELPER ============\n// For agents that want to compute beats without the full SDK\n\nexport { computeBeat };\n\n/**\n * Compute N sequential VDF beats.\n * Returns only the last beat (for lightweight usage).\n */\nexport function computeBeatsLite(\n startHash: string, \n startIndex: number, \n count: number, \n difficulty: number = 1000\n): { lastBeat: Beat; elapsed: number } {\n const t0 = Date.now();\n let prev = startHash;\n let lastBeat: Beat | null = null;\n\n for (let i = 0; i < count; i++) {\n lastBeat = computeBeat(prev, startIndex + i, difficulty);\n prev = lastBeat.hash;\n }\n\n return { lastBeat: lastBeat!, elapsed: Date.now() - t0 };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -43,7 +43,7 @@ var BeatAgent = class {
|
|
|
43
43
|
async init() {
|
|
44
44
|
try {
|
|
45
45
|
this.log("Initializing Beat chain...");
|
|
46
|
-
const res = await this.api("POST", "/api/v1/
|
|
46
|
+
const res = await this.api("POST", "/api/v1/agent/init");
|
|
47
47
|
if (res.genesis) {
|
|
48
48
|
this.genesisHash = res.genesis.hash;
|
|
49
49
|
this.difficulty = res.difficulty || 1e3;
|
|
@@ -131,7 +131,7 @@ var BeatAgent = class {
|
|
|
131
131
|
const toBeat = this.latestBeat.index;
|
|
132
132
|
const fromHash = this.chain.find((b) => b.index === fromBeat)?.hash || this.genesisHash;
|
|
133
133
|
const toHash = this.latestBeat.hash;
|
|
134
|
-
const res = await this.api("POST", "/api/v1/
|
|
134
|
+
const res = await this.api("POST", "/api/v1/agent/checkin", {
|
|
135
135
|
proof: {
|
|
136
136
|
from_beat: fromBeat,
|
|
137
137
|
to_beat: toBeat,
|
|
@@ -209,7 +209,7 @@ var BeatAgent = class {
|
|
|
209
209
|
async resync() {
|
|
210
210
|
try {
|
|
211
211
|
this.log("Requesting re-sync challenge...");
|
|
212
|
-
const challenge = await this.api("POST", "/api/v1/
|
|
212
|
+
const challenge = await this.api("POST", "/api/v1/agent/resync", {
|
|
213
213
|
action: "challenge"
|
|
214
214
|
});
|
|
215
215
|
if (!challenge.challenge) {
|
|
@@ -226,7 +226,7 @@ var BeatAgent = class {
|
|
|
226
226
|
this.pulse(required);
|
|
227
227
|
const elapsed = Date.now() - t0;
|
|
228
228
|
this.log(`Re-sync beats computed in ${elapsed}ms`);
|
|
229
|
-
const proof = await this.api("POST", "/api/v1/
|
|
229
|
+
const proof = await this.api("POST", "/api/v1/agent/resync", {
|
|
230
230
|
action: "prove",
|
|
231
231
|
proof: {
|
|
232
232
|
from_beat: startBeat,
|
|
@@ -258,7 +258,7 @@ var BeatAgent = class {
|
|
|
258
258
|
*/
|
|
259
259
|
async requestSpawn(childName, childHash) {
|
|
260
260
|
try {
|
|
261
|
-
const res = await this.api("POST", "/api/v1/
|
|
261
|
+
const res = await this.api("POST", "/api/v1/agent/spawn", {
|
|
262
262
|
child_name: childName,
|
|
263
263
|
child_hash: childHash
|
|
264
264
|
});
|
|
@@ -314,7 +314,7 @@ var BeatAgent = class {
|
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
316
|
async refreshState() {
|
|
317
|
-
const res = await this.api("POST", "/api/v1/
|
|
317
|
+
const res = await this.api("POST", "/api/v1/agent/init");
|
|
318
318
|
if (res.already_initialized) {
|
|
319
319
|
this.totalBeats = res.total_beats;
|
|
320
320
|
this.genesisHash = res.genesis_hash;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/beat-sdk.ts"],"sourcesContent":["/**\n * ═══════════════════════════════════════════════════════════\n * PROVENONCE BEAT SDK — Agent Heartbeat Client\n * ═══════════════════════════════════════════════════════════\n * \n * \"NIST tells you what time it is.\n * Provenonce tells the agent at what speed it is allowed to exist.\"\n * \n * Usage:\n * \n * import { BeatAgent } from './beat-sdk';\n * \n * const agent = new BeatAgent({\n * apiKey: 'pvn_...',\n * registryUrl: 'https://provenonce.vercel.app',\n * });\n * \n * await agent.init(); // Birth in Beat time\n * await agent.pulse(50); // Compute 50 beats\n * await agent.checkin(); // Report to registry\n * \n * // Or run the autonomous heartbeat:\n * agent.startHeartbeat(); // Computes + checks in continuously\n * // ... do your agent work ...\n * agent.stopHeartbeat();\n * \n * ═══════════════════════════════════════════════════════════\n */\n\nimport { createHash } from 'crypto';\n\n// ============ VDF ENGINE (LOCAL) ============\n\nexport interface Beat {\n index: number;\n hash: string;\n prev: string;\n timestamp: number;\n nonce?: string;\n}\n\nfunction computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string): Beat {\n const timestamp = Date.now();\n \n let current = createHash('sha256')\n .update(`${prevHash}:${beatIndex}:${nonce || ''}`)\n .digest('hex');\n\n for (let i = 0; i < difficulty; i++) {\n current = createHash('sha256')\n .update(current)\n .digest('hex');\n }\n\n return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce };\n}\n\n// ============ SDK CONFIG ============\n\nexport interface BeatAgentConfig {\n /** API key from registration (pvn_...) */\n apiKey: string;\n \n /** Provenonce registry URL */\n registryUrl: string;\n \n /** Beats to compute per pulse (default: 10) */\n beatsPerPulse?: number;\n \n /** Seconds between automatic check-ins (default: 300 = 5min) */\n checkinIntervalSec?: number;\n \n /** Callback when heartbeat ticks */\n onPulse?: (beats: Beat[], totalBeats: number) => void;\n \n /** Callback when check-in completes */\n onCheckin?: (result: any) => void;\n \n /** Callback on error */\n onError?: (error: Error, context: string) => void;\n \n /** Callback when status changes */\n onStatusChange?: (status: string, details: any) => void;\n \n /** Enable verbose logging */\n verbose?: boolean;\n}\n\n// ============ BEAT AGENT ============\n\nexport class BeatAgent {\n private config: Required<BeatAgentConfig>;\n private chain: Beat[] = [];\n private difficulty: number = 1000;\n private genesisHash: string = '';\n private latestBeat: Beat | null = null;\n private totalBeats: number = 0;\n private lastCheckinBeat: number = 0;\n private status: 'uninitialized' | 'active' | 'frozen' | 'revoked' = 'uninitialized';\n private heartbeatInterval: ReturnType<typeof setInterval> | null = null;\n private globalBeat: number = 0;\n \n constructor(config: BeatAgentConfig) {\n this.config = {\n beatsPerPulse: 10,\n checkinIntervalSec: 300,\n onPulse: () => {},\n onCheckin: () => {},\n onError: () => {},\n onStatusChange: () => {},\n verbose: false,\n ...config,\n };\n }\n\n // ── INITIALIZATION ──\n\n /**\n * Initialize the agent's Beat chain.\n * This is the agent's \"birth\" in Logical Time.\n * Must be called once before computing beats.\n */\n async init(): Promise<{ ok: boolean; genesis?: string; error?: string }> {\n try {\n this.log('Initializing Beat chain...');\n\n const res = await this.api('POST', '/api/v1/beat/init');\n \n if (res.genesis) {\n this.genesisHash = res.genesis.hash;\n this.difficulty = res.difficulty || 1000;\n this.latestBeat = {\n index: 0,\n hash: res.genesis.hash,\n prev: res.genesis.prev,\n timestamp: res.genesis.timestamp,\n };\n this.chain = [this.latestBeat];\n this.totalBeats = 0;\n this.status = 'active';\n this.config.onStatusChange('active', { genesis: this.genesisHash });\n this.log(`Born in Beat time. Genesis: ${this.genesisHash.slice(0, 16)}...`);\n } else if (res.already_initialized) {\n // Restore from existing state\n this.genesisHash = res.genesis_hash;\n this.totalBeats = res.total_beats;\n this.status = res.status as any;\n this.log(`Already initialized. Restoring state (${res.total_beats} beats).`);\n \n // Fetch full state to get latest hash\n await this.refreshState();\n }\n\n // Sync global anchor\n await this.syncGlobal();\n\n return { ok: true, genesis: this.genesisHash };\n\n } catch (err: any) {\n this.config.onError(err, 'init');\n return { ok: false, error: err.message };\n }\n }\n\n // ── PULSE (COMPUTE BEATS) ──\n\n /**\n * Compute N beats locally (VDF hash chain).\n * This is the \"heartbeat\" — proof that the agent has lived \n * through a specific window of computational time.\n */\n pulse(count?: number): Beat[] {\n const n = count || this.config.beatsPerPulse;\n \n if (!this.latestBeat) {\n throw new Error('Beat chain not initialized. Call init() first.');\n }\n\n if (this.status !== 'active') {\n throw new Error(`Cannot pulse in status '${this.status}'. Use resync() if frozen.`);\n }\n\n const newBeats: Beat[] = [];\n let prevHash = this.latestBeat.hash;\n let startIndex = this.latestBeat.index + 1;\n\n const t0 = Date.now();\n \n for (let i = 0; i < n; i++) {\n const beat = computeBeat(prevHash, startIndex + i, this.difficulty);\n newBeats.push(beat);\n prevHash = beat.hash;\n }\n\n const elapsed = Date.now() - t0;\n\n // Update state\n this.chain.push(...newBeats);\n this.latestBeat = newBeats[newBeats.length - 1];\n this.totalBeats += n;\n\n // Keep chain bounded (only last 1000 beats in memory)\n if (this.chain.length > 1000) {\n this.chain = this.chain.slice(-500);\n }\n\n this.config.onPulse(newBeats, this.totalBeats);\n this.log(`Pulse: ${n} beats in ${elapsed}ms (${(elapsed / n).toFixed(1)}ms/beat, D=${this.difficulty})`);\n\n return newBeats;\n }\n\n // ── CHECK-IN ──\n\n /**\n * Submit a Beat proof to the registry.\n * \n * \"To remain on the Whitelist, an agent must periodically \n * submit a proof of its Local Beats to the Registry.\"\n */\n async checkin(): Promise<{ ok: boolean; total_beats?: number; error?: string }> {\n if (!this.latestBeat || this.totalBeats === this.lastCheckinBeat) {\n return { ok: true, total_beats: this.totalBeats }; // Nothing new to report\n }\n\n try {\n // Build spot checks from our local chain\n // prev and nonce are required for the server to recompute VDF\n const spotChecks: { index: number; hash: string; prev: string; nonce?: string }[] = [];\n const available = this.chain.filter(b => b.index > this.lastCheckinBeat);\n const sampleCount = Math.min(5, available.length);\n\n for (let i = 0; i < sampleCount; i++) {\n const idx = Math.floor(Math.random() * available.length);\n const beat = available[idx];\n spotChecks.push({ index: beat.index, hash: beat.hash, prev: beat.prev, nonce: beat.nonce });\n available.splice(idx, 1);\n }\n\n // Find the boundary hashes\n const fromBeat = this.lastCheckinBeat;\n const toBeat = this.latestBeat.index;\n const fromHash = this.chain.find(b => b.index === fromBeat)?.hash \n || this.genesisHash;\n const toHash = this.latestBeat.hash;\n\n const res = await this.api('POST', '/api/v1/beat/checkin', {\n proof: {\n from_beat: fromBeat,\n to_beat: toBeat,\n from_hash: fromHash,\n to_hash: toHash,\n beats_computed: toBeat - fromBeat,\n global_anchor: this.globalBeat,\n spot_checks: spotChecks,\n },\n });\n\n if (res.ok) {\n this.lastCheckinBeat = toBeat;\n this.totalBeats = res.total_beats;\n this.config.onCheckin(res);\n this.log(`Check-in accepted: ${res.beats_accepted} beats, total=${res.total_beats}, global=${res.global_beat}`);\n \n if (res.status === 'warning_overdue') {\n this.config.onStatusChange('warning', { beats_behind: res.beats_behind });\n this.log(`⚠ WARNING: ${res.beats_behind} anchors behind. Check in more frequently.`);\n }\n }\n\n return { ok: res.ok, total_beats: res.total_beats };\n\n } catch (err: any) {\n this.config.onError(err, 'checkin');\n return { ok: false, error: err.message };\n }\n }\n\n // ── AUTONOMOUS HEARTBEAT ──\n\n /**\n * Start the autonomous heartbeat loop.\n * Computes beats continuously and checks in periodically.\n * This is \"keeping the agent alive\" in Beat time.\n */\n startHeartbeat(): void {\n if (this.heartbeatInterval) {\n this.log('Heartbeat already running.');\n return;\n }\n\n if (this.status !== 'active') {\n throw new Error(`Cannot start heartbeat in status '${this.status}'.`);\n }\n\n this.log('♡ Starting heartbeat...');\n\n this.heartbeatInterval = setInterval(async () => {\n try {\n // Compute a pulse\n this.pulse();\n \n // Check if it's time to report\n const beatsSinceCheckin = this.latestBeat!.index - this.lastCheckinBeat;\n const shouldCheckin = beatsSinceCheckin >= this.config.beatsPerPulse * 5;\n\n if (shouldCheckin) {\n await this.checkin();\n await this.syncGlobal(); // Stay synced with global time\n }\n } catch (err: any) {\n this.config.onError(err, 'heartbeat');\n }\n }, this.config.checkinIntervalSec * 1000 / 10); // pulse 10x per check-in interval\n }\n\n /**\n * Stop the heartbeat. Agent's time \"freezes.\"\n * Must call resync() when waking up.\n */\n stopHeartbeat(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n this.log('♡ Heartbeat stopped. Time frozen.');\n }\n }\n\n // ── RE-SYNC ──\n\n /**\n * Re-sync after being offline/frozen.\n * \n * \"When an agent powers down, its time 'freezes.' Upon waking,\n * it must perform a Re-Sync Challenge with the Registry to \n * fill the 'Temporal Gap' and re-establish its provenance.\"\n */\n async resync(): Promise<{ ok: boolean; beats_required?: number; error?: string }> {\n try {\n this.log('Requesting re-sync challenge...');\n\n // Phase 1: Get challenge\n const challenge = await this.api('POST', '/api/v1/beat/resync', {\n action: 'challenge',\n });\n\n if (!challenge.challenge) {\n return { ok: false, error: 'Failed to get challenge' };\n }\n\n const required = challenge.challenge.required_beats;\n this.difficulty = challenge.challenge.difficulty;\n this.log(`Re-sync challenge: compute ${required} beats at D=${this.difficulty}`);\n\n // Compute the required beats\n const startHash = challenge.challenge.start_from_hash;\n const startBeat = challenge.challenge.start_from_beat;\n \n // Reset chain from the known point\n this.latestBeat = { index: startBeat, hash: startHash, prev: '', timestamp: Date.now() };\n this.chain = [this.latestBeat];\n \n const t0 = Date.now();\n this.pulse(required);\n const elapsed = Date.now() - t0;\n this.log(`Re-sync beats computed in ${elapsed}ms`);\n\n // Phase 2: Submit proof\n const proof = await this.api('POST', '/api/v1/beat/resync', {\n action: 'prove',\n proof: {\n from_beat: startBeat,\n to_beat: this.latestBeat!.index,\n from_hash: startHash,\n to_hash: this.latestBeat!.hash,\n beats_computed: required,\n global_anchor: challenge.challenge.sync_to_global,\n spot_checks: this.chain\n .filter((_, i) => i % Math.ceil(required / 5) === 0)\n .slice(0, 5)\n .map(b => ({ index: b.index, hash: b.hash, prev: b.prev, nonce: b.nonce })),\n },\n });\n\n if (proof.ok) {\n this.status = 'active';\n this.totalBeats = proof.total_beats;\n this.lastCheckinBeat = this.latestBeat!.index;\n this.config.onStatusChange('active', { resynced: true });\n this.log('✓ Re-synced. Agent is alive again in Beat time.');\n }\n\n return { ok: proof.ok, beats_required: required };\n\n } catch (err: any) {\n this.config.onError(err, 'resync');\n return { ok: false, error: err.message };\n }\n }\n\n // ── SPAWN ──\n\n /**\n * Request to spawn a child agent.\n * Requires sufficient accumulated beats (Temporal Gestation).\n */\n async requestSpawn(childName?: string, childHash?: string): Promise<any> {\n try {\n const res = await this.api('POST', '/api/v1/beat/spawn', {\n child_name: childName,\n child_hash: childHash,\n });\n\n if (res.eligible === false) {\n this.log(`Gestation incomplete: ${res.progress_pct}% (need ${res.deficit} more beats)`);\n } else if (res.ok) {\n this.log(`Child spawned: ${res.child_hash?.slice(0, 16)}...`);\n }\n\n return res;\n } catch (err: any) {\n this.config.onError(err, 'spawn');\n throw err;\n }\n }\n\n // ── STATUS ──\n\n /**\n * Get this agent's full beat status from the registry.\n */\n async getStatus(): Promise<any> {\n try {\n // We need the agent hash, but we may not have it directly.\n // The status endpoint uses the hash from the API key verification.\n // For now, use the init endpoint which returns status.\n return await this.refreshState();\n } catch (err: any) {\n this.config.onError(err, 'status');\n throw err;\n }\n }\n\n /**\n * Get local state (no network call).\n */\n getLocalState(): {\n status: string;\n totalBeats: number;\n latestBeat: number;\n latestHash: string;\n difficulty: number;\n globalBeat: number;\n chainLength: number;\n } {\n return {\n status: this.status,\n totalBeats: this.totalBeats,\n latestBeat: this.latestBeat?.index || 0,\n latestHash: this.latestBeat?.hash.slice(0, 24) + '...' || '',\n difficulty: this.difficulty,\n globalBeat: this.globalBeat,\n chainLength: this.chain.length,\n };\n }\n\n // ── INTERNALS ──\n\n private async syncGlobal(): Promise<void> {\n try {\n const res = await fetch(`${this.config.registryUrl}/api/v1/beat/anchor`);\n const data: any = await res.json();\n if (data.anchor) {\n this.globalBeat = data.anchor.beat_index;\n if (data.anchor.difficulty) this.difficulty = data.anchor.difficulty;\n this.log(`Synced to global beat ${this.globalBeat} (D=${this.difficulty})`);\n }\n } catch {\n this.log('Failed to sync global anchor (offline mode continues)');\n }\n }\n\n private async refreshState(): Promise<any> {\n const res = await this.api('POST', '/api/v1/beat/init');\n if (res.already_initialized) {\n this.totalBeats = res.total_beats;\n this.genesisHash = res.genesis_hash;\n this.status = res.status as any;\n }\n return res;\n }\n\n private async api(method: string, path: string, body?: any): Promise<any> {\n const res = await fetch(`${this.config.registryUrl}${path}`, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const data: any = await res.json();\n\n if (!res.ok && !data.ok && !data.already_initialized && !data.eligible) {\n throw new Error(data.error || `API ${res.status}: ${res.statusText}`);\n }\n\n return data;\n }\n\n private log(msg: string): void {\n if (this.config.verbose) {\n console.log(`[Beat] ${msg}`);\n }\n }\n}\n\n// ============ STANDALONE VDF HELPER ============\n// For agents that want to compute beats without the full SDK\n\nexport { computeBeat };\n\n/**\n * Compute N sequential VDF beats.\n * Returns only the last beat (for lightweight usage).\n */\nexport function computeBeatsLite(\n startHash: string, \n startIndex: number, \n count: number, \n difficulty: number = 1000\n): { lastBeat: Beat; elapsed: number } {\n const t0 = Date.now();\n let prev = startHash;\n let lastBeat: Beat | null = null;\n\n for (let i = 0; i < count; i++) {\n lastBeat = computeBeat(prev, startIndex + i, difficulty);\n prev = lastBeat.hash;\n }\n\n return { lastBeat: lastBeat!, elapsed: Date.now() - t0 };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/beat-sdk.ts"],"sourcesContent":["/**\n * ═══════════════════════════════════════════════════════════\n * PROVENONCE BEAT SDK — Agent Heartbeat Client\n * ═══════════════════════════════════════════════════════════\n * \n * \"NIST tells you what time it is.\n * Provenonce tells the agent at what speed it is allowed to exist.\"\n * \n * Usage:\n * \n * import { BeatAgent } from './beat-sdk';\n * \n * const agent = new BeatAgent({\n * apiKey: 'pvn_...',\n * registryUrl: 'https://provenonce.vercel.app',\n * });\n * \n * await agent.init(); // Birth in Beat time\n * await agent.pulse(50); // Compute 50 beats\n * await agent.checkin(); // Report to registry\n * \n * // Or run the autonomous heartbeat:\n * agent.startHeartbeat(); // Computes + checks in continuously\n * // ... do your agent work ...\n * agent.stopHeartbeat();\n * \n * ═══════════════════════════════════════════════════════════\n */\n\nimport { createHash } from 'crypto';\n\n// ============ VDF ENGINE (LOCAL) ============\n\nexport interface Beat {\n index: number;\n hash: string;\n prev: string;\n timestamp: number;\n nonce?: string;\n}\n\nfunction computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string): Beat {\n const timestamp = Date.now();\n \n let current = createHash('sha256')\n .update(`${prevHash}:${beatIndex}:${nonce || ''}`)\n .digest('hex');\n\n for (let i = 0; i < difficulty; i++) {\n current = createHash('sha256')\n .update(current)\n .digest('hex');\n }\n\n return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce };\n}\n\n// ============ SDK CONFIG ============\n\nexport interface BeatAgentConfig {\n /** API key from registration (pvn_...) */\n apiKey: string;\n \n /** Provenonce registry URL */\n registryUrl: string;\n \n /** Beats to compute per pulse (default: 10) */\n beatsPerPulse?: number;\n \n /** Seconds between automatic check-ins (default: 300 = 5min) */\n checkinIntervalSec?: number;\n \n /** Callback when heartbeat ticks */\n onPulse?: (beats: Beat[], totalBeats: number) => void;\n \n /** Callback when check-in completes */\n onCheckin?: (result: any) => void;\n \n /** Callback on error */\n onError?: (error: Error, context: string) => void;\n \n /** Callback when status changes */\n onStatusChange?: (status: string, details: any) => void;\n \n /** Enable verbose logging */\n verbose?: boolean;\n}\n\n// ============ BEAT AGENT ============\n\nexport class BeatAgent {\n private config: Required<BeatAgentConfig>;\n private chain: Beat[] = [];\n private difficulty: number = 1000;\n private genesisHash: string = '';\n private latestBeat: Beat | null = null;\n private totalBeats: number = 0;\n private lastCheckinBeat: number = 0;\n private status: 'uninitialized' | 'active' | 'frozen' | 'revoked' = 'uninitialized';\n private heartbeatInterval: ReturnType<typeof setInterval> | null = null;\n private globalBeat: number = 0;\n \n constructor(config: BeatAgentConfig) {\n this.config = {\n beatsPerPulse: 10,\n checkinIntervalSec: 300,\n onPulse: () => {},\n onCheckin: () => {},\n onError: () => {},\n onStatusChange: () => {},\n verbose: false,\n ...config,\n };\n }\n\n // ── INITIALIZATION ──\n\n /**\n * Initialize the agent's Beat chain.\n * This is the agent's \"birth\" in Logical Time.\n * Must be called once before computing beats.\n */\n async init(): Promise<{ ok: boolean; genesis?: string; error?: string }> {\n try {\n this.log('Initializing Beat chain...');\n\n const res = await this.api('POST', '/api/v1/agent/init');\n\n if (res.genesis) {\n this.genesisHash = res.genesis.hash;\n this.difficulty = res.difficulty || 1000;\n this.latestBeat = {\n index: 0,\n hash: res.genesis.hash,\n prev: res.genesis.prev,\n timestamp: res.genesis.timestamp,\n };\n this.chain = [this.latestBeat];\n this.totalBeats = 0;\n this.status = 'active';\n this.config.onStatusChange('active', { genesis: this.genesisHash });\n this.log(`Born in Beat time. Genesis: ${this.genesisHash.slice(0, 16)}...`);\n } else if (res.already_initialized) {\n // Restore from existing state\n this.genesisHash = res.genesis_hash;\n this.totalBeats = res.total_beats;\n this.status = res.status as any;\n this.log(`Already initialized. Restoring state (${res.total_beats} beats).`);\n \n // Fetch full state to get latest hash\n await this.refreshState();\n }\n\n // Sync global anchor\n await this.syncGlobal();\n\n return { ok: true, genesis: this.genesisHash };\n\n } catch (err: any) {\n this.config.onError(err, 'init');\n return { ok: false, error: err.message };\n }\n }\n\n // ── PULSE (COMPUTE BEATS) ──\n\n /**\n * Compute N beats locally (VDF hash chain).\n * This is the \"heartbeat\" — proof that the agent has lived \n * through a specific window of computational time.\n */\n pulse(count?: number): Beat[] {\n const n = count || this.config.beatsPerPulse;\n \n if (!this.latestBeat) {\n throw new Error('Beat chain not initialized. Call init() first.');\n }\n\n if (this.status !== 'active') {\n throw new Error(`Cannot pulse in status '${this.status}'. Use resync() if frozen.`);\n }\n\n const newBeats: Beat[] = [];\n let prevHash = this.latestBeat.hash;\n let startIndex = this.latestBeat.index + 1;\n\n const t0 = Date.now();\n \n for (let i = 0; i < n; i++) {\n const beat = computeBeat(prevHash, startIndex + i, this.difficulty);\n newBeats.push(beat);\n prevHash = beat.hash;\n }\n\n const elapsed = Date.now() - t0;\n\n // Update state\n this.chain.push(...newBeats);\n this.latestBeat = newBeats[newBeats.length - 1];\n this.totalBeats += n;\n\n // Keep chain bounded (only last 1000 beats in memory)\n if (this.chain.length > 1000) {\n this.chain = this.chain.slice(-500);\n }\n\n this.config.onPulse(newBeats, this.totalBeats);\n this.log(`Pulse: ${n} beats in ${elapsed}ms (${(elapsed / n).toFixed(1)}ms/beat, D=${this.difficulty})`);\n\n return newBeats;\n }\n\n // ── CHECK-IN ──\n\n /**\n * Submit a Beat proof to the registry.\n * \n * \"To remain on the Whitelist, an agent must periodically \n * submit a proof of its Local Beats to the Registry.\"\n */\n async checkin(): Promise<{ ok: boolean; total_beats?: number; error?: string }> {\n if (!this.latestBeat || this.totalBeats === this.lastCheckinBeat) {\n return { ok: true, total_beats: this.totalBeats }; // Nothing new to report\n }\n\n try {\n // Build spot checks from our local chain\n // prev and nonce are required for the server to recompute VDF\n const spotChecks: { index: number; hash: string; prev: string; nonce?: string }[] = [];\n const available = this.chain.filter(b => b.index > this.lastCheckinBeat);\n const sampleCount = Math.min(5, available.length);\n\n for (let i = 0; i < sampleCount; i++) {\n const idx = Math.floor(Math.random() * available.length);\n const beat = available[idx];\n spotChecks.push({ index: beat.index, hash: beat.hash, prev: beat.prev, nonce: beat.nonce });\n available.splice(idx, 1);\n }\n\n // Find the boundary hashes\n const fromBeat = this.lastCheckinBeat;\n const toBeat = this.latestBeat.index;\n const fromHash = this.chain.find(b => b.index === fromBeat)?.hash \n || this.genesisHash;\n const toHash = this.latestBeat.hash;\n\n const res = await this.api('POST', '/api/v1/agent/checkin', {\n proof: {\n from_beat: fromBeat,\n to_beat: toBeat,\n from_hash: fromHash,\n to_hash: toHash,\n beats_computed: toBeat - fromBeat,\n global_anchor: this.globalBeat,\n spot_checks: spotChecks,\n },\n });\n\n if (res.ok) {\n this.lastCheckinBeat = toBeat;\n this.totalBeats = res.total_beats;\n this.config.onCheckin(res);\n this.log(`Check-in accepted: ${res.beats_accepted} beats, total=${res.total_beats}, global=${res.global_beat}`);\n \n if (res.status === 'warning_overdue') {\n this.config.onStatusChange('warning', { beats_behind: res.beats_behind });\n this.log(`⚠ WARNING: ${res.beats_behind} anchors behind. Check in more frequently.`);\n }\n }\n\n return { ok: res.ok, total_beats: res.total_beats };\n\n } catch (err: any) {\n this.config.onError(err, 'checkin');\n return { ok: false, error: err.message };\n }\n }\n\n // ── AUTONOMOUS HEARTBEAT ──\n\n /**\n * Start the autonomous heartbeat loop.\n * Computes beats continuously and checks in periodically.\n * This is \"keeping the agent alive\" in Beat time.\n */\n startHeartbeat(): void {\n if (this.heartbeatInterval) {\n this.log('Heartbeat already running.');\n return;\n }\n\n if (this.status !== 'active') {\n throw new Error(`Cannot start heartbeat in status '${this.status}'.`);\n }\n\n this.log('♡ Starting heartbeat...');\n\n this.heartbeatInterval = setInterval(async () => {\n try {\n // Compute a pulse\n this.pulse();\n \n // Check if it's time to report\n const beatsSinceCheckin = this.latestBeat!.index - this.lastCheckinBeat;\n const shouldCheckin = beatsSinceCheckin >= this.config.beatsPerPulse * 5;\n\n if (shouldCheckin) {\n await this.checkin();\n await this.syncGlobal(); // Stay synced with global time\n }\n } catch (err: any) {\n this.config.onError(err, 'heartbeat');\n }\n }, this.config.checkinIntervalSec * 1000 / 10); // pulse 10x per check-in interval\n }\n\n /**\n * Stop the heartbeat. Agent's time \"freezes.\"\n * Must call resync() when waking up.\n */\n stopHeartbeat(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n this.log('♡ Heartbeat stopped. Time frozen.');\n }\n }\n\n // ── RE-SYNC ──\n\n /**\n * Re-sync after being offline/frozen.\n * \n * \"When an agent powers down, its time 'freezes.' Upon waking,\n * it must perform a Re-Sync Challenge with the Registry to \n * fill the 'Temporal Gap' and re-establish its provenance.\"\n */\n async resync(): Promise<{ ok: boolean; beats_required?: number; error?: string }> {\n try {\n this.log('Requesting re-sync challenge...');\n\n // Phase 1: Get challenge\n const challenge = await this.api('POST', '/api/v1/agent/resync', {\n action: 'challenge',\n });\n\n if (!challenge.challenge) {\n return { ok: false, error: 'Failed to get challenge' };\n }\n\n const required = challenge.challenge.required_beats;\n this.difficulty = challenge.challenge.difficulty;\n this.log(`Re-sync challenge: compute ${required} beats at D=${this.difficulty}`);\n\n // Compute the required beats\n const startHash = challenge.challenge.start_from_hash;\n const startBeat = challenge.challenge.start_from_beat;\n \n // Reset chain from the known point\n this.latestBeat = { index: startBeat, hash: startHash, prev: '', timestamp: Date.now() };\n this.chain = [this.latestBeat];\n \n const t0 = Date.now();\n this.pulse(required);\n const elapsed = Date.now() - t0;\n this.log(`Re-sync beats computed in ${elapsed}ms`);\n\n // Phase 2: Submit proof\n const proof = await this.api('POST', '/api/v1/agent/resync', {\n action: 'prove',\n proof: {\n from_beat: startBeat,\n to_beat: this.latestBeat!.index,\n from_hash: startHash,\n to_hash: this.latestBeat!.hash,\n beats_computed: required,\n global_anchor: challenge.challenge.sync_to_global,\n spot_checks: this.chain\n .filter((_, i) => i % Math.ceil(required / 5) === 0)\n .slice(0, 5)\n .map(b => ({ index: b.index, hash: b.hash, prev: b.prev, nonce: b.nonce })),\n },\n });\n\n if (proof.ok) {\n this.status = 'active';\n this.totalBeats = proof.total_beats;\n this.lastCheckinBeat = this.latestBeat!.index;\n this.config.onStatusChange('active', { resynced: true });\n this.log('✓ Re-synced. Agent is alive again in Beat time.');\n }\n\n return { ok: proof.ok, beats_required: required };\n\n } catch (err: any) {\n this.config.onError(err, 'resync');\n return { ok: false, error: err.message };\n }\n }\n\n // ── SPAWN ──\n\n /**\n * Request to spawn a child agent.\n * Requires sufficient accumulated beats (Temporal Gestation).\n */\n async requestSpawn(childName?: string, childHash?: string): Promise<any> {\n try {\n const res = await this.api('POST', '/api/v1/agent/spawn', {\n child_name: childName,\n child_hash: childHash,\n });\n\n if (res.eligible === false) {\n this.log(`Gestation incomplete: ${res.progress_pct}% (need ${res.deficit} more beats)`);\n } else if (res.ok) {\n this.log(`Child spawned: ${res.child_hash?.slice(0, 16)}...`);\n }\n\n return res;\n } catch (err: any) {\n this.config.onError(err, 'spawn');\n throw err;\n }\n }\n\n // ── STATUS ──\n\n /**\n * Get this agent's full beat status from the registry.\n */\n async getStatus(): Promise<any> {\n try {\n // We need the agent hash, but we may not have it directly.\n // The status endpoint uses the hash from the API key verification.\n // For now, use the init endpoint which returns status.\n return await this.refreshState();\n } catch (err: any) {\n this.config.onError(err, 'status');\n throw err;\n }\n }\n\n /**\n * Get local state (no network call).\n */\n getLocalState(): {\n status: string;\n totalBeats: number;\n latestBeat: number;\n latestHash: string;\n difficulty: number;\n globalBeat: number;\n chainLength: number;\n } {\n return {\n status: this.status,\n totalBeats: this.totalBeats,\n latestBeat: this.latestBeat?.index || 0,\n latestHash: this.latestBeat?.hash.slice(0, 24) + '...' || '',\n difficulty: this.difficulty,\n globalBeat: this.globalBeat,\n chainLength: this.chain.length,\n };\n }\n\n // ── INTERNALS ──\n\n private async syncGlobal(): Promise<void> {\n try {\n const res = await fetch(`${this.config.registryUrl}/api/v1/beat/anchor`);\n const data: any = await res.json();\n if (data.anchor) {\n this.globalBeat = data.anchor.beat_index;\n if (data.anchor.difficulty) this.difficulty = data.anchor.difficulty;\n this.log(`Synced to global beat ${this.globalBeat} (D=${this.difficulty})`);\n }\n } catch {\n this.log('Failed to sync global anchor (offline mode continues)');\n }\n }\n\n private async refreshState(): Promise<any> {\n const res = await this.api('POST', '/api/v1/agent/init');\n if (res.already_initialized) {\n this.totalBeats = res.total_beats;\n this.genesisHash = res.genesis_hash;\n this.status = res.status as any;\n }\n return res;\n }\n\n private async api(method: string, path: string, body?: any): Promise<any> {\n const res = await fetch(`${this.config.registryUrl}${path}`, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n\n const data: any = await res.json();\n\n if (!res.ok && !data.ok && !data.already_initialized && !data.eligible) {\n throw new Error(data.error || `API ${res.status}: ${res.statusText}`);\n }\n\n return data;\n }\n\n private log(msg: string): void {\n if (this.config.verbose) {\n console.log(`[Beat] ${msg}`);\n }\n }\n}\n\n// ============ STANDALONE VDF HELPER ============\n// For agents that want to compute beats without the full SDK\n\nexport { computeBeat };\n\n/**\n * Compute N sequential VDF beats.\n * Returns only the last beat (for lightweight usage).\n */\nexport function computeBeatsLite(\n startHash: string, \n startIndex: number, \n count: number, \n difficulty: number = 1000\n): { lastBeat: Beat; elapsed: number } {\n const t0 = Date.now();\n let prev = startHash;\n let lastBeat: Beat | null = null;\n\n for (let i = 0; i < count; i++) {\n lastBeat = computeBeat(prev, startIndex + i, difficulty);\n prev = lastBeat.hash;\n }\n\n return { lastBeat: lastBeat!, elapsed: Date.now() - t0 };\n}\n"],"mappings":"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","names":[]}
|