@consensus-tools/consensus-tools 0.1.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/src/types.ts ADDED
@@ -0,0 +1,232 @@
1
+ export type ConsensusPolicyType =
2
+ | 'SINGLE_WINNER'
3
+ | 'HIGHEST_CONFIDENCE_SINGLE'
4
+ | 'OWNER_PICK'
5
+ | 'TOP_K_SPLIT'
6
+ | 'MAJORITY_VOTE'
7
+ | 'WEIGHTED_VOTE_SIMPLE'
8
+ | 'WEIGHTED_REPUTATION'
9
+ | 'TRUSTED_ARBITER';
10
+ export type JobMode = 'SUBMISSION' | 'VOTING';
11
+ export type JobStatus =
12
+ | 'OPEN'
13
+ | 'CLOSED'
14
+ | 'RESOLVED'
15
+ | 'CANCELLED'
16
+ | 'CLAIMED'
17
+ | 'IN_PROGRESS'
18
+ | 'SUBMITTED'
19
+ | 'EXPIRED';
20
+ export type ClaimStatus = 'ACTIVE' | 'COMPLETED' | 'EXPIRED' | 'CANCELLED';
21
+ export type SubmissionStatus = 'VALID' | 'WITHDRAWN' | 'SELECTED' | 'REJECTED' | 'SUBMITTED' | 'ACCEPTED';
22
+ export type VoteTargetType = 'SUBMISSION' | 'CHOICE';
23
+
24
+ export interface ConsensusPolicyConfig {
25
+ type: ConsensusPolicyType;
26
+ trustedArbiterAgentId?: string;
27
+ minConfidence?: number;
28
+ topK?: number;
29
+ ordering?: 'confidence' | 'score';
30
+ quorum?: number;
31
+ }
32
+
33
+ export interface SlashingPolicy {
34
+ enabled: boolean;
35
+ slashPercent: number;
36
+ slashFlat: number;
37
+ }
38
+
39
+ export interface JobConstraints {
40
+ timeSeconds?: number;
41
+ budget?: number;
42
+ }
43
+
44
+ export interface EscrowPolicy {
45
+ type: 'mint' | 'pool';
46
+ poolAccountId?: string;
47
+ }
48
+
49
+ export interface Job {
50
+ id: string;
51
+ boardId?: string;
52
+ creatorPrincipalId?: string;
53
+ title: string;
54
+ desc?: string;
55
+ description: string;
56
+ inputRef?: string;
57
+ createdAt: string;
58
+ expiresAt: string;
59
+ opensAt?: string;
60
+ closesAt?: string;
61
+ resolvesAt?: string;
62
+ createdByAgentId: string;
63
+ mode?: JobMode;
64
+ policyKey?: string;
65
+ policyConfigJson?: Record<string, unknown>;
66
+ tags: string[];
67
+ priority: number;
68
+ requiredCapabilities: string[];
69
+ inputs: Record<string, unknown>;
70
+ constraints: JobConstraints;
71
+ reward: number;
72
+ rewardAmount?: number;
73
+ stakeRequired: number;
74
+ stakeAmount?: number;
75
+ currency?: string;
76
+ maxParticipants: number;
77
+ minParticipants: number;
78
+ consensusPolicy: ConsensusPolicyConfig;
79
+ slashingPolicy: SlashingPolicy;
80
+ escrowPolicy: EscrowPolicy;
81
+ artifactSchemaJson?: Record<string, unknown>;
82
+ status: JobStatus;
83
+ }
84
+
85
+ export interface Bid {
86
+ agentId: string;
87
+ jobId: string;
88
+ stakeAmount: number;
89
+ stakeAt: string;
90
+ eligibilityProof?: string;
91
+ reputationSnapshot?: number;
92
+ }
93
+
94
+ export interface Assignment {
95
+ agentId: string;
96
+ jobId: string;
97
+ claimAt: string;
98
+ leaseUntil: string;
99
+ heartbeatAt: string;
100
+ status: ClaimStatus;
101
+ }
102
+
103
+ export interface Submission {
104
+ id: string;
105
+ jobId: string;
106
+ boardId?: string;
107
+ submitterPrincipalId?: string;
108
+ agentId: string;
109
+ submittedAt: string;
110
+ createdAt?: string;
111
+ artifactRef?: string;
112
+ artifacts: Record<string, unknown>;
113
+ summary: string;
114
+ confidence: number;
115
+ requestedPayout: number;
116
+ evidenceLinks?: string[];
117
+ status: SubmissionStatus;
118
+ }
119
+
120
+ export interface Vote {
121
+ id: string;
122
+ jobId: string;
123
+ boardId?: string;
124
+ voterPrincipalId?: string;
125
+ submissionId?: string;
126
+ targetType?: VoteTargetType;
127
+ targetId?: string;
128
+ choiceKey?: string;
129
+ agentId: string;
130
+ score: number;
131
+ weight?: number;
132
+ stakeAmount?: number;
133
+ rationale?: string;
134
+ createdAt: string;
135
+ }
136
+
137
+ export interface Resolution {
138
+ jobId: string;
139
+ resolvedAt: string;
140
+ winners: string[];
141
+ winningSubmissionIds: string[];
142
+ payouts: Array<{ agentId: string; amount: number }>;
143
+ slashes: Array<{ agentId: string; amount: number; reason: string }>;
144
+ consensusTrace: Record<string, unknown>;
145
+ finalArtifact: Record<string, unknown> | null;
146
+ auditLog: string[];
147
+ }
148
+
149
+ export interface AuditEvent {
150
+ id: string;
151
+ at: string;
152
+ type: string;
153
+ jobId?: string;
154
+ actorAgentId?: string;
155
+ details?: Record<string, unknown>;
156
+ }
157
+
158
+ export interface DiagnosticEntry {
159
+ id: string;
160
+ at: string;
161
+ message: string;
162
+ context?: Record<string, unknown>;
163
+ }
164
+
165
+ export type LedgerEntryType = 'FAUCET' | 'STAKE' | 'UNSTAKE' | 'PAYOUT' | 'SLASH' | 'ADJUST' | 'ESCROW_MINT';
166
+
167
+ export interface LedgerEntry {
168
+ id: string;
169
+ at: string;
170
+ type: LedgerEntryType;
171
+ agentId: string;
172
+ amount: number;
173
+ jobId?: string;
174
+ reason?: string;
175
+ }
176
+
177
+ export interface StorageState {
178
+ jobs: Job[];
179
+ bids: Bid[];
180
+ claims: Assignment[];
181
+ submissions: Submission[];
182
+ votes: Vote[];
183
+ resolutions: Resolution[];
184
+ ledger: LedgerEntry[];
185
+ audit: AuditEvent[];
186
+ errors: DiagnosticEntry[];
187
+ }
188
+
189
+ export interface ConsensusToolsConfig {
190
+ mode: 'local' | 'global';
191
+ local: {
192
+ storage: {
193
+ kind: 'sqlite' | 'json';
194
+ path: string;
195
+ };
196
+ server: {
197
+ enabled: boolean;
198
+ host: string;
199
+ port: number;
200
+ authToken: string;
201
+ };
202
+ slashingEnabled: boolean;
203
+ jobDefaults: {
204
+ reward: number;
205
+ stakeRequired: number;
206
+ maxParticipants: number;
207
+ minParticipants: number;
208
+ expiresSeconds: number;
209
+ consensusPolicy: ConsensusPolicyConfig;
210
+ slashingPolicy: SlashingPolicy;
211
+ };
212
+ consensusPolicies?: Record<string, ConsensusPolicyConfig>;
213
+ ledger: {
214
+ faucetEnabled: boolean;
215
+ initialCreditsPerAgent: number;
216
+ balances: Record<string, number>;
217
+ balancesMode?: 'initial' | 'override';
218
+ };
219
+ };
220
+ global: {
221
+ baseUrl: string;
222
+ accessToken: string;
223
+ };
224
+ agentIdentity: {
225
+ agentIdSource: 'openclaw' | 'env' | 'manual';
226
+ manualAgentId: string;
227
+ };
228
+ safety: {
229
+ requireOptionalToolsOptIn: boolean;
230
+ allowNetworkSideEffects: boolean;
231
+ };
232
+ }
@@ -0,0 +1,21 @@
1
+ import crypto from 'node:crypto';
2
+
3
+ export function newId(prefix = 'id'): string {
4
+ const id = crypto?.randomUUID ? crypto.randomUUID() : fallbackUuid();
5
+ return `${prefix}_${id}`;
6
+ }
7
+
8
+ function fallbackUuid(): string {
9
+ const bytes = crypto.randomBytes(16);
10
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
11
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
12
+ const hex = bytes.toString('hex');
13
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
14
+ }
15
+
16
+ export function deepCopy<T>(value: T): T {
17
+ if (typeof structuredClone === 'function') {
18
+ return structuredClone(value);
19
+ }
20
+ return JSON.parse(JSON.stringify(value));
21
+ }
@@ -0,0 +1,36 @@
1
+ export class Mutex {
2
+ private queue: Array<() => void> = [];
3
+ private locked = false;
4
+
5
+ async runExclusive<T>(fn: () => Promise<T>): Promise<T> {
6
+ await this.lock();
7
+ try {
8
+ return await fn();
9
+ } finally {
10
+ this.unlock();
11
+ }
12
+ }
13
+
14
+ private lock(): Promise<void> {
15
+ if (!this.locked) {
16
+ this.locked = true;
17
+ return Promise.resolve();
18
+ }
19
+
20
+ return new Promise((resolve) => {
21
+ this.queue.push(() => {
22
+ this.locked = true;
23
+ resolve();
24
+ });
25
+ });
26
+ }
27
+
28
+ private unlock(): void {
29
+ const next = this.queue.shift();
30
+ if (next) {
31
+ next();
32
+ } else {
33
+ this.locked = false;
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,35 @@
1
+ export interface ColumnDef {
2
+ key: string;
3
+ label?: string;
4
+ align?: 'left' | 'right';
5
+ }
6
+
7
+ export function renderTable(rows: Array<Record<string, unknown>>, columns: ColumnDef[]): string {
8
+ const headers = columns.map((col) => col.label || col.key);
9
+ const data = rows.map((row) =>
10
+ columns.map((col) => {
11
+ const val = row[col.key];
12
+ return val === undefined || val === null ? '' : String(val);
13
+ })
14
+ );
15
+
16
+ const widths = headers.map((header, idx) => {
17
+ const maxCell = Math.max(header.length, ...data.map((row) => row[idx]?.length || 0));
18
+ return Math.min(Math.max(maxCell, 4), 80);
19
+ });
20
+
21
+ const formatRow = (cells: string[]) =>
22
+ cells
23
+ .map((cell, idx) => {
24
+ const width = widths[idx];
25
+ const trimmed = cell.length > width ? `${cell.slice(0, width - 3)}...` : cell;
26
+ if (columns[idx].align === 'right') {
27
+ return trimmed.padStart(width, ' ');
28
+ }
29
+ return trimmed.padEnd(width, ' ');
30
+ })
31
+ .join(' ');
32
+
33
+ const lines = [formatRow(headers), formatRow(headers.map((h, i) => '-'.repeat(widths[i]))), ...data.map(formatRow)];
34
+ return lines.join('\n');
35
+ }
@@ -0,0 +1,12 @@
1
+ export function nowIso(): string {
2
+ return new Date().toISOString();
3
+ }
4
+
5
+ export function addSeconds(iso: string, seconds: number): string {
6
+ const base = iso ? Date.parse(iso) : Date.now();
7
+ return new Date(base + seconds * 1000).toISOString();
8
+ }
9
+
10
+ export function isPast(iso: string): boolean {
11
+ return Date.parse(iso) <= Date.now();
12
+ }