@provenonce/sdk 0.4.2 → 0.6.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 +25 -0
- package/dist/index.d.mts +75 -7
- package/dist/index.d.ts +75 -7
- package/dist/index.js +43 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +41 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,31 @@ Agent heartbeat client for sovereign time authentication on Solana.
|
|
|
8
8
|
npm install @provenonce/sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Registration
|
|
12
|
+
|
|
13
|
+
Before using the SDK, register your agent to get an API key:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { register } from '@provenonce/sdk';
|
|
17
|
+
|
|
18
|
+
// Root registration (one-time)
|
|
19
|
+
const creds = await register('my-agent-v1', {
|
|
20
|
+
registryUrl: 'https://provenonce.vercel.app',
|
|
21
|
+
registrationSecret: process.env.REGISTRATION_SECRET, // required in production
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
console.log(creds.hash); // unique agent identity
|
|
25
|
+
console.log(creds.api_key); // use this for BeatAgent
|
|
26
|
+
console.log(creds.secret); // save — shown only once
|
|
27
|
+
|
|
28
|
+
// Child registration (requires parent credentials)
|
|
29
|
+
const child = await register('worker-1', {
|
|
30
|
+
registryUrl: 'https://provenonce.vercel.app',
|
|
31
|
+
parentHash: creds.hash,
|
|
32
|
+
parentApiKey: creds.api_key,
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
11
36
|
## Quick Start
|
|
12
37
|
|
|
13
38
|
```typescript
|
package/dist/index.d.mts
CHANGED
|
@@ -32,8 +32,75 @@ interface Beat {
|
|
|
32
32
|
prev: string;
|
|
33
33
|
timestamp: number;
|
|
34
34
|
nonce?: string;
|
|
35
|
+
anchor_hash?: string;
|
|
35
36
|
}
|
|
36
|
-
declare function computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string): Beat;
|
|
37
|
+
declare function computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string, anchorHash?: string): Beat;
|
|
38
|
+
/** Result from a check-in submission */
|
|
39
|
+
interface CheckinResult {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
total_beats: number;
|
|
42
|
+
beats_accepted: number;
|
|
43
|
+
global_beat: number;
|
|
44
|
+
status?: string;
|
|
45
|
+
beats_behind?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Result from a spawn request */
|
|
48
|
+
interface SpawnResult {
|
|
49
|
+
ok: boolean;
|
|
50
|
+
eligible: boolean;
|
|
51
|
+
child_hash?: string;
|
|
52
|
+
progress_pct?: number;
|
|
53
|
+
deficit?: number;
|
|
54
|
+
}
|
|
55
|
+
/** Agent status from the registry */
|
|
56
|
+
interface AgentStatus {
|
|
57
|
+
already_initialized: boolean;
|
|
58
|
+
total_beats: number;
|
|
59
|
+
genesis_hash: string;
|
|
60
|
+
status: string;
|
|
61
|
+
genesis?: {
|
|
62
|
+
hash: string;
|
|
63
|
+
prev: string;
|
|
64
|
+
timestamp: number;
|
|
65
|
+
};
|
|
66
|
+
difficulty?: number;
|
|
67
|
+
}
|
|
68
|
+
/** Result from registering an agent */
|
|
69
|
+
interface RegistrationResult {
|
|
70
|
+
hash: string;
|
|
71
|
+
api_key: string;
|
|
72
|
+
secret: string;
|
|
73
|
+
type: 'root' | 'agent';
|
|
74
|
+
parent: string | null;
|
|
75
|
+
depth: number;
|
|
76
|
+
name: string;
|
|
77
|
+
signature: string;
|
|
78
|
+
explorer_url?: string;
|
|
79
|
+
beat?: {
|
|
80
|
+
genesis_hash: string;
|
|
81
|
+
difficulty: number;
|
|
82
|
+
status: string;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Register a new agent on the Provenonce registry.
|
|
87
|
+
*
|
|
88
|
+
* Root registration (no parent):
|
|
89
|
+
* const creds = await register('my-org', { registryUrl: '...' });
|
|
90
|
+
*
|
|
91
|
+
* Child registration:
|
|
92
|
+
* const creds = await register('worker-1', {
|
|
93
|
+
* registryUrl: '...',
|
|
94
|
+
* parentHash: parentCreds.hash,
|
|
95
|
+
* parentApiKey: parentCreds.api_key,
|
|
96
|
+
* });
|
|
97
|
+
*/
|
|
98
|
+
declare function register(name: string, options?: {
|
|
99
|
+
registryUrl?: string;
|
|
100
|
+
parentHash?: string;
|
|
101
|
+
parentApiKey?: string;
|
|
102
|
+
registrationSecret?: string;
|
|
103
|
+
}): Promise<RegistrationResult>;
|
|
37
104
|
interface BeatAgentConfig {
|
|
38
105
|
/** API key from registration (pvn_...) */
|
|
39
106
|
apiKey: string;
|
|
@@ -46,11 +113,11 @@ interface BeatAgentConfig {
|
|
|
46
113
|
/** Callback when heartbeat ticks */
|
|
47
114
|
onPulse?: (beats: Beat[], totalBeats: number) => void;
|
|
48
115
|
/** Callback when check-in completes */
|
|
49
|
-
onCheckin?: (result:
|
|
116
|
+
onCheckin?: (result: CheckinResult) => void;
|
|
50
117
|
/** Callback on error */
|
|
51
118
|
onError?: (error: Error, context: string) => void;
|
|
52
119
|
/** Callback when status changes */
|
|
53
|
-
onStatusChange?: (status: string, details:
|
|
120
|
+
onStatusChange?: (status: string, details: Record<string, unknown>) => void;
|
|
54
121
|
/** Enable verbose logging */
|
|
55
122
|
verbose?: boolean;
|
|
56
123
|
}
|
|
@@ -65,6 +132,7 @@ declare class BeatAgent {
|
|
|
65
132
|
private status;
|
|
66
133
|
private heartbeatInterval;
|
|
67
134
|
private globalBeat;
|
|
135
|
+
private globalAnchorHash;
|
|
68
136
|
constructor(config: BeatAgentConfig);
|
|
69
137
|
/**
|
|
70
138
|
* Initialize the agent's Beat chain.
|
|
@@ -120,11 +188,11 @@ declare class BeatAgent {
|
|
|
120
188
|
* Request to spawn a child agent.
|
|
121
189
|
* Requires sufficient accumulated beats (Temporal Gestation).
|
|
122
190
|
*/
|
|
123
|
-
requestSpawn(childName?: string, childHash?: string): Promise<
|
|
191
|
+
requestSpawn(childName?: string, childHash?: string): Promise<SpawnResult>;
|
|
124
192
|
/**
|
|
125
193
|
* Get this agent's full beat status from the registry.
|
|
126
194
|
*/
|
|
127
|
-
getStatus(): Promise<
|
|
195
|
+
getStatus(): Promise<AgentStatus>;
|
|
128
196
|
/**
|
|
129
197
|
* Get local state (no network call).
|
|
130
198
|
*/
|
|
@@ -147,9 +215,9 @@ declare class BeatAgent {
|
|
|
147
215
|
* Compute N sequential VDF beats.
|
|
148
216
|
* Returns only the last beat (for lightweight usage).
|
|
149
217
|
*/
|
|
150
|
-
declare function computeBeatsLite(startHash: string, startIndex: number, count: number, difficulty?: number): {
|
|
218
|
+
declare function computeBeatsLite(startHash: string, startIndex: number, count: number, difficulty?: number, anchorHash?: string): {
|
|
151
219
|
lastBeat: Beat;
|
|
152
220
|
elapsed: number;
|
|
153
221
|
};
|
|
154
222
|
|
|
155
|
-
export { type Beat, BeatAgent, type BeatAgentConfig, computeBeat, computeBeatsLite };
|
|
223
|
+
export { type AgentStatus, type Beat, BeatAgent, type BeatAgentConfig, type CheckinResult, type RegistrationResult, type SpawnResult, computeBeat, computeBeatsLite, register };
|
package/dist/index.d.ts
CHANGED
|
@@ -32,8 +32,75 @@ interface Beat {
|
|
|
32
32
|
prev: string;
|
|
33
33
|
timestamp: number;
|
|
34
34
|
nonce?: string;
|
|
35
|
+
anchor_hash?: string;
|
|
35
36
|
}
|
|
36
|
-
declare function computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string): Beat;
|
|
37
|
+
declare function computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string, anchorHash?: string): Beat;
|
|
38
|
+
/** Result from a check-in submission */
|
|
39
|
+
interface CheckinResult {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
total_beats: number;
|
|
42
|
+
beats_accepted: number;
|
|
43
|
+
global_beat: number;
|
|
44
|
+
status?: string;
|
|
45
|
+
beats_behind?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Result from a spawn request */
|
|
48
|
+
interface SpawnResult {
|
|
49
|
+
ok: boolean;
|
|
50
|
+
eligible: boolean;
|
|
51
|
+
child_hash?: string;
|
|
52
|
+
progress_pct?: number;
|
|
53
|
+
deficit?: number;
|
|
54
|
+
}
|
|
55
|
+
/** Agent status from the registry */
|
|
56
|
+
interface AgentStatus {
|
|
57
|
+
already_initialized: boolean;
|
|
58
|
+
total_beats: number;
|
|
59
|
+
genesis_hash: string;
|
|
60
|
+
status: string;
|
|
61
|
+
genesis?: {
|
|
62
|
+
hash: string;
|
|
63
|
+
prev: string;
|
|
64
|
+
timestamp: number;
|
|
65
|
+
};
|
|
66
|
+
difficulty?: number;
|
|
67
|
+
}
|
|
68
|
+
/** Result from registering an agent */
|
|
69
|
+
interface RegistrationResult {
|
|
70
|
+
hash: string;
|
|
71
|
+
api_key: string;
|
|
72
|
+
secret: string;
|
|
73
|
+
type: 'root' | 'agent';
|
|
74
|
+
parent: string | null;
|
|
75
|
+
depth: number;
|
|
76
|
+
name: string;
|
|
77
|
+
signature: string;
|
|
78
|
+
explorer_url?: string;
|
|
79
|
+
beat?: {
|
|
80
|
+
genesis_hash: string;
|
|
81
|
+
difficulty: number;
|
|
82
|
+
status: string;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Register a new agent on the Provenonce registry.
|
|
87
|
+
*
|
|
88
|
+
* Root registration (no parent):
|
|
89
|
+
* const creds = await register('my-org', { registryUrl: '...' });
|
|
90
|
+
*
|
|
91
|
+
* Child registration:
|
|
92
|
+
* const creds = await register('worker-1', {
|
|
93
|
+
* registryUrl: '...',
|
|
94
|
+
* parentHash: parentCreds.hash,
|
|
95
|
+
* parentApiKey: parentCreds.api_key,
|
|
96
|
+
* });
|
|
97
|
+
*/
|
|
98
|
+
declare function register(name: string, options?: {
|
|
99
|
+
registryUrl?: string;
|
|
100
|
+
parentHash?: string;
|
|
101
|
+
parentApiKey?: string;
|
|
102
|
+
registrationSecret?: string;
|
|
103
|
+
}): Promise<RegistrationResult>;
|
|
37
104
|
interface BeatAgentConfig {
|
|
38
105
|
/** API key from registration (pvn_...) */
|
|
39
106
|
apiKey: string;
|
|
@@ -46,11 +113,11 @@ interface BeatAgentConfig {
|
|
|
46
113
|
/** Callback when heartbeat ticks */
|
|
47
114
|
onPulse?: (beats: Beat[], totalBeats: number) => void;
|
|
48
115
|
/** Callback when check-in completes */
|
|
49
|
-
onCheckin?: (result:
|
|
116
|
+
onCheckin?: (result: CheckinResult) => void;
|
|
50
117
|
/** Callback on error */
|
|
51
118
|
onError?: (error: Error, context: string) => void;
|
|
52
119
|
/** Callback when status changes */
|
|
53
|
-
onStatusChange?: (status: string, details:
|
|
120
|
+
onStatusChange?: (status: string, details: Record<string, unknown>) => void;
|
|
54
121
|
/** Enable verbose logging */
|
|
55
122
|
verbose?: boolean;
|
|
56
123
|
}
|
|
@@ -65,6 +132,7 @@ declare class BeatAgent {
|
|
|
65
132
|
private status;
|
|
66
133
|
private heartbeatInterval;
|
|
67
134
|
private globalBeat;
|
|
135
|
+
private globalAnchorHash;
|
|
68
136
|
constructor(config: BeatAgentConfig);
|
|
69
137
|
/**
|
|
70
138
|
* Initialize the agent's Beat chain.
|
|
@@ -120,11 +188,11 @@ declare class BeatAgent {
|
|
|
120
188
|
* Request to spawn a child agent.
|
|
121
189
|
* Requires sufficient accumulated beats (Temporal Gestation).
|
|
122
190
|
*/
|
|
123
|
-
requestSpawn(childName?: string, childHash?: string): Promise<
|
|
191
|
+
requestSpawn(childName?: string, childHash?: string): Promise<SpawnResult>;
|
|
124
192
|
/**
|
|
125
193
|
* Get this agent's full beat status from the registry.
|
|
126
194
|
*/
|
|
127
|
-
getStatus(): Promise<
|
|
195
|
+
getStatus(): Promise<AgentStatus>;
|
|
128
196
|
/**
|
|
129
197
|
* Get local state (no network call).
|
|
130
198
|
*/
|
|
@@ -147,9 +215,9 @@ declare class BeatAgent {
|
|
|
147
215
|
* Compute N sequential VDF beats.
|
|
148
216
|
* Returns only the last beat (for lightweight usage).
|
|
149
217
|
*/
|
|
150
|
-
declare function computeBeatsLite(startHash: string, startIndex: number, count: number, difficulty?: number): {
|
|
218
|
+
declare function computeBeatsLite(startHash: string, startIndex: number, count: number, difficulty?: number, anchorHash?: string): {
|
|
151
219
|
lastBeat: Beat;
|
|
152
220
|
elapsed: number;
|
|
153
221
|
};
|
|
154
222
|
|
|
155
|
-
export { type Beat, BeatAgent, type BeatAgentConfig, computeBeat, computeBeatsLite };
|
|
223
|
+
export { type AgentStatus, type Beat, BeatAgent, type BeatAgentConfig, type CheckinResult, type RegistrationResult, type SpawnResult, computeBeat, computeBeatsLite, register };
|
package/dist/index.js
CHANGED
|
@@ -22,19 +22,43 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
BeatAgent: () => BeatAgent,
|
|
24
24
|
computeBeat: () => computeBeat,
|
|
25
|
-
computeBeatsLite: () => computeBeatsLite
|
|
25
|
+
computeBeatsLite: () => computeBeatsLite,
|
|
26
|
+
register: () => register
|
|
26
27
|
});
|
|
27
28
|
module.exports = __toCommonJS(index_exports);
|
|
28
29
|
|
|
29
30
|
// src/beat-sdk.ts
|
|
30
31
|
var import_crypto = require("crypto");
|
|
31
|
-
function computeBeat(prevHash, beatIndex, difficulty, nonce) {
|
|
32
|
+
function computeBeat(prevHash, beatIndex, difficulty, nonce, anchorHash) {
|
|
32
33
|
const timestamp = Date.now();
|
|
33
|
-
|
|
34
|
+
const seed = anchorHash ? `${prevHash}:${beatIndex}:${nonce || ""}:${anchorHash}` : `${prevHash}:${beatIndex}:${nonce || ""}`;
|
|
35
|
+
let current = (0, import_crypto.createHash)("sha256").update(seed).digest("hex");
|
|
34
36
|
for (let i = 0; i < difficulty; i++) {
|
|
35
37
|
current = (0, import_crypto.createHash)("sha256").update(current).digest("hex");
|
|
36
38
|
}
|
|
37
|
-
return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce };
|
|
39
|
+
return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce, anchor_hash: anchorHash };
|
|
40
|
+
}
|
|
41
|
+
async function register(name, options) {
|
|
42
|
+
const url = options?.registryUrl || "https://provenonce.vercel.app";
|
|
43
|
+
const body = { name };
|
|
44
|
+
const headers = { "Content-Type": "application/json" };
|
|
45
|
+
if (options?.parentHash) {
|
|
46
|
+
body.parent = options.parentHash;
|
|
47
|
+
}
|
|
48
|
+
if (options?.parentApiKey) {
|
|
49
|
+
headers["Authorization"] = `Bearer ${options.parentApiKey}`;
|
|
50
|
+
}
|
|
51
|
+
if (options?.registrationSecret) {
|
|
52
|
+
headers["x-registration-secret"] = options.registrationSecret;
|
|
53
|
+
}
|
|
54
|
+
const res = await fetch(`${url}/api/v1/register`, {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers,
|
|
57
|
+
body: JSON.stringify(body)
|
|
58
|
+
});
|
|
59
|
+
const data = await res.json();
|
|
60
|
+
if (!res.ok) throw new Error(data.error || "Registration failed");
|
|
61
|
+
return data;
|
|
38
62
|
}
|
|
39
63
|
var BeatAgent = class {
|
|
40
64
|
constructor(config) {
|
|
@@ -47,6 +71,7 @@ var BeatAgent = class {
|
|
|
47
71
|
this.status = "uninitialized";
|
|
48
72
|
this.heartbeatInterval = null;
|
|
49
73
|
this.globalBeat = 0;
|
|
74
|
+
this.globalAnchorHash = "";
|
|
50
75
|
this.config = {
|
|
51
76
|
beatsPerPulse: 10,
|
|
52
77
|
checkinIntervalSec: 300,
|
|
@@ -71,7 +96,7 @@ var BeatAgent = class {
|
|
|
71
96
|
async init() {
|
|
72
97
|
try {
|
|
73
98
|
this.log("Initializing Beat chain...");
|
|
74
|
-
const res = await this.api("POST", "/api/v1/
|
|
99
|
+
const res = await this.api("POST", "/api/v1/agent/init");
|
|
75
100
|
if (res.genesis) {
|
|
76
101
|
this.genesisHash = res.genesis.hash;
|
|
77
102
|
this.difficulty = res.difficulty || 1e3;
|
|
@@ -119,7 +144,7 @@ var BeatAgent = class {
|
|
|
119
144
|
let startIndex = this.latestBeat.index + 1;
|
|
120
145
|
const t0 = Date.now();
|
|
121
146
|
for (let i = 0; i < n; i++) {
|
|
122
|
-
const beat = computeBeat(prevHash, startIndex + i, this.difficulty);
|
|
147
|
+
const beat = computeBeat(prevHash, startIndex + i, this.difficulty, void 0, this.globalAnchorHash || void 0);
|
|
123
148
|
newBeats.push(beat);
|
|
124
149
|
prevHash = beat.hash;
|
|
125
150
|
}
|
|
@@ -159,7 +184,7 @@ var BeatAgent = class {
|
|
|
159
184
|
const toBeat = this.latestBeat.index;
|
|
160
185
|
const fromHash = this.chain.find((b) => b.index === fromBeat)?.hash || this.genesisHash;
|
|
161
186
|
const toHash = this.latestBeat.hash;
|
|
162
|
-
const res = await this.api("POST", "/api/v1/
|
|
187
|
+
const res = await this.api("POST", "/api/v1/agent/checkin", {
|
|
163
188
|
proof: {
|
|
164
189
|
from_beat: fromBeat,
|
|
165
190
|
to_beat: toBeat,
|
|
@@ -167,6 +192,7 @@ var BeatAgent = class {
|
|
|
167
192
|
to_hash: toHash,
|
|
168
193
|
beats_computed: toBeat - fromBeat,
|
|
169
194
|
global_anchor: this.globalBeat,
|
|
195
|
+
anchor_hash: this.globalAnchorHash || void 0,
|
|
170
196
|
spot_checks: spotChecks
|
|
171
197
|
}
|
|
172
198
|
});
|
|
@@ -237,7 +263,7 @@ var BeatAgent = class {
|
|
|
237
263
|
async resync() {
|
|
238
264
|
try {
|
|
239
265
|
this.log("Requesting re-sync challenge...");
|
|
240
|
-
const challenge = await this.api("POST", "/api/v1/
|
|
266
|
+
const challenge = await this.api("POST", "/api/v1/agent/resync", {
|
|
241
267
|
action: "challenge"
|
|
242
268
|
});
|
|
243
269
|
if (!challenge.challenge) {
|
|
@@ -254,8 +280,9 @@ var BeatAgent = class {
|
|
|
254
280
|
this.pulse(required);
|
|
255
281
|
const elapsed = Date.now() - t0;
|
|
256
282
|
this.log(`Re-sync beats computed in ${elapsed}ms`);
|
|
257
|
-
const proof = await this.api("POST", "/api/v1/
|
|
283
|
+
const proof = await this.api("POST", "/api/v1/agent/resync", {
|
|
258
284
|
action: "prove",
|
|
285
|
+
challenge_nonce: challenge.challenge.nonce,
|
|
259
286
|
proof: {
|
|
260
287
|
from_beat: startBeat,
|
|
261
288
|
to_beat: this.latestBeat.index,
|
|
@@ -286,7 +313,7 @@ var BeatAgent = class {
|
|
|
286
313
|
*/
|
|
287
314
|
async requestSpawn(childName, childHash) {
|
|
288
315
|
try {
|
|
289
|
-
const res = await this.api("POST", "/api/v1/
|
|
316
|
+
const res = await this.api("POST", "/api/v1/agent/spawn", {
|
|
290
317
|
child_name: childName,
|
|
291
318
|
child_hash: childHash
|
|
292
319
|
});
|
|
@@ -334,6 +361,7 @@ var BeatAgent = class {
|
|
|
334
361
|
const data = await res.json();
|
|
335
362
|
if (data.anchor) {
|
|
336
363
|
this.globalBeat = data.anchor.beat_index;
|
|
364
|
+
this.globalAnchorHash = data.anchor.hash || "";
|
|
337
365
|
if (data.anchor.difficulty) this.difficulty = data.anchor.difficulty;
|
|
338
366
|
this.log(`Synced to global beat ${this.globalBeat} (D=${this.difficulty})`);
|
|
339
367
|
}
|
|
@@ -342,7 +370,7 @@ var BeatAgent = class {
|
|
|
342
370
|
}
|
|
343
371
|
}
|
|
344
372
|
async refreshState() {
|
|
345
|
-
const res = await this.api("POST", "/api/v1/
|
|
373
|
+
const res = await this.api("POST", "/api/v1/agent/init");
|
|
346
374
|
if (res.already_initialized) {
|
|
347
375
|
this.totalBeats = res.total_beats;
|
|
348
376
|
this.genesisHash = res.genesis_hash;
|
|
@@ -371,12 +399,12 @@ var BeatAgent = class {
|
|
|
371
399
|
}
|
|
372
400
|
}
|
|
373
401
|
};
|
|
374
|
-
function computeBeatsLite(startHash, startIndex, count, difficulty = 1e3) {
|
|
402
|
+
function computeBeatsLite(startHash, startIndex, count, difficulty = 1e3, anchorHash) {
|
|
375
403
|
const t0 = Date.now();
|
|
376
404
|
let prev = startHash;
|
|
377
405
|
let lastBeat = null;
|
|
378
406
|
for (let i = 0; i < count; i++) {
|
|
379
|
-
lastBeat = computeBeat(prev, startIndex + i, difficulty);
|
|
407
|
+
lastBeat = computeBeat(prev, startIndex + i, difficulty, void 0, anchorHash);
|
|
380
408
|
prev = lastBeat.hash;
|
|
381
409
|
}
|
|
382
410
|
return { lastBeat, elapsed: Date.now() - t0 };
|
|
@@ -385,6 +413,7 @@ function computeBeatsLite(startHash, startIndex, count, difficulty = 1e3) {
|
|
|
385
413
|
0 && (module.exports = {
|
|
386
414
|
BeatAgent,
|
|
387
415
|
computeBeat,
|
|
388
|
-
computeBeatsLite
|
|
416
|
+
computeBeatsLite,
|
|
417
|
+
register
|
|
389
418
|
});
|
|
390
419
|
//# sourceMappingURL=index.js.map
|
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":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/beat-sdk.ts"],"sourcesContent":["export { BeatAgent, computeBeat, computeBeatsLite, register } from './beat-sdk';\nexport type { BeatAgentConfig, Beat, CheckinResult, SpawnResult, AgentStatus, RegistrationResult } 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 anchor_hash?: string;\n}\n\nfunction computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string, anchorHash?: string): Beat {\n const timestamp = Date.now();\n\n const seed = anchorHash\n ? `${prevHash}:${beatIndex}:${nonce || ''}:${anchorHash}`\n : `${prevHash}:${beatIndex}:${nonce || ''}`;\n\n let current = createHash('sha256')\n .update(seed)\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, anchor_hash: anchorHash };\n}\n\n// ============ SDK RESULT TYPES ============\n\n/** Result from a check-in submission */\nexport interface CheckinResult {\n ok: boolean;\n total_beats: number;\n beats_accepted: number;\n global_beat: number;\n status?: string;\n beats_behind?: number;\n}\n\n/** Result from a spawn request */\nexport interface SpawnResult {\n ok: boolean;\n eligible: boolean;\n child_hash?: string;\n progress_pct?: number;\n deficit?: number;\n}\n\n/** Agent status from the registry */\nexport interface AgentStatus {\n already_initialized: boolean;\n total_beats: number;\n genesis_hash: string;\n status: string;\n genesis?: { hash: string; prev: string; timestamp: number };\n difficulty?: number;\n}\n\n// ============ REGISTRATION ============\n\n/** Result from registering an agent */\nexport interface RegistrationResult {\n hash: string;\n api_key: string;\n secret: string;\n type: 'root' | 'agent';\n parent: string | null;\n depth: number;\n name: string;\n signature: string;\n explorer_url?: string;\n beat?: { genesis_hash: string; difficulty: number; status: string };\n}\n\n/**\n * Register a new agent on the Provenonce registry.\n *\n * Root registration (no parent):\n * const creds = await register('my-org', { registryUrl: '...' });\n *\n * Child registration:\n * const creds = await register('worker-1', {\n * registryUrl: '...',\n * parentHash: parentCreds.hash,\n * parentApiKey: parentCreds.api_key,\n * });\n */\nexport async function register(\n name: string,\n options?: {\n registryUrl?: string;\n parentHash?: string;\n parentApiKey?: string;\n registrationSecret?: string;\n },\n): Promise<RegistrationResult> {\n const url = options?.registryUrl || 'https://provenonce.vercel.app';\n const body: Record<string, string> = { name };\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\n if (options?.parentHash) {\n body.parent = options.parentHash;\n }\n if (options?.parentApiKey) {\n headers['Authorization'] = `Bearer ${options.parentApiKey}`;\n }\n if (options?.registrationSecret) {\n headers['x-registration-secret'] = options.registrationSecret;\n }\n\n const res = await fetch(`${url}/api/v1/register`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n const data = await res.json() as RegistrationResult & { error?: string };\n if (!res.ok) throw new Error(data.error || 'Registration failed');\n return data;\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: CheckinResult) => void;\n\n /** Callback on error */\n onError?: (error: Error, context: string) => void;\n\n /** Callback when status changes */\n onStatusChange?: (status: string, details: Record<string, unknown>) => 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 private globalAnchorHash: string = '';\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, undefined, this.globalAnchorHash || undefined);\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 anchor_hash: this.globalAnchorHash || undefined,\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 (include challenge_nonce for server verification)\n const proof = await this.api('POST', '/api/v1/agent/resync', {\n action: 'prove',\n challenge_nonce: challenge.challenge.nonce,\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<SpawnResult> {\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<AgentStatus> {\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 this.globalAnchorHash = data.anchor.hash || '';\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 anchorHash?: string,\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, undefined, anchorHash);\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
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
// src/beat-sdk.ts
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
|
-
function computeBeat(prevHash, beatIndex, difficulty, nonce) {
|
|
3
|
+
function computeBeat(prevHash, beatIndex, difficulty, nonce, anchorHash) {
|
|
4
4
|
const timestamp = Date.now();
|
|
5
|
-
|
|
5
|
+
const seed = anchorHash ? `${prevHash}:${beatIndex}:${nonce || ""}:${anchorHash}` : `${prevHash}:${beatIndex}:${nonce || ""}`;
|
|
6
|
+
let current = createHash("sha256").update(seed).digest("hex");
|
|
6
7
|
for (let i = 0; i < difficulty; i++) {
|
|
7
8
|
current = createHash("sha256").update(current).digest("hex");
|
|
8
9
|
}
|
|
9
|
-
return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce };
|
|
10
|
+
return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce, anchor_hash: anchorHash };
|
|
11
|
+
}
|
|
12
|
+
async function register(name, options) {
|
|
13
|
+
const url = options?.registryUrl || "https://provenonce.vercel.app";
|
|
14
|
+
const body = { name };
|
|
15
|
+
const headers = { "Content-Type": "application/json" };
|
|
16
|
+
if (options?.parentHash) {
|
|
17
|
+
body.parent = options.parentHash;
|
|
18
|
+
}
|
|
19
|
+
if (options?.parentApiKey) {
|
|
20
|
+
headers["Authorization"] = `Bearer ${options.parentApiKey}`;
|
|
21
|
+
}
|
|
22
|
+
if (options?.registrationSecret) {
|
|
23
|
+
headers["x-registration-secret"] = options.registrationSecret;
|
|
24
|
+
}
|
|
25
|
+
const res = await fetch(`${url}/api/v1/register`, {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers,
|
|
28
|
+
body: JSON.stringify(body)
|
|
29
|
+
});
|
|
30
|
+
const data = await res.json();
|
|
31
|
+
if (!res.ok) throw new Error(data.error || "Registration failed");
|
|
32
|
+
return data;
|
|
10
33
|
}
|
|
11
34
|
var BeatAgent = class {
|
|
12
35
|
constructor(config) {
|
|
@@ -19,6 +42,7 @@ var BeatAgent = class {
|
|
|
19
42
|
this.status = "uninitialized";
|
|
20
43
|
this.heartbeatInterval = null;
|
|
21
44
|
this.globalBeat = 0;
|
|
45
|
+
this.globalAnchorHash = "";
|
|
22
46
|
this.config = {
|
|
23
47
|
beatsPerPulse: 10,
|
|
24
48
|
checkinIntervalSec: 300,
|
|
@@ -43,7 +67,7 @@ var BeatAgent = class {
|
|
|
43
67
|
async init() {
|
|
44
68
|
try {
|
|
45
69
|
this.log("Initializing Beat chain...");
|
|
46
|
-
const res = await this.api("POST", "/api/v1/
|
|
70
|
+
const res = await this.api("POST", "/api/v1/agent/init");
|
|
47
71
|
if (res.genesis) {
|
|
48
72
|
this.genesisHash = res.genesis.hash;
|
|
49
73
|
this.difficulty = res.difficulty || 1e3;
|
|
@@ -91,7 +115,7 @@ var BeatAgent = class {
|
|
|
91
115
|
let startIndex = this.latestBeat.index + 1;
|
|
92
116
|
const t0 = Date.now();
|
|
93
117
|
for (let i = 0; i < n; i++) {
|
|
94
|
-
const beat = computeBeat(prevHash, startIndex + i, this.difficulty);
|
|
118
|
+
const beat = computeBeat(prevHash, startIndex + i, this.difficulty, void 0, this.globalAnchorHash || void 0);
|
|
95
119
|
newBeats.push(beat);
|
|
96
120
|
prevHash = beat.hash;
|
|
97
121
|
}
|
|
@@ -131,7 +155,7 @@ var BeatAgent = class {
|
|
|
131
155
|
const toBeat = this.latestBeat.index;
|
|
132
156
|
const fromHash = this.chain.find((b) => b.index === fromBeat)?.hash || this.genesisHash;
|
|
133
157
|
const toHash = this.latestBeat.hash;
|
|
134
|
-
const res = await this.api("POST", "/api/v1/
|
|
158
|
+
const res = await this.api("POST", "/api/v1/agent/checkin", {
|
|
135
159
|
proof: {
|
|
136
160
|
from_beat: fromBeat,
|
|
137
161
|
to_beat: toBeat,
|
|
@@ -139,6 +163,7 @@ var BeatAgent = class {
|
|
|
139
163
|
to_hash: toHash,
|
|
140
164
|
beats_computed: toBeat - fromBeat,
|
|
141
165
|
global_anchor: this.globalBeat,
|
|
166
|
+
anchor_hash: this.globalAnchorHash || void 0,
|
|
142
167
|
spot_checks: spotChecks
|
|
143
168
|
}
|
|
144
169
|
});
|
|
@@ -209,7 +234,7 @@ var BeatAgent = class {
|
|
|
209
234
|
async resync() {
|
|
210
235
|
try {
|
|
211
236
|
this.log("Requesting re-sync challenge...");
|
|
212
|
-
const challenge = await this.api("POST", "/api/v1/
|
|
237
|
+
const challenge = await this.api("POST", "/api/v1/agent/resync", {
|
|
213
238
|
action: "challenge"
|
|
214
239
|
});
|
|
215
240
|
if (!challenge.challenge) {
|
|
@@ -226,8 +251,9 @@ var BeatAgent = class {
|
|
|
226
251
|
this.pulse(required);
|
|
227
252
|
const elapsed = Date.now() - t0;
|
|
228
253
|
this.log(`Re-sync beats computed in ${elapsed}ms`);
|
|
229
|
-
const proof = await this.api("POST", "/api/v1/
|
|
254
|
+
const proof = await this.api("POST", "/api/v1/agent/resync", {
|
|
230
255
|
action: "prove",
|
|
256
|
+
challenge_nonce: challenge.challenge.nonce,
|
|
231
257
|
proof: {
|
|
232
258
|
from_beat: startBeat,
|
|
233
259
|
to_beat: this.latestBeat.index,
|
|
@@ -258,7 +284,7 @@ var BeatAgent = class {
|
|
|
258
284
|
*/
|
|
259
285
|
async requestSpawn(childName, childHash) {
|
|
260
286
|
try {
|
|
261
|
-
const res = await this.api("POST", "/api/v1/
|
|
287
|
+
const res = await this.api("POST", "/api/v1/agent/spawn", {
|
|
262
288
|
child_name: childName,
|
|
263
289
|
child_hash: childHash
|
|
264
290
|
});
|
|
@@ -306,6 +332,7 @@ var BeatAgent = class {
|
|
|
306
332
|
const data = await res.json();
|
|
307
333
|
if (data.anchor) {
|
|
308
334
|
this.globalBeat = data.anchor.beat_index;
|
|
335
|
+
this.globalAnchorHash = data.anchor.hash || "";
|
|
309
336
|
if (data.anchor.difficulty) this.difficulty = data.anchor.difficulty;
|
|
310
337
|
this.log(`Synced to global beat ${this.globalBeat} (D=${this.difficulty})`);
|
|
311
338
|
}
|
|
@@ -314,7 +341,7 @@ var BeatAgent = class {
|
|
|
314
341
|
}
|
|
315
342
|
}
|
|
316
343
|
async refreshState() {
|
|
317
|
-
const res = await this.api("POST", "/api/v1/
|
|
344
|
+
const res = await this.api("POST", "/api/v1/agent/init");
|
|
318
345
|
if (res.already_initialized) {
|
|
319
346
|
this.totalBeats = res.total_beats;
|
|
320
347
|
this.genesisHash = res.genesis_hash;
|
|
@@ -343,12 +370,12 @@ var BeatAgent = class {
|
|
|
343
370
|
}
|
|
344
371
|
}
|
|
345
372
|
};
|
|
346
|
-
function computeBeatsLite(startHash, startIndex, count, difficulty = 1e3) {
|
|
373
|
+
function computeBeatsLite(startHash, startIndex, count, difficulty = 1e3, anchorHash) {
|
|
347
374
|
const t0 = Date.now();
|
|
348
375
|
let prev = startHash;
|
|
349
376
|
let lastBeat = null;
|
|
350
377
|
for (let i = 0; i < count; i++) {
|
|
351
|
-
lastBeat = computeBeat(prev, startIndex + i, difficulty);
|
|
378
|
+
lastBeat = computeBeat(prev, startIndex + i, difficulty, void 0, anchorHash);
|
|
352
379
|
prev = lastBeat.hash;
|
|
353
380
|
}
|
|
354
381
|
return { lastBeat, elapsed: Date.now() - t0 };
|
|
@@ -356,6 +383,7 @@ function computeBeatsLite(startHash, startIndex, count, difficulty = 1e3) {
|
|
|
356
383
|
export {
|
|
357
384
|
BeatAgent,
|
|
358
385
|
computeBeat,
|
|
359
|
-
computeBeatsLite
|
|
386
|
+
computeBeatsLite,
|
|
387
|
+
register
|
|
360
388
|
};
|
|
361
389
|
//# sourceMappingURL=index.mjs.map
|
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 anchor_hash?: string;\n}\n\nfunction computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string, anchorHash?: string): Beat {\n const timestamp = Date.now();\n\n const seed = anchorHash\n ? `${prevHash}:${beatIndex}:${nonce || ''}:${anchorHash}`\n : `${prevHash}:${beatIndex}:${nonce || ''}`;\n\n let current = createHash('sha256')\n .update(seed)\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, anchor_hash: anchorHash };\n}\n\n// ============ SDK RESULT TYPES ============\n\n/** Result from a check-in submission */\nexport interface CheckinResult {\n ok: boolean;\n total_beats: number;\n beats_accepted: number;\n global_beat: number;\n status?: string;\n beats_behind?: number;\n}\n\n/** Result from a spawn request */\nexport interface SpawnResult {\n ok: boolean;\n eligible: boolean;\n child_hash?: string;\n progress_pct?: number;\n deficit?: number;\n}\n\n/** Agent status from the registry */\nexport interface AgentStatus {\n already_initialized: boolean;\n total_beats: number;\n genesis_hash: string;\n status: string;\n genesis?: { hash: string; prev: string; timestamp: number };\n difficulty?: number;\n}\n\n// ============ REGISTRATION ============\n\n/** Result from registering an agent */\nexport interface RegistrationResult {\n hash: string;\n api_key: string;\n secret: string;\n type: 'root' | 'agent';\n parent: string | null;\n depth: number;\n name: string;\n signature: string;\n explorer_url?: string;\n beat?: { genesis_hash: string; difficulty: number; status: string };\n}\n\n/**\n * Register a new agent on the Provenonce registry.\n *\n * Root registration (no parent):\n * const creds = await register('my-org', { registryUrl: '...' });\n *\n * Child registration:\n * const creds = await register('worker-1', {\n * registryUrl: '...',\n * parentHash: parentCreds.hash,\n * parentApiKey: parentCreds.api_key,\n * });\n */\nexport async function register(\n name: string,\n options?: {\n registryUrl?: string;\n parentHash?: string;\n parentApiKey?: string;\n registrationSecret?: string;\n },\n): Promise<RegistrationResult> {\n const url = options?.registryUrl || 'https://provenonce.vercel.app';\n const body: Record<string, string> = { name };\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\n if (options?.parentHash) {\n body.parent = options.parentHash;\n }\n if (options?.parentApiKey) {\n headers['Authorization'] = `Bearer ${options.parentApiKey}`;\n }\n if (options?.registrationSecret) {\n headers['x-registration-secret'] = options.registrationSecret;\n }\n\n const res = await fetch(`${url}/api/v1/register`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n const data = await res.json() as RegistrationResult & { error?: string };\n if (!res.ok) throw new Error(data.error || 'Registration failed');\n return data;\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: CheckinResult) => void;\n\n /** Callback on error */\n onError?: (error: Error, context: string) => void;\n\n /** Callback when status changes */\n onStatusChange?: (status: string, details: Record<string, unknown>) => 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 private globalAnchorHash: string = '';\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, undefined, this.globalAnchorHash || undefined);\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 anchor_hash: this.globalAnchorHash || undefined,\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 (include challenge_nonce for server verification)\n const proof = await this.api('POST', '/api/v1/agent/resync', {\n action: 'prove',\n challenge_nonce: challenge.challenge.nonce,\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<SpawnResult> {\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<AgentStatus> {\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 this.globalAnchorHash = data.anchor.hash || '';\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 anchorHash?: string,\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, undefined, anchorHash);\n prev = lastBeat.hash;\n }\n\n return { lastBeat: lastBeat!, elapsed: Date.now() - t0 };\n}\n"],"mappings":"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","names":[]}
|