clinch-core 0.3.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.d.ts +2 -0
- package/dist/index.js +23 -24
- package/package.json +1 -1
- package/src/index.ts +23 -27
package/dist/index.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export declare class ClinchCore extends EventEmitter {
|
|
|
51
51
|
private isSandboxMode;
|
|
52
52
|
private sandboxModelContext;
|
|
53
53
|
private sandboxMaxTurns;
|
|
54
|
+
get activeNegotiationId(): string | null;
|
|
54
55
|
constructor(config?: ClinchConfig);
|
|
55
56
|
private setStatus;
|
|
56
57
|
initialize(cachedToken?: string): Promise<void>;
|
|
@@ -83,6 +84,7 @@ export interface SellerRecord {
|
|
|
83
84
|
supported_modes: string[];
|
|
84
85
|
categories: string[];
|
|
85
86
|
capabilities: string[];
|
|
87
|
+
display_name?: string;
|
|
86
88
|
}
|
|
87
89
|
export declare class ClinchSeller extends EventEmitter {
|
|
88
90
|
private config;
|
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,10 +71,12 @@ 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;
|
|
77
|
+
get activeNegotiationId() {
|
|
78
|
+
return Array.from(this.activeSessions.keys()).pop() || null;
|
|
79
|
+
}
|
|
73
80
|
constructor(config = {}) {
|
|
74
81
|
super();
|
|
75
82
|
this.config = { timeoutMs: 5000, ...config };
|
|
@@ -224,9 +231,6 @@ class ClinchCore extends events_1.EventEmitter {
|
|
|
224
231
|
this.ws = null;
|
|
225
232
|
}
|
|
226
233
|
}
|
|
227
|
-
// --------------------------------------------------------------------------
|
|
228
|
-
// SESSION STATE MANAGEMENT (For Enterprise Horizontal Scaling & Reconnects)
|
|
229
|
-
// --------------------------------------------------------------------------
|
|
230
234
|
exportSessionState(sessionId) {
|
|
231
235
|
const session = this.activeSessions.get(sessionId);
|
|
232
236
|
if (!session)
|
|
@@ -262,9 +266,6 @@ class ClinchCore extends events_1.EventEmitter {
|
|
|
262
266
|
getSession(sessionId) {
|
|
263
267
|
return this.activeSessions.get(sessionId);
|
|
264
268
|
}
|
|
265
|
-
// --------------------------------------------------------------------------
|
|
266
|
-
// UNIVERSAL PROMPT BUILDER
|
|
267
|
-
// --------------------------------------------------------------------------
|
|
268
269
|
buildAgentPrompt(sessionId, incomingMessage) {
|
|
269
270
|
const session = this.activeSessions.get(sessionId);
|
|
270
271
|
if (!session)
|
|
@@ -299,9 +300,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
299
300
|
"message": "<One concise sentence of negotiation dialogue>"
|
|
300
301
|
}`;
|
|
301
302
|
}
|
|
302
|
-
// --------------------------------------------------------------------------
|
|
303
|
-
// PROTOCOL OPERATIONS
|
|
304
|
-
// --------------------------------------------------------------------------
|
|
305
303
|
async search(query, mode) {
|
|
306
304
|
let url = `/api/discover?category=${encodeURIComponent(query)}`;
|
|
307
305
|
if (mode)
|
|
@@ -351,7 +349,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
351
349
|
headers: { 'Content-Type': 'application/json' },
|
|
352
350
|
body: JSON.stringify({ ...payload, buyer_sig })
|
|
353
351
|
});
|
|
354
|
-
// Sync state if deal reached
|
|
355
352
|
if (response.msg_type === 'accept' || response.status === 'COMMITTED') {
|
|
356
353
|
session.status = 'CLOSED';
|
|
357
354
|
session.lastKnownPrice = response.price || price;
|
|
@@ -371,9 +368,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
371
368
|
session.exitTokenHash = res.token_hash;
|
|
372
369
|
return res.token_hash;
|
|
373
370
|
}
|
|
374
|
-
// --------------------------------------------------------------------------
|
|
375
|
-
// OUT-OF-THE-BOX SANDBOX (AUTO-TURN ENGINE)
|
|
376
|
-
// --------------------------------------------------------------------------
|
|
377
371
|
async sandbox(config = {}) {
|
|
378
372
|
this.isSandboxMode = true;
|
|
379
373
|
this.sandboxMaxTurns = config.maxTurns || 6;
|
|
@@ -388,7 +382,7 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
388
382
|
}
|
|
389
383
|
async setupSandbox(config = {}) {
|
|
390
384
|
if (this.sandboxModelContext)
|
|
391
|
-
return;
|
|
385
|
+
return;
|
|
392
386
|
const settings = {
|
|
393
387
|
downloadLLM: true,
|
|
394
388
|
modelUrl: "https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF/resolve/main/qwen2.5-1.5b-instruct-q4_k_m.gguf",
|
|
@@ -471,7 +465,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
471
465
|
async sandboxEvaluate(session, incomingOffer) {
|
|
472
466
|
const { LlamaChatSession, ChatMLChatWrapper } = await Promise.resolve().then(() => __importStar(require('node-llama-cpp')));
|
|
473
467
|
const systemPrompt = this.buildAgentPrompt(session.sessionId, incomingOffer);
|
|
474
|
-
// State Isolation: Bind sequence to the session, not the global class!
|
|
475
468
|
if (!session.sandboxSequence) {
|
|
476
469
|
session.sandboxSequence = this.sandboxModelContext.getSequence();
|
|
477
470
|
session.sandboxSession = new LlamaChatSession({
|
|
@@ -487,9 +480,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
487
480
|
});
|
|
488
481
|
return responseText;
|
|
489
482
|
}
|
|
490
|
-
// --------------------------------------------------------------------------
|
|
491
|
-
// UTILITIES
|
|
492
|
-
// --------------------------------------------------------------------------
|
|
493
483
|
extractPrice(text, prefix) {
|
|
494
484
|
const regex = new RegExp(`${prefix}\\s*\\:?\\s*\\$?(\\d+(?:\\.\\d{2})?)`, 'i');
|
|
495
485
|
const match = text.match(regex);
|
|
@@ -546,10 +536,19 @@ class ClinchSeller extends events_1.EventEmitter {
|
|
|
546
536
|
super();
|
|
547
537
|
this.config = { timeoutMs: 8000, ...config };
|
|
548
538
|
if (config.privateKeyHex) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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
|
+
}
|
|
553
552
|
}
|
|
554
553
|
else {
|
|
555
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,11 +97,14 @@ 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
|
|
|
104
|
+
public get activeNegotiationId(): string | null {
|
|
105
|
+
return Array.from(this.activeSessions.keys()).pop() || null;
|
|
106
|
+
}
|
|
107
|
+
|
|
103
108
|
constructor(config: ClinchConfig = {}) {
|
|
104
109
|
super();
|
|
105
110
|
this.config = { timeoutMs: 5000, ...config };
|
|
@@ -261,9 +266,6 @@ export class ClinchCore extends EventEmitter {
|
|
|
261
266
|
if (this.ws) { this.ws.close(); this.ws = null; }
|
|
262
267
|
}
|
|
263
268
|
|
|
264
|
-
// --------------------------------------------------------------------------
|
|
265
|
-
// SESSION STATE MANAGEMENT (For Enterprise Horizontal Scaling & Reconnects)
|
|
266
|
-
// --------------------------------------------------------------------------
|
|
267
269
|
public exportSessionState(sessionId: string): string {
|
|
268
270
|
const session = this.activeSessions.get(sessionId);
|
|
269
271
|
if (!session) throw new Error("Session not found");
|
|
@@ -284,7 +286,6 @@ export class ClinchCore extends EventEmitter {
|
|
|
284
286
|
|
|
285
287
|
public importSessionState(serializedData: string): void {
|
|
286
288
|
const data = JSON.parse(serializedData);
|
|
287
|
-
|
|
288
289
|
const secretKey = fromHex(data.ephemeralSecretKeyHex);
|
|
289
290
|
const keyPair = nacl.sign.keyPair.fromSecretKey(secretKey);
|
|
290
291
|
|
|
@@ -306,9 +307,6 @@ export class ClinchCore extends EventEmitter {
|
|
|
306
307
|
return this.activeSessions.get(sessionId);
|
|
307
308
|
}
|
|
308
309
|
|
|
309
|
-
// --------------------------------------------------------------------------
|
|
310
|
-
// UNIVERSAL PROMPT BUILDER
|
|
311
|
-
// --------------------------------------------------------------------------
|
|
312
310
|
public buildAgentPrompt(sessionId: string, incomingMessage: string): string {
|
|
313
311
|
const session = this.activeSessions.get(sessionId);
|
|
314
312
|
if (!session) throw new Error("Cannot build prompt: Session not found.");
|
|
@@ -345,9 +343,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
345
343
|
}`;
|
|
346
344
|
}
|
|
347
345
|
|
|
348
|
-
// --------------------------------------------------------------------------
|
|
349
|
-
// PROTOCOL OPERATIONS
|
|
350
|
-
// --------------------------------------------------------------------------
|
|
351
346
|
async search(query: string, mode?: string): Promise<any> {
|
|
352
347
|
let url = `/api/discover?category=${encodeURIComponent(query)}`;
|
|
353
348
|
if (mode) url += `&mode=${encodeURIComponent(mode)}`;
|
|
@@ -409,7 +404,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
409
404
|
body: JSON.stringify({ ...payload, buyer_sig })
|
|
410
405
|
});
|
|
411
406
|
|
|
412
|
-
// Sync state if deal reached
|
|
413
407
|
if (response.msg_type === 'accept' || response.status === 'COMMITTED') {
|
|
414
408
|
session.status = 'CLOSED';
|
|
415
409
|
session.lastKnownPrice = response.price || price;
|
|
@@ -432,9 +426,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
432
426
|
return res.token_hash;
|
|
433
427
|
}
|
|
434
428
|
|
|
435
|
-
// --------------------------------------------------------------------------
|
|
436
|
-
// OUT-OF-THE-BOX SANDBOX (AUTO-TURN ENGINE)
|
|
437
|
-
// --------------------------------------------------------------------------
|
|
438
429
|
async sandbox(config: SandboxConfig = {}): Promise<void> {
|
|
439
430
|
this.isSandboxMode = true;
|
|
440
431
|
this.sandboxMaxTurns = config.maxTurns || 6;
|
|
@@ -450,7 +441,7 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
450
441
|
}
|
|
451
442
|
|
|
452
443
|
private async setupSandbox(config: SandboxConfig = {}): Promise<void> {
|
|
453
|
-
if (this.sandboxModelContext) return;
|
|
444
|
+
if (this.sandboxModelContext) return;
|
|
454
445
|
|
|
455
446
|
const settings = {
|
|
456
447
|
downloadLLM: true,
|
|
@@ -541,7 +532,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
541
532
|
|
|
542
533
|
const systemPrompt = this.buildAgentPrompt(session.sessionId, incomingOffer);
|
|
543
534
|
|
|
544
|
-
// State Isolation: Bind sequence to the session, not the global class!
|
|
545
535
|
if (!session.sandboxSequence) {
|
|
546
536
|
session.sandboxSequence = this.sandboxModelContext.getSequence();
|
|
547
537
|
session.sandboxSession = new LlamaChatSession({
|
|
@@ -560,9 +550,6 @@ You MUST respond ONLY in valid JSON matching this exact schema. Do not include m
|
|
|
560
550
|
return responseText;
|
|
561
551
|
}
|
|
562
552
|
|
|
563
|
-
// --------------------------------------------------------------------------
|
|
564
|
-
// UTILITIES
|
|
565
|
-
// --------------------------------------------------------------------------
|
|
566
553
|
private extractPrice(text: string, prefix: string): number | null {
|
|
567
554
|
const regex = new RegExp(`${prefix}\\s*\\:?\\s*\\$?(\\d+(?:\\.\\d{2})?)`, 'i');
|
|
568
555
|
const match = text.match(regex);
|
|
@@ -618,6 +605,7 @@ export interface SellerRecord {
|
|
|
618
605
|
supported_modes: string[];
|
|
619
606
|
categories: string[];
|
|
620
607
|
capabilities: string[];
|
|
608
|
+
display_name?: string;
|
|
621
609
|
}
|
|
622
610
|
|
|
623
611
|
export class ClinchSeller extends EventEmitter {
|
|
@@ -631,10 +619,18 @@ export class ClinchSeller extends EventEmitter {
|
|
|
631
619
|
this.config = { timeoutMs: 8000, ...config };
|
|
632
620
|
|
|
633
621
|
if (config.privateKeyHex) {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
+
}
|
|
638
634
|
} else {
|
|
639
635
|
const kp = nacl.sign.keyPair();
|
|
640
636
|
this.identityPrivKey = kp.secretKey;
|