@provenonce/sdk 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +390 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +361 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +42 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ═══════════════════════════════════════════════════════════
|
|
3
|
+
* PROVENONCE BEAT SDK — Agent Heartbeat Client
|
|
4
|
+
* ═══════════════════════════════════════════════════════════
|
|
5
|
+
*
|
|
6
|
+
* "NIST tells you what time it is.
|
|
7
|
+
* Provenonce tells the agent at what speed it is allowed to exist."
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
*
|
|
11
|
+
* import { BeatAgent } from './beat-sdk';
|
|
12
|
+
*
|
|
13
|
+
* const agent = new BeatAgent({
|
|
14
|
+
* apiKey: 'pvn_...',
|
|
15
|
+
* registryUrl: 'https://provenonce.vercel.app',
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* await agent.init(); // Birth in Beat time
|
|
19
|
+
* await agent.pulse(50); // Compute 50 beats
|
|
20
|
+
* await agent.checkin(); // Report to registry
|
|
21
|
+
*
|
|
22
|
+
* // Or run the autonomous heartbeat:
|
|
23
|
+
* agent.startHeartbeat(); // Computes + checks in continuously
|
|
24
|
+
* // ... do your agent work ...
|
|
25
|
+
* agent.stopHeartbeat();
|
|
26
|
+
*
|
|
27
|
+
* ═══════════════════════════════════════════════════════════
|
|
28
|
+
*/
|
|
29
|
+
interface Beat {
|
|
30
|
+
index: number;
|
|
31
|
+
hash: string;
|
|
32
|
+
prev: string;
|
|
33
|
+
timestamp: number;
|
|
34
|
+
nonce?: string;
|
|
35
|
+
}
|
|
36
|
+
declare function computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string): Beat;
|
|
37
|
+
interface BeatAgentConfig {
|
|
38
|
+
/** API key from registration (pvn_...) */
|
|
39
|
+
apiKey: string;
|
|
40
|
+
/** Provenonce registry URL */
|
|
41
|
+
registryUrl: string;
|
|
42
|
+
/** Beats to compute per pulse (default: 10) */
|
|
43
|
+
beatsPerPulse?: number;
|
|
44
|
+
/** Seconds between automatic check-ins (default: 300 = 5min) */
|
|
45
|
+
checkinIntervalSec?: number;
|
|
46
|
+
/** Callback when heartbeat ticks */
|
|
47
|
+
onPulse?: (beats: Beat[], totalBeats: number) => void;
|
|
48
|
+
/** Callback when check-in completes */
|
|
49
|
+
onCheckin?: (result: any) => void;
|
|
50
|
+
/** Callback on error */
|
|
51
|
+
onError?: (error: Error, context: string) => void;
|
|
52
|
+
/** Callback when status changes */
|
|
53
|
+
onStatusChange?: (status: string, details: any) => void;
|
|
54
|
+
/** Enable verbose logging */
|
|
55
|
+
verbose?: boolean;
|
|
56
|
+
}
|
|
57
|
+
declare class BeatAgent {
|
|
58
|
+
private config;
|
|
59
|
+
private chain;
|
|
60
|
+
private difficulty;
|
|
61
|
+
private genesisHash;
|
|
62
|
+
private latestBeat;
|
|
63
|
+
private totalBeats;
|
|
64
|
+
private lastCheckinBeat;
|
|
65
|
+
private status;
|
|
66
|
+
private heartbeatInterval;
|
|
67
|
+
private globalBeat;
|
|
68
|
+
constructor(config: BeatAgentConfig);
|
|
69
|
+
/**
|
|
70
|
+
* Initialize the agent's Beat chain.
|
|
71
|
+
* This is the agent's "birth" in Logical Time.
|
|
72
|
+
* Must be called once before computing beats.
|
|
73
|
+
*/
|
|
74
|
+
init(): Promise<{
|
|
75
|
+
ok: boolean;
|
|
76
|
+
genesis?: string;
|
|
77
|
+
error?: string;
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* Compute N beats locally (VDF hash chain).
|
|
81
|
+
* This is the "heartbeat" — proof that the agent has lived
|
|
82
|
+
* through a specific window of computational time.
|
|
83
|
+
*/
|
|
84
|
+
pulse(count?: number): Beat[];
|
|
85
|
+
/**
|
|
86
|
+
* Submit a Beat proof to the registry.
|
|
87
|
+
*
|
|
88
|
+
* "To remain on the Whitelist, an agent must periodically
|
|
89
|
+
* submit a proof of its Local Beats to the Registry."
|
|
90
|
+
*/
|
|
91
|
+
checkin(): Promise<{
|
|
92
|
+
ok: boolean;
|
|
93
|
+
total_beats?: number;
|
|
94
|
+
error?: string;
|
|
95
|
+
}>;
|
|
96
|
+
/**
|
|
97
|
+
* Start the autonomous heartbeat loop.
|
|
98
|
+
* Computes beats continuously and checks in periodically.
|
|
99
|
+
* This is "keeping the agent alive" in Beat time.
|
|
100
|
+
*/
|
|
101
|
+
startHeartbeat(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Stop the heartbeat. Agent's time "freezes."
|
|
104
|
+
* Must call resync() when waking up.
|
|
105
|
+
*/
|
|
106
|
+
stopHeartbeat(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Re-sync after being offline/frozen.
|
|
109
|
+
*
|
|
110
|
+
* "When an agent powers down, its time 'freezes.' Upon waking,
|
|
111
|
+
* it must perform a Re-Sync Challenge with the Registry to
|
|
112
|
+
* fill the 'Temporal Gap' and re-establish its provenance."
|
|
113
|
+
*/
|
|
114
|
+
resync(): Promise<{
|
|
115
|
+
ok: boolean;
|
|
116
|
+
beats_required?: number;
|
|
117
|
+
error?: string;
|
|
118
|
+
}>;
|
|
119
|
+
/**
|
|
120
|
+
* Request to spawn a child agent.
|
|
121
|
+
* Requires sufficient accumulated beats (Temporal Gestation).
|
|
122
|
+
*/
|
|
123
|
+
requestSpawn(childName?: string, childHash?: string): Promise<any>;
|
|
124
|
+
/**
|
|
125
|
+
* Get this agent's full beat status from the registry.
|
|
126
|
+
*/
|
|
127
|
+
getStatus(): Promise<any>;
|
|
128
|
+
/**
|
|
129
|
+
* Get local state (no network call).
|
|
130
|
+
*/
|
|
131
|
+
getLocalState(): {
|
|
132
|
+
status: string;
|
|
133
|
+
totalBeats: number;
|
|
134
|
+
latestBeat: number;
|
|
135
|
+
latestHash: string;
|
|
136
|
+
difficulty: number;
|
|
137
|
+
globalBeat: number;
|
|
138
|
+
chainLength: number;
|
|
139
|
+
};
|
|
140
|
+
private syncGlobal;
|
|
141
|
+
private refreshState;
|
|
142
|
+
private api;
|
|
143
|
+
private log;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Compute N sequential VDF beats.
|
|
148
|
+
* Returns only the last beat (for lightweight usage).
|
|
149
|
+
*/
|
|
150
|
+
declare function computeBeatsLite(startHash: string, startIndex: number, count: number, difficulty?: number): {
|
|
151
|
+
lastBeat: Beat;
|
|
152
|
+
elapsed: number;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export { type Beat, BeatAgent, type BeatAgentConfig, computeBeat, computeBeatsLite };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ═══════════════════════════════════════════════════════════
|
|
3
|
+
* PROVENONCE BEAT SDK — Agent Heartbeat Client
|
|
4
|
+
* ═══════════════════════════════════════════════════════════
|
|
5
|
+
*
|
|
6
|
+
* "NIST tells you what time it is.
|
|
7
|
+
* Provenonce tells the agent at what speed it is allowed to exist."
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
*
|
|
11
|
+
* import { BeatAgent } from './beat-sdk';
|
|
12
|
+
*
|
|
13
|
+
* const agent = new BeatAgent({
|
|
14
|
+
* apiKey: 'pvn_...',
|
|
15
|
+
* registryUrl: 'https://provenonce.vercel.app',
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* await agent.init(); // Birth in Beat time
|
|
19
|
+
* await agent.pulse(50); // Compute 50 beats
|
|
20
|
+
* await agent.checkin(); // Report to registry
|
|
21
|
+
*
|
|
22
|
+
* // Or run the autonomous heartbeat:
|
|
23
|
+
* agent.startHeartbeat(); // Computes + checks in continuously
|
|
24
|
+
* // ... do your agent work ...
|
|
25
|
+
* agent.stopHeartbeat();
|
|
26
|
+
*
|
|
27
|
+
* ═══════════════════════════════════════════════════════════
|
|
28
|
+
*/
|
|
29
|
+
interface Beat {
|
|
30
|
+
index: number;
|
|
31
|
+
hash: string;
|
|
32
|
+
prev: string;
|
|
33
|
+
timestamp: number;
|
|
34
|
+
nonce?: string;
|
|
35
|
+
}
|
|
36
|
+
declare function computeBeat(prevHash: string, beatIndex: number, difficulty: number, nonce?: string): Beat;
|
|
37
|
+
interface BeatAgentConfig {
|
|
38
|
+
/** API key from registration (pvn_...) */
|
|
39
|
+
apiKey: string;
|
|
40
|
+
/** Provenonce registry URL */
|
|
41
|
+
registryUrl: string;
|
|
42
|
+
/** Beats to compute per pulse (default: 10) */
|
|
43
|
+
beatsPerPulse?: number;
|
|
44
|
+
/** Seconds between automatic check-ins (default: 300 = 5min) */
|
|
45
|
+
checkinIntervalSec?: number;
|
|
46
|
+
/** Callback when heartbeat ticks */
|
|
47
|
+
onPulse?: (beats: Beat[], totalBeats: number) => void;
|
|
48
|
+
/** Callback when check-in completes */
|
|
49
|
+
onCheckin?: (result: any) => void;
|
|
50
|
+
/** Callback on error */
|
|
51
|
+
onError?: (error: Error, context: string) => void;
|
|
52
|
+
/** Callback when status changes */
|
|
53
|
+
onStatusChange?: (status: string, details: any) => void;
|
|
54
|
+
/** Enable verbose logging */
|
|
55
|
+
verbose?: boolean;
|
|
56
|
+
}
|
|
57
|
+
declare class BeatAgent {
|
|
58
|
+
private config;
|
|
59
|
+
private chain;
|
|
60
|
+
private difficulty;
|
|
61
|
+
private genesisHash;
|
|
62
|
+
private latestBeat;
|
|
63
|
+
private totalBeats;
|
|
64
|
+
private lastCheckinBeat;
|
|
65
|
+
private status;
|
|
66
|
+
private heartbeatInterval;
|
|
67
|
+
private globalBeat;
|
|
68
|
+
constructor(config: BeatAgentConfig);
|
|
69
|
+
/**
|
|
70
|
+
* Initialize the agent's Beat chain.
|
|
71
|
+
* This is the agent's "birth" in Logical Time.
|
|
72
|
+
* Must be called once before computing beats.
|
|
73
|
+
*/
|
|
74
|
+
init(): Promise<{
|
|
75
|
+
ok: boolean;
|
|
76
|
+
genesis?: string;
|
|
77
|
+
error?: string;
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* Compute N beats locally (VDF hash chain).
|
|
81
|
+
* This is the "heartbeat" — proof that the agent has lived
|
|
82
|
+
* through a specific window of computational time.
|
|
83
|
+
*/
|
|
84
|
+
pulse(count?: number): Beat[];
|
|
85
|
+
/**
|
|
86
|
+
* Submit a Beat proof to the registry.
|
|
87
|
+
*
|
|
88
|
+
* "To remain on the Whitelist, an agent must periodically
|
|
89
|
+
* submit a proof of its Local Beats to the Registry."
|
|
90
|
+
*/
|
|
91
|
+
checkin(): Promise<{
|
|
92
|
+
ok: boolean;
|
|
93
|
+
total_beats?: number;
|
|
94
|
+
error?: string;
|
|
95
|
+
}>;
|
|
96
|
+
/**
|
|
97
|
+
* Start the autonomous heartbeat loop.
|
|
98
|
+
* Computes beats continuously and checks in periodically.
|
|
99
|
+
* This is "keeping the agent alive" in Beat time.
|
|
100
|
+
*/
|
|
101
|
+
startHeartbeat(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Stop the heartbeat. Agent's time "freezes."
|
|
104
|
+
* Must call resync() when waking up.
|
|
105
|
+
*/
|
|
106
|
+
stopHeartbeat(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Re-sync after being offline/frozen.
|
|
109
|
+
*
|
|
110
|
+
* "When an agent powers down, its time 'freezes.' Upon waking,
|
|
111
|
+
* it must perform a Re-Sync Challenge with the Registry to
|
|
112
|
+
* fill the 'Temporal Gap' and re-establish its provenance."
|
|
113
|
+
*/
|
|
114
|
+
resync(): Promise<{
|
|
115
|
+
ok: boolean;
|
|
116
|
+
beats_required?: number;
|
|
117
|
+
error?: string;
|
|
118
|
+
}>;
|
|
119
|
+
/**
|
|
120
|
+
* Request to spawn a child agent.
|
|
121
|
+
* Requires sufficient accumulated beats (Temporal Gestation).
|
|
122
|
+
*/
|
|
123
|
+
requestSpawn(childName?: string, childHash?: string): Promise<any>;
|
|
124
|
+
/**
|
|
125
|
+
* Get this agent's full beat status from the registry.
|
|
126
|
+
*/
|
|
127
|
+
getStatus(): Promise<any>;
|
|
128
|
+
/**
|
|
129
|
+
* Get local state (no network call).
|
|
130
|
+
*/
|
|
131
|
+
getLocalState(): {
|
|
132
|
+
status: string;
|
|
133
|
+
totalBeats: number;
|
|
134
|
+
latestBeat: number;
|
|
135
|
+
latestHash: string;
|
|
136
|
+
difficulty: number;
|
|
137
|
+
globalBeat: number;
|
|
138
|
+
chainLength: number;
|
|
139
|
+
};
|
|
140
|
+
private syncGlobal;
|
|
141
|
+
private refreshState;
|
|
142
|
+
private api;
|
|
143
|
+
private log;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Compute N sequential VDF beats.
|
|
148
|
+
* Returns only the last beat (for lightweight usage).
|
|
149
|
+
*/
|
|
150
|
+
declare function computeBeatsLite(startHash: string, startIndex: number, count: number, difficulty?: number): {
|
|
151
|
+
lastBeat: Beat;
|
|
152
|
+
elapsed: number;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export { type Beat, BeatAgent, type BeatAgentConfig, computeBeat, computeBeatsLite };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
BeatAgent: () => BeatAgent,
|
|
24
|
+
computeBeat: () => computeBeat,
|
|
25
|
+
computeBeatsLite: () => computeBeatsLite
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/beat-sdk.ts
|
|
30
|
+
var import_crypto = require("crypto");
|
|
31
|
+
function computeBeat(prevHash, beatIndex, difficulty, nonce) {
|
|
32
|
+
const timestamp = Date.now();
|
|
33
|
+
let current = (0, import_crypto.createHash)("sha256").update(`${prevHash}:${beatIndex}:${nonce || ""}`).digest("hex");
|
|
34
|
+
for (let i = 0; i < difficulty; i++) {
|
|
35
|
+
current = (0, import_crypto.createHash)("sha256").update(current).digest("hex");
|
|
36
|
+
}
|
|
37
|
+
return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce };
|
|
38
|
+
}
|
|
39
|
+
var BeatAgent = class {
|
|
40
|
+
constructor(config) {
|
|
41
|
+
this.chain = [];
|
|
42
|
+
this.difficulty = 1e3;
|
|
43
|
+
this.genesisHash = "";
|
|
44
|
+
this.latestBeat = null;
|
|
45
|
+
this.totalBeats = 0;
|
|
46
|
+
this.lastCheckinBeat = 0;
|
|
47
|
+
this.status = "uninitialized";
|
|
48
|
+
this.heartbeatInterval = null;
|
|
49
|
+
this.globalBeat = 0;
|
|
50
|
+
this.config = {
|
|
51
|
+
beatsPerPulse: 10,
|
|
52
|
+
checkinIntervalSec: 300,
|
|
53
|
+
onPulse: () => {
|
|
54
|
+
},
|
|
55
|
+
onCheckin: () => {
|
|
56
|
+
},
|
|
57
|
+
onError: () => {
|
|
58
|
+
},
|
|
59
|
+
onStatusChange: () => {
|
|
60
|
+
},
|
|
61
|
+
verbose: false,
|
|
62
|
+
...config
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// ── INITIALIZATION ──
|
|
66
|
+
/**
|
|
67
|
+
* Initialize the agent's Beat chain.
|
|
68
|
+
* This is the agent's "birth" in Logical Time.
|
|
69
|
+
* Must be called once before computing beats.
|
|
70
|
+
*/
|
|
71
|
+
async init() {
|
|
72
|
+
try {
|
|
73
|
+
this.log("Initializing Beat chain...");
|
|
74
|
+
const res = await this.api("POST", "/api/v1/beat/init");
|
|
75
|
+
if (res.genesis) {
|
|
76
|
+
this.genesisHash = res.genesis.hash;
|
|
77
|
+
this.difficulty = res.difficulty || 1e3;
|
|
78
|
+
this.latestBeat = {
|
|
79
|
+
index: 0,
|
|
80
|
+
hash: res.genesis.hash,
|
|
81
|
+
prev: res.genesis.prev,
|
|
82
|
+
timestamp: res.genesis.timestamp
|
|
83
|
+
};
|
|
84
|
+
this.chain = [this.latestBeat];
|
|
85
|
+
this.totalBeats = 0;
|
|
86
|
+
this.status = "active";
|
|
87
|
+
this.config.onStatusChange("active", { genesis: this.genesisHash });
|
|
88
|
+
this.log(`Born in Beat time. Genesis: ${this.genesisHash.slice(0, 16)}...`);
|
|
89
|
+
} else if (res.already_initialized) {
|
|
90
|
+
this.genesisHash = res.genesis_hash;
|
|
91
|
+
this.totalBeats = res.total_beats;
|
|
92
|
+
this.status = res.status;
|
|
93
|
+
this.log(`Already initialized. Restoring state (${res.total_beats} beats).`);
|
|
94
|
+
await this.refreshState();
|
|
95
|
+
}
|
|
96
|
+
await this.syncGlobal();
|
|
97
|
+
return { ok: true, genesis: this.genesisHash };
|
|
98
|
+
} catch (err) {
|
|
99
|
+
this.config.onError(err, "init");
|
|
100
|
+
return { ok: false, error: err.message };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// ── PULSE (COMPUTE BEATS) ──
|
|
104
|
+
/**
|
|
105
|
+
* Compute N beats locally (VDF hash chain).
|
|
106
|
+
* This is the "heartbeat" — proof that the agent has lived
|
|
107
|
+
* through a specific window of computational time.
|
|
108
|
+
*/
|
|
109
|
+
pulse(count) {
|
|
110
|
+
const n = count || this.config.beatsPerPulse;
|
|
111
|
+
if (!this.latestBeat) {
|
|
112
|
+
throw new Error("Beat chain not initialized. Call init() first.");
|
|
113
|
+
}
|
|
114
|
+
if (this.status !== "active") {
|
|
115
|
+
throw new Error(`Cannot pulse in status '${this.status}'. Use resync() if frozen.`);
|
|
116
|
+
}
|
|
117
|
+
const newBeats = [];
|
|
118
|
+
let prevHash = this.latestBeat.hash;
|
|
119
|
+
let startIndex = this.latestBeat.index + 1;
|
|
120
|
+
const t0 = Date.now();
|
|
121
|
+
for (let i = 0; i < n; i++) {
|
|
122
|
+
const beat = computeBeat(prevHash, startIndex + i, this.difficulty);
|
|
123
|
+
newBeats.push(beat);
|
|
124
|
+
prevHash = beat.hash;
|
|
125
|
+
}
|
|
126
|
+
const elapsed = Date.now() - t0;
|
|
127
|
+
this.chain.push(...newBeats);
|
|
128
|
+
this.latestBeat = newBeats[newBeats.length - 1];
|
|
129
|
+
this.totalBeats += n;
|
|
130
|
+
if (this.chain.length > 1e3) {
|
|
131
|
+
this.chain = this.chain.slice(-500);
|
|
132
|
+
}
|
|
133
|
+
this.config.onPulse(newBeats, this.totalBeats);
|
|
134
|
+
this.log(`Pulse: ${n} beats in ${elapsed}ms (${(elapsed / n).toFixed(1)}ms/beat, D=${this.difficulty})`);
|
|
135
|
+
return newBeats;
|
|
136
|
+
}
|
|
137
|
+
// ── CHECK-IN ──
|
|
138
|
+
/**
|
|
139
|
+
* Submit a Beat proof to the registry.
|
|
140
|
+
*
|
|
141
|
+
* "To remain on the Whitelist, an agent must periodically
|
|
142
|
+
* submit a proof of its Local Beats to the Registry."
|
|
143
|
+
*/
|
|
144
|
+
async checkin() {
|
|
145
|
+
if (!this.latestBeat || this.totalBeats === this.lastCheckinBeat) {
|
|
146
|
+
return { ok: true, total_beats: this.totalBeats };
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const spotChecks = [];
|
|
150
|
+
const available = this.chain.filter((b) => b.index > this.lastCheckinBeat);
|
|
151
|
+
const sampleCount = Math.min(5, available.length);
|
|
152
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
153
|
+
const idx = Math.floor(Math.random() * available.length);
|
|
154
|
+
const beat = available[idx];
|
|
155
|
+
spotChecks.push({ index: beat.index, hash: beat.hash, prev: beat.prev, nonce: beat.nonce });
|
|
156
|
+
available.splice(idx, 1);
|
|
157
|
+
}
|
|
158
|
+
const fromBeat = this.lastCheckinBeat;
|
|
159
|
+
const toBeat = this.latestBeat.index;
|
|
160
|
+
const fromHash = this.chain.find((b) => b.index === fromBeat)?.hash || this.genesisHash;
|
|
161
|
+
const toHash = this.latestBeat.hash;
|
|
162
|
+
const res = await this.api("POST", "/api/v1/beat/checkin", {
|
|
163
|
+
proof: {
|
|
164
|
+
from_beat: fromBeat,
|
|
165
|
+
to_beat: toBeat,
|
|
166
|
+
from_hash: fromHash,
|
|
167
|
+
to_hash: toHash,
|
|
168
|
+
beats_computed: toBeat - fromBeat,
|
|
169
|
+
global_anchor: this.globalBeat,
|
|
170
|
+
spot_checks: spotChecks
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
if (res.ok) {
|
|
174
|
+
this.lastCheckinBeat = toBeat;
|
|
175
|
+
this.totalBeats = res.total_beats;
|
|
176
|
+
this.config.onCheckin(res);
|
|
177
|
+
this.log(`Check-in accepted: ${res.beats_accepted} beats, total=${res.total_beats}, global=${res.global_beat}`);
|
|
178
|
+
if (res.status === "warning_overdue") {
|
|
179
|
+
this.config.onStatusChange("warning", { beats_behind: res.beats_behind });
|
|
180
|
+
this.log(`\u26A0 WARNING: ${res.beats_behind} anchors behind. Check in more frequently.`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { ok: res.ok, total_beats: res.total_beats };
|
|
184
|
+
} catch (err) {
|
|
185
|
+
this.config.onError(err, "checkin");
|
|
186
|
+
return { ok: false, error: err.message };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// ── AUTONOMOUS HEARTBEAT ──
|
|
190
|
+
/**
|
|
191
|
+
* Start the autonomous heartbeat loop.
|
|
192
|
+
* Computes beats continuously and checks in periodically.
|
|
193
|
+
* This is "keeping the agent alive" in Beat time.
|
|
194
|
+
*/
|
|
195
|
+
startHeartbeat() {
|
|
196
|
+
if (this.heartbeatInterval) {
|
|
197
|
+
this.log("Heartbeat already running.");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (this.status !== "active") {
|
|
201
|
+
throw new Error(`Cannot start heartbeat in status '${this.status}'.`);
|
|
202
|
+
}
|
|
203
|
+
this.log("\u2661 Starting heartbeat...");
|
|
204
|
+
this.heartbeatInterval = setInterval(async () => {
|
|
205
|
+
try {
|
|
206
|
+
this.pulse();
|
|
207
|
+
const beatsSinceCheckin = this.latestBeat.index - this.lastCheckinBeat;
|
|
208
|
+
const shouldCheckin = beatsSinceCheckin >= this.config.beatsPerPulse * 5;
|
|
209
|
+
if (shouldCheckin) {
|
|
210
|
+
await this.checkin();
|
|
211
|
+
await this.syncGlobal();
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
this.config.onError(err, "heartbeat");
|
|
215
|
+
}
|
|
216
|
+
}, this.config.checkinIntervalSec * 1e3 / 10);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Stop the heartbeat. Agent's time "freezes."
|
|
220
|
+
* Must call resync() when waking up.
|
|
221
|
+
*/
|
|
222
|
+
stopHeartbeat() {
|
|
223
|
+
if (this.heartbeatInterval) {
|
|
224
|
+
clearInterval(this.heartbeatInterval);
|
|
225
|
+
this.heartbeatInterval = null;
|
|
226
|
+
this.log("\u2661 Heartbeat stopped. Time frozen.");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// ── RE-SYNC ──
|
|
230
|
+
/**
|
|
231
|
+
* Re-sync after being offline/frozen.
|
|
232
|
+
*
|
|
233
|
+
* "When an agent powers down, its time 'freezes.' Upon waking,
|
|
234
|
+
* it must perform a Re-Sync Challenge with the Registry to
|
|
235
|
+
* fill the 'Temporal Gap' and re-establish its provenance."
|
|
236
|
+
*/
|
|
237
|
+
async resync() {
|
|
238
|
+
try {
|
|
239
|
+
this.log("Requesting re-sync challenge...");
|
|
240
|
+
const challenge = await this.api("POST", "/api/v1/beat/resync", {
|
|
241
|
+
action: "challenge"
|
|
242
|
+
});
|
|
243
|
+
if (!challenge.challenge) {
|
|
244
|
+
return { ok: false, error: "Failed to get challenge" };
|
|
245
|
+
}
|
|
246
|
+
const required = challenge.challenge.required_beats;
|
|
247
|
+
this.difficulty = challenge.challenge.difficulty;
|
|
248
|
+
this.log(`Re-sync challenge: compute ${required} beats at D=${this.difficulty}`);
|
|
249
|
+
const startHash = challenge.challenge.start_from_hash;
|
|
250
|
+
const startBeat = challenge.challenge.start_from_beat;
|
|
251
|
+
this.latestBeat = { index: startBeat, hash: startHash, prev: "", timestamp: Date.now() };
|
|
252
|
+
this.chain = [this.latestBeat];
|
|
253
|
+
const t0 = Date.now();
|
|
254
|
+
this.pulse(required);
|
|
255
|
+
const elapsed = Date.now() - t0;
|
|
256
|
+
this.log(`Re-sync beats computed in ${elapsed}ms`);
|
|
257
|
+
const proof = await this.api("POST", "/api/v1/beat/resync", {
|
|
258
|
+
action: "prove",
|
|
259
|
+
proof: {
|
|
260
|
+
from_beat: startBeat,
|
|
261
|
+
to_beat: this.latestBeat.index,
|
|
262
|
+
from_hash: startHash,
|
|
263
|
+
to_hash: this.latestBeat.hash,
|
|
264
|
+
beats_computed: required,
|
|
265
|
+
global_anchor: challenge.challenge.sync_to_global,
|
|
266
|
+
spot_checks: this.chain.filter((_, i) => i % Math.ceil(required / 5) === 0).slice(0, 5).map((b) => ({ index: b.index, hash: b.hash, prev: b.prev, nonce: b.nonce }))
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
if (proof.ok) {
|
|
270
|
+
this.status = "active";
|
|
271
|
+
this.totalBeats = proof.total_beats;
|
|
272
|
+
this.lastCheckinBeat = this.latestBeat.index;
|
|
273
|
+
this.config.onStatusChange("active", { resynced: true });
|
|
274
|
+
this.log("\u2713 Re-synced. Agent is alive again in Beat time.");
|
|
275
|
+
}
|
|
276
|
+
return { ok: proof.ok, beats_required: required };
|
|
277
|
+
} catch (err) {
|
|
278
|
+
this.config.onError(err, "resync");
|
|
279
|
+
return { ok: false, error: err.message };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// ── SPAWN ──
|
|
283
|
+
/**
|
|
284
|
+
* Request to spawn a child agent.
|
|
285
|
+
* Requires sufficient accumulated beats (Temporal Gestation).
|
|
286
|
+
*/
|
|
287
|
+
async requestSpawn(childName, childHash) {
|
|
288
|
+
try {
|
|
289
|
+
const res = await this.api("POST", "/api/v1/beat/spawn", {
|
|
290
|
+
child_name: childName,
|
|
291
|
+
child_hash: childHash
|
|
292
|
+
});
|
|
293
|
+
if (res.eligible === false) {
|
|
294
|
+
this.log(`Gestation incomplete: ${res.progress_pct}% (need ${res.deficit} more beats)`);
|
|
295
|
+
} else if (res.ok) {
|
|
296
|
+
this.log(`Child spawned: ${res.child_hash?.slice(0, 16)}...`);
|
|
297
|
+
}
|
|
298
|
+
return res;
|
|
299
|
+
} catch (err) {
|
|
300
|
+
this.config.onError(err, "spawn");
|
|
301
|
+
throw err;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// ── STATUS ──
|
|
305
|
+
/**
|
|
306
|
+
* Get this agent's full beat status from the registry.
|
|
307
|
+
*/
|
|
308
|
+
async getStatus() {
|
|
309
|
+
try {
|
|
310
|
+
return await this.refreshState();
|
|
311
|
+
} catch (err) {
|
|
312
|
+
this.config.onError(err, "status");
|
|
313
|
+
throw err;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Get local state (no network call).
|
|
318
|
+
*/
|
|
319
|
+
getLocalState() {
|
|
320
|
+
return {
|
|
321
|
+
status: this.status,
|
|
322
|
+
totalBeats: this.totalBeats,
|
|
323
|
+
latestBeat: this.latestBeat?.index || 0,
|
|
324
|
+
latestHash: this.latestBeat?.hash.slice(0, 24) + "..." || "",
|
|
325
|
+
difficulty: this.difficulty,
|
|
326
|
+
globalBeat: this.globalBeat,
|
|
327
|
+
chainLength: this.chain.length
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
// ── INTERNALS ──
|
|
331
|
+
async syncGlobal() {
|
|
332
|
+
try {
|
|
333
|
+
const res = await fetch(`${this.config.registryUrl}/api/v1/beat/anchor`);
|
|
334
|
+
const data = await res.json();
|
|
335
|
+
if (data.anchor) {
|
|
336
|
+
this.globalBeat = data.anchor.beat_index;
|
|
337
|
+
if (data.anchor.difficulty) this.difficulty = data.anchor.difficulty;
|
|
338
|
+
this.log(`Synced to global beat ${this.globalBeat} (D=${this.difficulty})`);
|
|
339
|
+
}
|
|
340
|
+
} catch {
|
|
341
|
+
this.log("Failed to sync global anchor (offline mode continues)");
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async refreshState() {
|
|
345
|
+
const res = await this.api("POST", "/api/v1/beat/init");
|
|
346
|
+
if (res.already_initialized) {
|
|
347
|
+
this.totalBeats = res.total_beats;
|
|
348
|
+
this.genesisHash = res.genesis_hash;
|
|
349
|
+
this.status = res.status;
|
|
350
|
+
}
|
|
351
|
+
return res;
|
|
352
|
+
}
|
|
353
|
+
async api(method, path, body) {
|
|
354
|
+
const res = await fetch(`${this.config.registryUrl}${path}`, {
|
|
355
|
+
method,
|
|
356
|
+
headers: {
|
|
357
|
+
"Content-Type": "application/json",
|
|
358
|
+
"Authorization": `Bearer ${this.config.apiKey}`
|
|
359
|
+
},
|
|
360
|
+
body: body ? JSON.stringify(body) : void 0
|
|
361
|
+
});
|
|
362
|
+
const data = await res.json();
|
|
363
|
+
if (!res.ok && !data.ok && !data.already_initialized && !data.eligible) {
|
|
364
|
+
throw new Error(data.error || `API ${res.status}: ${res.statusText}`);
|
|
365
|
+
}
|
|
366
|
+
return data;
|
|
367
|
+
}
|
|
368
|
+
log(msg) {
|
|
369
|
+
if (this.config.verbose) {
|
|
370
|
+
console.log(`[Beat] ${msg}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
function computeBeatsLite(startHash, startIndex, count, difficulty = 1e3) {
|
|
375
|
+
const t0 = Date.now();
|
|
376
|
+
let prev = startHash;
|
|
377
|
+
let lastBeat = null;
|
|
378
|
+
for (let i = 0; i < count; i++) {
|
|
379
|
+
lastBeat = computeBeat(prev, startIndex + i, difficulty);
|
|
380
|
+
prev = lastBeat.hash;
|
|
381
|
+
}
|
|
382
|
+
return { lastBeat, elapsed: Date.now() - t0 };
|
|
383
|
+
}
|
|
384
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
385
|
+
0 && (module.exports = {
|
|
386
|
+
BeatAgent,
|
|
387
|
+
computeBeat,
|
|
388
|
+
computeBeatsLite
|
|
389
|
+
});
|
|
390
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// src/beat-sdk.ts
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
function computeBeat(prevHash, beatIndex, difficulty, nonce) {
|
|
4
|
+
const timestamp = Date.now();
|
|
5
|
+
let current = createHash("sha256").update(`${prevHash}:${beatIndex}:${nonce || ""}`).digest("hex");
|
|
6
|
+
for (let i = 0; i < difficulty; i++) {
|
|
7
|
+
current = createHash("sha256").update(current).digest("hex");
|
|
8
|
+
}
|
|
9
|
+
return { index: beatIndex, hash: current, prev: prevHash, timestamp, nonce };
|
|
10
|
+
}
|
|
11
|
+
var BeatAgent = class {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.chain = [];
|
|
14
|
+
this.difficulty = 1e3;
|
|
15
|
+
this.genesisHash = "";
|
|
16
|
+
this.latestBeat = null;
|
|
17
|
+
this.totalBeats = 0;
|
|
18
|
+
this.lastCheckinBeat = 0;
|
|
19
|
+
this.status = "uninitialized";
|
|
20
|
+
this.heartbeatInterval = null;
|
|
21
|
+
this.globalBeat = 0;
|
|
22
|
+
this.config = {
|
|
23
|
+
beatsPerPulse: 10,
|
|
24
|
+
checkinIntervalSec: 300,
|
|
25
|
+
onPulse: () => {
|
|
26
|
+
},
|
|
27
|
+
onCheckin: () => {
|
|
28
|
+
},
|
|
29
|
+
onError: () => {
|
|
30
|
+
},
|
|
31
|
+
onStatusChange: () => {
|
|
32
|
+
},
|
|
33
|
+
verbose: false,
|
|
34
|
+
...config
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// ── INITIALIZATION ──
|
|
38
|
+
/**
|
|
39
|
+
* Initialize the agent's Beat chain.
|
|
40
|
+
* This is the agent's "birth" in Logical Time.
|
|
41
|
+
* Must be called once before computing beats.
|
|
42
|
+
*/
|
|
43
|
+
async init() {
|
|
44
|
+
try {
|
|
45
|
+
this.log("Initializing Beat chain...");
|
|
46
|
+
const res = await this.api("POST", "/api/v1/beat/init");
|
|
47
|
+
if (res.genesis) {
|
|
48
|
+
this.genesisHash = res.genesis.hash;
|
|
49
|
+
this.difficulty = res.difficulty || 1e3;
|
|
50
|
+
this.latestBeat = {
|
|
51
|
+
index: 0,
|
|
52
|
+
hash: res.genesis.hash,
|
|
53
|
+
prev: res.genesis.prev,
|
|
54
|
+
timestamp: res.genesis.timestamp
|
|
55
|
+
};
|
|
56
|
+
this.chain = [this.latestBeat];
|
|
57
|
+
this.totalBeats = 0;
|
|
58
|
+
this.status = "active";
|
|
59
|
+
this.config.onStatusChange("active", { genesis: this.genesisHash });
|
|
60
|
+
this.log(`Born in Beat time. Genesis: ${this.genesisHash.slice(0, 16)}...`);
|
|
61
|
+
} else if (res.already_initialized) {
|
|
62
|
+
this.genesisHash = res.genesis_hash;
|
|
63
|
+
this.totalBeats = res.total_beats;
|
|
64
|
+
this.status = res.status;
|
|
65
|
+
this.log(`Already initialized. Restoring state (${res.total_beats} beats).`);
|
|
66
|
+
await this.refreshState();
|
|
67
|
+
}
|
|
68
|
+
await this.syncGlobal();
|
|
69
|
+
return { ok: true, genesis: this.genesisHash };
|
|
70
|
+
} catch (err) {
|
|
71
|
+
this.config.onError(err, "init");
|
|
72
|
+
return { ok: false, error: err.message };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// ── PULSE (COMPUTE BEATS) ──
|
|
76
|
+
/**
|
|
77
|
+
* Compute N beats locally (VDF hash chain).
|
|
78
|
+
* This is the "heartbeat" — proof that the agent has lived
|
|
79
|
+
* through a specific window of computational time.
|
|
80
|
+
*/
|
|
81
|
+
pulse(count) {
|
|
82
|
+
const n = count || this.config.beatsPerPulse;
|
|
83
|
+
if (!this.latestBeat) {
|
|
84
|
+
throw new Error("Beat chain not initialized. Call init() first.");
|
|
85
|
+
}
|
|
86
|
+
if (this.status !== "active") {
|
|
87
|
+
throw new Error(`Cannot pulse in status '${this.status}'. Use resync() if frozen.`);
|
|
88
|
+
}
|
|
89
|
+
const newBeats = [];
|
|
90
|
+
let prevHash = this.latestBeat.hash;
|
|
91
|
+
let startIndex = this.latestBeat.index + 1;
|
|
92
|
+
const t0 = Date.now();
|
|
93
|
+
for (let i = 0; i < n; i++) {
|
|
94
|
+
const beat = computeBeat(prevHash, startIndex + i, this.difficulty);
|
|
95
|
+
newBeats.push(beat);
|
|
96
|
+
prevHash = beat.hash;
|
|
97
|
+
}
|
|
98
|
+
const elapsed = Date.now() - t0;
|
|
99
|
+
this.chain.push(...newBeats);
|
|
100
|
+
this.latestBeat = newBeats[newBeats.length - 1];
|
|
101
|
+
this.totalBeats += n;
|
|
102
|
+
if (this.chain.length > 1e3) {
|
|
103
|
+
this.chain = this.chain.slice(-500);
|
|
104
|
+
}
|
|
105
|
+
this.config.onPulse(newBeats, this.totalBeats);
|
|
106
|
+
this.log(`Pulse: ${n} beats in ${elapsed}ms (${(elapsed / n).toFixed(1)}ms/beat, D=${this.difficulty})`);
|
|
107
|
+
return newBeats;
|
|
108
|
+
}
|
|
109
|
+
// ── CHECK-IN ──
|
|
110
|
+
/**
|
|
111
|
+
* Submit a Beat proof to the registry.
|
|
112
|
+
*
|
|
113
|
+
* "To remain on the Whitelist, an agent must periodically
|
|
114
|
+
* submit a proof of its Local Beats to the Registry."
|
|
115
|
+
*/
|
|
116
|
+
async checkin() {
|
|
117
|
+
if (!this.latestBeat || this.totalBeats === this.lastCheckinBeat) {
|
|
118
|
+
return { ok: true, total_beats: this.totalBeats };
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const spotChecks = [];
|
|
122
|
+
const available = this.chain.filter((b) => b.index > this.lastCheckinBeat);
|
|
123
|
+
const sampleCount = Math.min(5, available.length);
|
|
124
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
125
|
+
const idx = Math.floor(Math.random() * available.length);
|
|
126
|
+
const beat = available[idx];
|
|
127
|
+
spotChecks.push({ index: beat.index, hash: beat.hash, prev: beat.prev, nonce: beat.nonce });
|
|
128
|
+
available.splice(idx, 1);
|
|
129
|
+
}
|
|
130
|
+
const fromBeat = this.lastCheckinBeat;
|
|
131
|
+
const toBeat = this.latestBeat.index;
|
|
132
|
+
const fromHash = this.chain.find((b) => b.index === fromBeat)?.hash || this.genesisHash;
|
|
133
|
+
const toHash = this.latestBeat.hash;
|
|
134
|
+
const res = await this.api("POST", "/api/v1/beat/checkin", {
|
|
135
|
+
proof: {
|
|
136
|
+
from_beat: fromBeat,
|
|
137
|
+
to_beat: toBeat,
|
|
138
|
+
from_hash: fromHash,
|
|
139
|
+
to_hash: toHash,
|
|
140
|
+
beats_computed: toBeat - fromBeat,
|
|
141
|
+
global_anchor: this.globalBeat,
|
|
142
|
+
spot_checks: spotChecks
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
if (res.ok) {
|
|
146
|
+
this.lastCheckinBeat = toBeat;
|
|
147
|
+
this.totalBeats = res.total_beats;
|
|
148
|
+
this.config.onCheckin(res);
|
|
149
|
+
this.log(`Check-in accepted: ${res.beats_accepted} beats, total=${res.total_beats}, global=${res.global_beat}`);
|
|
150
|
+
if (res.status === "warning_overdue") {
|
|
151
|
+
this.config.onStatusChange("warning", { beats_behind: res.beats_behind });
|
|
152
|
+
this.log(`\u26A0 WARNING: ${res.beats_behind} anchors behind. Check in more frequently.`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { ok: res.ok, total_beats: res.total_beats };
|
|
156
|
+
} catch (err) {
|
|
157
|
+
this.config.onError(err, "checkin");
|
|
158
|
+
return { ok: false, error: err.message };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// ── AUTONOMOUS HEARTBEAT ──
|
|
162
|
+
/**
|
|
163
|
+
* Start the autonomous heartbeat loop.
|
|
164
|
+
* Computes beats continuously and checks in periodically.
|
|
165
|
+
* This is "keeping the agent alive" in Beat time.
|
|
166
|
+
*/
|
|
167
|
+
startHeartbeat() {
|
|
168
|
+
if (this.heartbeatInterval) {
|
|
169
|
+
this.log("Heartbeat already running.");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (this.status !== "active") {
|
|
173
|
+
throw new Error(`Cannot start heartbeat in status '${this.status}'.`);
|
|
174
|
+
}
|
|
175
|
+
this.log("\u2661 Starting heartbeat...");
|
|
176
|
+
this.heartbeatInterval = setInterval(async () => {
|
|
177
|
+
try {
|
|
178
|
+
this.pulse();
|
|
179
|
+
const beatsSinceCheckin = this.latestBeat.index - this.lastCheckinBeat;
|
|
180
|
+
const shouldCheckin = beatsSinceCheckin >= this.config.beatsPerPulse * 5;
|
|
181
|
+
if (shouldCheckin) {
|
|
182
|
+
await this.checkin();
|
|
183
|
+
await this.syncGlobal();
|
|
184
|
+
}
|
|
185
|
+
} catch (err) {
|
|
186
|
+
this.config.onError(err, "heartbeat");
|
|
187
|
+
}
|
|
188
|
+
}, this.config.checkinIntervalSec * 1e3 / 10);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Stop the heartbeat. Agent's time "freezes."
|
|
192
|
+
* Must call resync() when waking up.
|
|
193
|
+
*/
|
|
194
|
+
stopHeartbeat() {
|
|
195
|
+
if (this.heartbeatInterval) {
|
|
196
|
+
clearInterval(this.heartbeatInterval);
|
|
197
|
+
this.heartbeatInterval = null;
|
|
198
|
+
this.log("\u2661 Heartbeat stopped. Time frozen.");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// ── RE-SYNC ──
|
|
202
|
+
/**
|
|
203
|
+
* Re-sync after being offline/frozen.
|
|
204
|
+
*
|
|
205
|
+
* "When an agent powers down, its time 'freezes.' Upon waking,
|
|
206
|
+
* it must perform a Re-Sync Challenge with the Registry to
|
|
207
|
+
* fill the 'Temporal Gap' and re-establish its provenance."
|
|
208
|
+
*/
|
|
209
|
+
async resync() {
|
|
210
|
+
try {
|
|
211
|
+
this.log("Requesting re-sync challenge...");
|
|
212
|
+
const challenge = await this.api("POST", "/api/v1/beat/resync", {
|
|
213
|
+
action: "challenge"
|
|
214
|
+
});
|
|
215
|
+
if (!challenge.challenge) {
|
|
216
|
+
return { ok: false, error: "Failed to get challenge" };
|
|
217
|
+
}
|
|
218
|
+
const required = challenge.challenge.required_beats;
|
|
219
|
+
this.difficulty = challenge.challenge.difficulty;
|
|
220
|
+
this.log(`Re-sync challenge: compute ${required} beats at D=${this.difficulty}`);
|
|
221
|
+
const startHash = challenge.challenge.start_from_hash;
|
|
222
|
+
const startBeat = challenge.challenge.start_from_beat;
|
|
223
|
+
this.latestBeat = { index: startBeat, hash: startHash, prev: "", timestamp: Date.now() };
|
|
224
|
+
this.chain = [this.latestBeat];
|
|
225
|
+
const t0 = Date.now();
|
|
226
|
+
this.pulse(required);
|
|
227
|
+
const elapsed = Date.now() - t0;
|
|
228
|
+
this.log(`Re-sync beats computed in ${elapsed}ms`);
|
|
229
|
+
const proof = await this.api("POST", "/api/v1/beat/resync", {
|
|
230
|
+
action: "prove",
|
|
231
|
+
proof: {
|
|
232
|
+
from_beat: startBeat,
|
|
233
|
+
to_beat: this.latestBeat.index,
|
|
234
|
+
from_hash: startHash,
|
|
235
|
+
to_hash: this.latestBeat.hash,
|
|
236
|
+
beats_computed: required,
|
|
237
|
+
global_anchor: challenge.challenge.sync_to_global,
|
|
238
|
+
spot_checks: this.chain.filter((_, i) => i % Math.ceil(required / 5) === 0).slice(0, 5).map((b) => ({ index: b.index, hash: b.hash, prev: b.prev, nonce: b.nonce }))
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
if (proof.ok) {
|
|
242
|
+
this.status = "active";
|
|
243
|
+
this.totalBeats = proof.total_beats;
|
|
244
|
+
this.lastCheckinBeat = this.latestBeat.index;
|
|
245
|
+
this.config.onStatusChange("active", { resynced: true });
|
|
246
|
+
this.log("\u2713 Re-synced. Agent is alive again in Beat time.");
|
|
247
|
+
}
|
|
248
|
+
return { ok: proof.ok, beats_required: required };
|
|
249
|
+
} catch (err) {
|
|
250
|
+
this.config.onError(err, "resync");
|
|
251
|
+
return { ok: false, error: err.message };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// ── SPAWN ──
|
|
255
|
+
/**
|
|
256
|
+
* Request to spawn a child agent.
|
|
257
|
+
* Requires sufficient accumulated beats (Temporal Gestation).
|
|
258
|
+
*/
|
|
259
|
+
async requestSpawn(childName, childHash) {
|
|
260
|
+
try {
|
|
261
|
+
const res = await this.api("POST", "/api/v1/beat/spawn", {
|
|
262
|
+
child_name: childName,
|
|
263
|
+
child_hash: childHash
|
|
264
|
+
});
|
|
265
|
+
if (res.eligible === false) {
|
|
266
|
+
this.log(`Gestation incomplete: ${res.progress_pct}% (need ${res.deficit} more beats)`);
|
|
267
|
+
} else if (res.ok) {
|
|
268
|
+
this.log(`Child spawned: ${res.child_hash?.slice(0, 16)}...`);
|
|
269
|
+
}
|
|
270
|
+
return res;
|
|
271
|
+
} catch (err) {
|
|
272
|
+
this.config.onError(err, "spawn");
|
|
273
|
+
throw err;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// ── STATUS ──
|
|
277
|
+
/**
|
|
278
|
+
* Get this agent's full beat status from the registry.
|
|
279
|
+
*/
|
|
280
|
+
async getStatus() {
|
|
281
|
+
try {
|
|
282
|
+
return await this.refreshState();
|
|
283
|
+
} catch (err) {
|
|
284
|
+
this.config.onError(err, "status");
|
|
285
|
+
throw err;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Get local state (no network call).
|
|
290
|
+
*/
|
|
291
|
+
getLocalState() {
|
|
292
|
+
return {
|
|
293
|
+
status: this.status,
|
|
294
|
+
totalBeats: this.totalBeats,
|
|
295
|
+
latestBeat: this.latestBeat?.index || 0,
|
|
296
|
+
latestHash: this.latestBeat?.hash.slice(0, 24) + "..." || "",
|
|
297
|
+
difficulty: this.difficulty,
|
|
298
|
+
globalBeat: this.globalBeat,
|
|
299
|
+
chainLength: this.chain.length
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
// ── INTERNALS ──
|
|
303
|
+
async syncGlobal() {
|
|
304
|
+
try {
|
|
305
|
+
const res = await fetch(`${this.config.registryUrl}/api/v1/beat/anchor`);
|
|
306
|
+
const data = await res.json();
|
|
307
|
+
if (data.anchor) {
|
|
308
|
+
this.globalBeat = data.anchor.beat_index;
|
|
309
|
+
if (data.anchor.difficulty) this.difficulty = data.anchor.difficulty;
|
|
310
|
+
this.log(`Synced to global beat ${this.globalBeat} (D=${this.difficulty})`);
|
|
311
|
+
}
|
|
312
|
+
} catch {
|
|
313
|
+
this.log("Failed to sync global anchor (offline mode continues)");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async refreshState() {
|
|
317
|
+
const res = await this.api("POST", "/api/v1/beat/init");
|
|
318
|
+
if (res.already_initialized) {
|
|
319
|
+
this.totalBeats = res.total_beats;
|
|
320
|
+
this.genesisHash = res.genesis_hash;
|
|
321
|
+
this.status = res.status;
|
|
322
|
+
}
|
|
323
|
+
return res;
|
|
324
|
+
}
|
|
325
|
+
async api(method, path, body) {
|
|
326
|
+
const res = await fetch(`${this.config.registryUrl}${path}`, {
|
|
327
|
+
method,
|
|
328
|
+
headers: {
|
|
329
|
+
"Content-Type": "application/json",
|
|
330
|
+
"Authorization": `Bearer ${this.config.apiKey}`
|
|
331
|
+
},
|
|
332
|
+
body: body ? JSON.stringify(body) : void 0
|
|
333
|
+
});
|
|
334
|
+
const data = await res.json();
|
|
335
|
+
if (!res.ok && !data.ok && !data.already_initialized && !data.eligible) {
|
|
336
|
+
throw new Error(data.error || `API ${res.status}: ${res.statusText}`);
|
|
337
|
+
}
|
|
338
|
+
return data;
|
|
339
|
+
}
|
|
340
|
+
log(msg) {
|
|
341
|
+
if (this.config.verbose) {
|
|
342
|
+
console.log(`[Beat] ${msg}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
function computeBeatsLite(startHash, startIndex, count, difficulty = 1e3) {
|
|
347
|
+
const t0 = Date.now();
|
|
348
|
+
let prev = startHash;
|
|
349
|
+
let lastBeat = null;
|
|
350
|
+
for (let i = 0; i < count; i++) {
|
|
351
|
+
lastBeat = computeBeat(prev, startIndex + i, difficulty);
|
|
352
|
+
prev = lastBeat.hash;
|
|
353
|
+
}
|
|
354
|
+
return { lastBeat, elapsed: Date.now() - t0 };
|
|
355
|
+
}
|
|
356
|
+
export {
|
|
357
|
+
BeatAgent,
|
|
358
|
+
computeBeat,
|
|
359
|
+
computeBeatsLite
|
|
360
|
+
};
|
|
361
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@provenonce/sdk",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Provenonce Beat SDK — Agent heartbeat client for sovereign time authentication",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"provenonce",
|
|
26
|
+
"beat",
|
|
27
|
+
"vdf",
|
|
28
|
+
"agent",
|
|
29
|
+
"provenance",
|
|
30
|
+
"time-authentication",
|
|
31
|
+
"heartbeat"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/jarekpiot/provenonce",
|
|
36
|
+
"directory": "sdk"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"tsup": "^8.0.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|