clinch-core 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +20 -25
- package/package.json +1 -1
- package/src/index.ts +19 -29
package/dist/index.js
CHANGED
|
@@ -50,7 +50,12 @@ function toHex(arr) {
|
|
|
50
50
|
return Array.from(arr).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
51
51
|
}
|
|
52
52
|
function fromHex(hex) {
|
|
53
|
-
|
|
53
|
+
// Strips all spaces, newlines, and rogue quotes from .env strings
|
|
54
|
+
const clean = hex.replace(/[^0-9a-fA-F]/g, '');
|
|
55
|
+
const match = clean.match(/.{1,2}/g);
|
|
56
|
+
if (!match)
|
|
57
|
+
return new Uint8Array(0);
|
|
58
|
+
return new Uint8Array(match.map(byte => parseInt(byte, 16)));
|
|
54
59
|
}
|
|
55
60
|
// ============================================================================
|
|
56
61
|
// THE CLINCH CORE LIBRARY (Buyer)
|
|
@@ -66,11 +71,9 @@ class ClinchCore extends events_1.EventEmitter {
|
|
|
66
71
|
identityPubKey;
|
|
67
72
|
activeSessions = new Map();
|
|
68
73
|
ws = null;
|
|
69
|
-
// Sandbox Engine Base (Model/Context are global, Sequences are per-session)
|
|
70
74
|
isSandboxMode = false;
|
|
71
75
|
sandboxModelContext = null;
|
|
72
76
|
sandboxMaxTurns = 6;
|
|
73
|
-
// Legacy support for single-tenant applications checking the last ID
|
|
74
77
|
get activeNegotiationId() {
|
|
75
78
|
return Array.from(this.activeSessions.keys()).pop() || null;
|
|
76
79
|
}
|
|
@@ -228,9 +231,6 @@ class ClinchCore extends events_1.EventEmitter {
|
|
|
228
231
|
this.ws = null;
|
|
229
232
|
}
|
|
230
233
|
}
|
|
231
|
-
// --------------------------------------------------------------------------
|
|
232
|
-
// SESSION STATE MANAGEMENT (For Enterprise Horizontal Scaling & Reconnects)
|
|
233
|
-
// --------------------------------------------------------------------------
|
|
234
234
|
exportSessionState(sessionId) {
|
|
235
235
|
const session = this.activeSessions.get(sessionId);
|
|
236
236
|
if (!session)
|
|
@@ -266,9 +266,6 @@ class ClinchCore extends events_1.EventEmitter {
|
|
|
266
266
|
getSession(sessionId) {
|
|
267
267
|
return this.activeSessions.get(sessionId);
|
|
268
268
|
}
|
|
269
|
-
// --------------------------------------------------------------------------
|
|
270
|
-
// UNIVERSAL PROMPT BUILDER
|
|
271
|
-
// --------------------------------------------------------------------------
|
|
272
269
|
buildAgentPrompt(sessionId, incomingMessage) {
|
|
273
270
|
const session = this.activeSessions.get(sessionId);
|
|
274
271
|
if (!session)
|
|
@@ -303,9 +300,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
303
300
|
"message": "<One concise sentence of negotiation dialogue>"
|
|
304
301
|
}`;
|
|
305
302
|
}
|
|
306
|
-
// --------------------------------------------------------------------------
|
|
307
|
-
// PROTOCOL OPERATIONS
|
|
308
|
-
// --------------------------------------------------------------------------
|
|
309
303
|
async search(query, mode) {
|
|
310
304
|
let url = `/api/discover?category=${encodeURIComponent(query)}`;
|
|
311
305
|
if (mode)
|
|
@@ -355,7 +349,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
355
349
|
headers: { 'Content-Type': 'application/json' },
|
|
356
350
|
body: JSON.stringify({ ...payload, buyer_sig })
|
|
357
351
|
});
|
|
358
|
-
// Sync state if deal reached
|
|
359
352
|
if (response.msg_type === 'accept' || response.status === 'COMMITTED') {
|
|
360
353
|
session.status = 'CLOSED';
|
|
361
354
|
session.lastKnownPrice = response.price || price;
|
|
@@ -375,9 +368,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
375
368
|
session.exitTokenHash = res.token_hash;
|
|
376
369
|
return res.token_hash;
|
|
377
370
|
}
|
|
378
|
-
// --------------------------------------------------------------------------
|
|
379
|
-
// OUT-OF-THE-BOX SANDBOX (AUTO-TURN ENGINE)
|
|
380
|
-
// --------------------------------------------------------------------------
|
|
381
371
|
async sandbox(config = {}) {
|
|
382
372
|
this.isSandboxMode = true;
|
|
383
373
|
this.sandboxMaxTurns = config.maxTurns || 6;
|
|
@@ -392,7 +382,7 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
392
382
|
}
|
|
393
383
|
async setupSandbox(config = {}) {
|
|
394
384
|
if (this.sandboxModelContext)
|
|
395
|
-
return;
|
|
385
|
+
return;
|
|
396
386
|
const settings = {
|
|
397
387
|
downloadLLM: true,
|
|
398
388
|
modelUrl: "https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF/resolve/main/qwen2.5-1.5b-instruct-q4_k_m.gguf",
|
|
@@ -475,7 +465,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
475
465
|
async sandboxEvaluate(session, incomingOffer) {
|
|
476
466
|
const { LlamaChatSession, ChatMLChatWrapper } = await Promise.resolve().then(() => __importStar(require('node-llama-cpp')));
|
|
477
467
|
const systemPrompt = this.buildAgentPrompt(session.sessionId, incomingOffer);
|
|
478
|
-
// State Isolation: Bind sequence to the session, not the global class!
|
|
479
468
|
if (!session.sandboxSequence) {
|
|
480
469
|
session.sandboxSequence = this.sandboxModelContext.getSequence();
|
|
481
470
|
session.sandboxSession = new LlamaChatSession({
|
|
@@ -491,9 +480,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
491
480
|
});
|
|
492
481
|
return responseText;
|
|
493
482
|
}
|
|
494
|
-
// --------------------------------------------------------------------------
|
|
495
|
-
// UTILITIES
|
|
496
|
-
// --------------------------------------------------------------------------
|
|
497
483
|
extractPrice(text, prefix) {
|
|
498
484
|
const regex = new RegExp(`${prefix}\\s*\\:?\\s*\\$?(\\d+(?:\\.\\d{2})?)`, 'i');
|
|
499
485
|
const match = text.match(regex);
|
|
@@ -550,10 +536,19 @@ class ClinchSeller extends events_1.EventEmitter {
|
|
|
550
536
|
super();
|
|
551
537
|
this.config = { timeoutMs: 8000, ...config };
|
|
552
538
|
if (config.privateKeyHex) {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
539
|
+
try {
|
|
540
|
+
const cleanHex = config.privateKeyHex.replace(/[^0-9a-fA-F]/g, '');
|
|
541
|
+
if (cleanHex.length !== 128) {
|
|
542
|
+
throw new Error(`Expected 128 hex chars (64 bytes), got ${cleanHex.length}`);
|
|
543
|
+
}
|
|
544
|
+
this.identityPrivKey = fromHex(cleanHex);
|
|
545
|
+
const kp = tweetnacl_1.default.sign.keyPair.fromSecretKey(this.identityPrivKey);
|
|
546
|
+
this.identityPubKey = toHex(kp.publicKey);
|
|
547
|
+
this.emit('log', `[Seller] Loaded permanent identity. PubKey: ${this.identityPubKey.substring(0, 12)}...`);
|
|
548
|
+
}
|
|
549
|
+
catch (e) {
|
|
550
|
+
throw new Error(`[Seller] Invalid privateKeyHex in constructor: ${e.message}`);
|
|
551
|
+
}
|
|
557
552
|
}
|
|
558
553
|
else {
|
|
559
554
|
const kp = tweetnacl_1.default.sign.keyPair();
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -14,7 +14,11 @@ function toHex(arr: Uint8Array | number[]): string {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function fromHex(hex: string): Uint8Array {
|
|
17
|
-
|
|
17
|
+
// Strips all spaces, newlines, and rogue quotes from .env strings
|
|
18
|
+
const clean = hex.replace(/[^0-9a-fA-F]/g, '');
|
|
19
|
+
const match = clean.match(/.{1,2}/g);
|
|
20
|
+
if (!match) return new Uint8Array(0);
|
|
21
|
+
return new Uint8Array(match.map(byte => parseInt(byte, 16)));
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
// ============================================================================
|
|
@@ -56,11 +60,9 @@ export interface SessionState {
|
|
|
56
60
|
exitTokenHash?: string;
|
|
57
61
|
constraints: ConstraintVector;
|
|
58
62
|
|
|
59
|
-
// Isolated State Tracking (Crucial for concurrency)
|
|
60
63
|
currentTurn: number;
|
|
61
64
|
lastKnownPrice: number;
|
|
62
65
|
|
|
63
|
-
// Local LLM Context Tracking
|
|
64
66
|
sandboxSequence?: any;
|
|
65
67
|
sandboxSession?: any;
|
|
66
68
|
}
|
|
@@ -95,12 +97,10 @@ export class ClinchCore extends EventEmitter {
|
|
|
95
97
|
private activeSessions = new Map<string, SessionState>();
|
|
96
98
|
private ws: WebSocket | null = null;
|
|
97
99
|
|
|
98
|
-
// Sandbox Engine Base (Model/Context are global, Sequences are per-session)
|
|
99
100
|
private isSandboxMode = false;
|
|
100
101
|
private sandboxModelContext: any = null;
|
|
101
102
|
private sandboxMaxTurns = 6;
|
|
102
103
|
|
|
103
|
-
// Legacy support for single-tenant applications checking the last ID
|
|
104
104
|
public get activeNegotiationId(): string | null {
|
|
105
105
|
return Array.from(this.activeSessions.keys()).pop() || null;
|
|
106
106
|
}
|
|
@@ -266,9 +266,6 @@ export class ClinchCore extends EventEmitter {
|
|
|
266
266
|
if (this.ws) { this.ws.close(); this.ws = null; }
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
// --------------------------------------------------------------------------
|
|
270
|
-
// SESSION STATE MANAGEMENT (For Enterprise Horizontal Scaling & Reconnects)
|
|
271
|
-
// --------------------------------------------------------------------------
|
|
272
269
|
public exportSessionState(sessionId: string): string {
|
|
273
270
|
const session = this.activeSessions.get(sessionId);
|
|
274
271
|
if (!session) throw new Error("Session not found");
|
|
@@ -289,7 +286,6 @@ export class ClinchCore extends EventEmitter {
|
|
|
289
286
|
|
|
290
287
|
public importSessionState(serializedData: string): void {
|
|
291
288
|
const data = JSON.parse(serializedData);
|
|
292
|
-
|
|
293
289
|
const secretKey = fromHex(data.ephemeralSecretKeyHex);
|
|
294
290
|
const keyPair = nacl.sign.keyPair.fromSecretKey(secretKey);
|
|
295
291
|
|
|
@@ -311,9 +307,6 @@ export class ClinchCore extends EventEmitter {
|
|
|
311
307
|
return this.activeSessions.get(sessionId);
|
|
312
308
|
}
|
|
313
309
|
|
|
314
|
-
// --------------------------------------------------------------------------
|
|
315
|
-
// UNIVERSAL PROMPT BUILDER
|
|
316
|
-
// --------------------------------------------------------------------------
|
|
317
310
|
public buildAgentPrompt(sessionId: string, incomingMessage: string): string {
|
|
318
311
|
const session = this.activeSessions.get(sessionId);
|
|
319
312
|
if (!session) throw new Error("Cannot build prompt: Session not found.");
|
|
@@ -350,9 +343,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
350
343
|
}`;
|
|
351
344
|
}
|
|
352
345
|
|
|
353
|
-
// --------------------------------------------------------------------------
|
|
354
|
-
// PROTOCOL OPERATIONS
|
|
355
|
-
// --------------------------------------------------------------------------
|
|
356
346
|
async search(query: string, mode?: string): Promise<any> {
|
|
357
347
|
let url = `/api/discover?category=${encodeURIComponent(query)}`;
|
|
358
348
|
if (mode) url += `&mode=${encodeURIComponent(mode)}`;
|
|
@@ -414,7 +404,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
414
404
|
body: JSON.stringify({ ...payload, buyer_sig })
|
|
415
405
|
});
|
|
416
406
|
|
|
417
|
-
// Sync state if deal reached
|
|
418
407
|
if (response.msg_type === 'accept' || response.status === 'COMMITTED') {
|
|
419
408
|
session.status = 'CLOSED';
|
|
420
409
|
session.lastKnownPrice = response.price || price;
|
|
@@ -437,9 +426,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
437
426
|
return res.token_hash;
|
|
438
427
|
}
|
|
439
428
|
|
|
440
|
-
// --------------------------------------------------------------------------
|
|
441
|
-
// OUT-OF-THE-BOX SANDBOX (AUTO-TURN ENGINE)
|
|
442
|
-
// --------------------------------------------------------------------------
|
|
443
429
|
async sandbox(config: SandboxConfig = {}): Promise<void> {
|
|
444
430
|
this.isSandboxMode = true;
|
|
445
431
|
this.sandboxMaxTurns = config.maxTurns || 6;
|
|
@@ -455,7 +441,7 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
455
441
|
}
|
|
456
442
|
|
|
457
443
|
private async setupSandbox(config: SandboxConfig = {}): Promise<void> {
|
|
458
|
-
if (this.sandboxModelContext) return;
|
|
444
|
+
if (this.sandboxModelContext) return;
|
|
459
445
|
|
|
460
446
|
const settings = {
|
|
461
447
|
downloadLLM: true,
|
|
@@ -546,7 +532,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
546
532
|
|
|
547
533
|
const systemPrompt = this.buildAgentPrompt(session.sessionId, incomingOffer);
|
|
548
534
|
|
|
549
|
-
// State Isolation: Bind sequence to the session, not the global class!
|
|
550
535
|
if (!session.sandboxSequence) {
|
|
551
536
|
session.sandboxSequence = this.sandboxModelContext.getSequence();
|
|
552
537
|
session.sandboxSession = new LlamaChatSession({
|
|
@@ -565,9 +550,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
565
550
|
return responseText;
|
|
566
551
|
}
|
|
567
552
|
|
|
568
|
-
// --------------------------------------------------------------------------
|
|
569
|
-
// UTILITIES
|
|
570
|
-
// --------------------------------------------------------------------------
|
|
571
553
|
private extractPrice(text: string, prefix: string): number | null {
|
|
572
554
|
const regex = new RegExp(`${prefix}\\s*\\:?\\s*\\$?(\\d+(?:\\.\\d{2})?)`, 'i');
|
|
573
555
|
const match = text.match(regex);
|
|
@@ -623,7 +605,7 @@ export interface SellerRecord {
|
|
|
623
605
|
supported_modes: string[];
|
|
624
606
|
categories: string[];
|
|
625
607
|
capabilities: string[];
|
|
626
|
-
display_name?: string;
|
|
608
|
+
display_name?: string;
|
|
627
609
|
}
|
|
628
610
|
|
|
629
611
|
export class ClinchSeller extends EventEmitter {
|
|
@@ -637,10 +619,18 @@ export class ClinchSeller extends EventEmitter {
|
|
|
637
619
|
this.config = { timeoutMs: 8000, ...config };
|
|
638
620
|
|
|
639
621
|
if (config.privateKeyHex) {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
622
|
+
try {
|
|
623
|
+
const cleanHex = config.privateKeyHex.replace(/[^0-9a-fA-F]/g, '');
|
|
624
|
+
if (cleanHex.length !== 128) {
|
|
625
|
+
throw new Error(`Expected 128 hex chars (64 bytes), got ${cleanHex.length}`);
|
|
626
|
+
}
|
|
627
|
+
this.identityPrivKey = fromHex(cleanHex);
|
|
628
|
+
const kp = nacl.sign.keyPair.fromSecretKey(this.identityPrivKey);
|
|
629
|
+
this.identityPubKey = toHex(kp.publicKey);
|
|
630
|
+
this.emit('log', `[Seller] Loaded permanent identity. PubKey: ${this.identityPubKey.substring(0, 12)}...`);
|
|
631
|
+
} catch (e: any) {
|
|
632
|
+
throw new Error(`[Seller] Invalid privateKeyHex in constructor: ${e.message}`);
|
|
633
|
+
}
|
|
644
634
|
} else {
|
|
645
635
|
const kp = nacl.sign.keyPair();
|
|
646
636
|
this.identityPrivKey = kp.secretKey;
|