@tacitprotocol/sdk 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/dist/index.d.mts +554 -0
- package/dist/index.d.ts +554 -0
- package/dist/index.js +996 -0
- package/dist/index.mjs +960 -0
- package/package.json +64 -0
- package/src/core/agent.ts +436 -0
- package/src/discovery/intent.ts +209 -0
- package/src/identity/authenticity.ts +260 -0
- package/src/identity/did.ts +192 -0
- package/src/index.ts +73 -0
- package/src/matching/scorer.ts +287 -0
- package/src/types/index.ts +286 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tacit Protocol — Match Scoring Engine
|
|
3
|
+
*
|
|
4
|
+
* Computes compatibility scores between two agents based on their
|
|
5
|
+
* intents, domains, authenticity, and preferences.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Intent,
|
|
10
|
+
AgentCard,
|
|
11
|
+
MatchResult,
|
|
12
|
+
MatchAction,
|
|
13
|
+
AuthenticityVector,
|
|
14
|
+
} from '../types/index.js';
|
|
15
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
16
|
+
|
|
17
|
+
// ─── Scoring Weights ──────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
const SCORE_WEIGHTS = {
|
|
20
|
+
intentAlignment: 0.30,
|
|
21
|
+
domainFit: 0.25,
|
|
22
|
+
authenticityCompatibility: 0.20,
|
|
23
|
+
preferenceMatch: 0.15,
|
|
24
|
+
timingFit: 0.10,
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
// ─── Default Thresholds ───────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
const DEFAULT_THRESHOLDS = {
|
|
30
|
+
autoPropose: 80,
|
|
31
|
+
suggest: 60,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// ─── Match Scorer ─────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
export class MatchScorer {
|
|
37
|
+
private thresholds: { autoPropose: number; suggest: number };
|
|
38
|
+
|
|
39
|
+
constructor(thresholds?: { autoPropose?: number; suggest?: number }) {
|
|
40
|
+
this.thresholds = {
|
|
41
|
+
autoPropose: thresholds?.autoPropose ?? DEFAULT_THRESHOLDS.autoPropose,
|
|
42
|
+
suggest: thresholds?.suggest ?? DEFAULT_THRESHOLDS.suggest,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Score the compatibility between two agents based on their intents and cards.
|
|
48
|
+
*/
|
|
49
|
+
score(params: {
|
|
50
|
+
initiator: { intent: Intent; card: AgentCard };
|
|
51
|
+
responder: { intent: Intent; card: AgentCard };
|
|
52
|
+
}): MatchResult {
|
|
53
|
+
const { initiator, responder } = params;
|
|
54
|
+
|
|
55
|
+
const breakdown = {
|
|
56
|
+
intentAlignment: this.scoreIntentAlignment(initiator.intent, responder.intent),
|
|
57
|
+
domainFit: this.scoreDomainFit(initiator.intent, responder.intent),
|
|
58
|
+
authenticityCompatibility: this.scoreAuthenticityCompatibility(
|
|
59
|
+
initiator.card.authenticity,
|
|
60
|
+
responder.card.authenticity,
|
|
61
|
+
initiator.intent,
|
|
62
|
+
responder.intent
|
|
63
|
+
),
|
|
64
|
+
preferenceMatch: this.scorePreferenceMatch(initiator.card, responder.card),
|
|
65
|
+
timingFit: this.scoreTimingFit(initiator.intent, responder.intent),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const overall = Math.round(
|
|
69
|
+
breakdown.intentAlignment * SCORE_WEIGHTS.intentAlignment +
|
|
70
|
+
breakdown.domainFit * SCORE_WEIGHTS.domainFit +
|
|
71
|
+
breakdown.authenticityCompatibility * SCORE_WEIGHTS.authenticityCompatibility +
|
|
72
|
+
breakdown.preferenceMatch * SCORE_WEIGHTS.preferenceMatch +
|
|
73
|
+
breakdown.timingFit * SCORE_WEIGHTS.timingFit
|
|
74
|
+
) * 100;
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
matchId: `match:${uuidv4()}`,
|
|
78
|
+
agents: {
|
|
79
|
+
initiator: initiator.card.agent.did,
|
|
80
|
+
responder: responder.card.agent.did,
|
|
81
|
+
},
|
|
82
|
+
score: {
|
|
83
|
+
overall: Math.min(100, Math.max(0, overall)),
|
|
84
|
+
breakdown,
|
|
85
|
+
},
|
|
86
|
+
timestamp: new Date().toISOString(),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Determine what action to take based on a match score.
|
|
92
|
+
*/
|
|
93
|
+
determineAction(score: number): MatchAction {
|
|
94
|
+
if (score >= this.thresholds.autoPropose) return 'auto-propose';
|
|
95
|
+
if (score >= this.thresholds.suggest) return 'suggest';
|
|
96
|
+
return 'ignore';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ─── Dimension Scorers ──────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Score how well the two intents complement each other.
|
|
103
|
+
* A seeking-offering match scores highest.
|
|
104
|
+
*/
|
|
105
|
+
private scoreIntentAlignment(a: Intent, b: Intent): number {
|
|
106
|
+
// Check for seeking-offering complementarity
|
|
107
|
+
const aSeeking = flattenValues(a.intent.seeking);
|
|
108
|
+
const bSeeking = flattenValues(b.intent.seeking);
|
|
109
|
+
const aContext = flattenValues(a.intent.context);
|
|
110
|
+
const bContext = flattenValues(b.intent.context);
|
|
111
|
+
|
|
112
|
+
// How well does A's seeking match B's context/offering?
|
|
113
|
+
const aToB = computeOverlap(aSeeking, [...bSeeking, ...bContext]);
|
|
114
|
+
// How well does B's seeking match A's context/offering?
|
|
115
|
+
const bToA = computeOverlap(bSeeking, [...aSeeking, ...aContext]);
|
|
116
|
+
|
|
117
|
+
// Bidirectional alignment is better
|
|
118
|
+
return (aToB + bToA) / 2;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Score the domain overlap between two intents.
|
|
123
|
+
*/
|
|
124
|
+
private scoreDomainFit(a: Intent, b: Intent): number {
|
|
125
|
+
// Same domain is a strong signal
|
|
126
|
+
if (a.domain === b.domain) return 0.9;
|
|
127
|
+
|
|
128
|
+
// Related domains get partial credit
|
|
129
|
+
const related = RELATED_DOMAINS.get(a.domain);
|
|
130
|
+
if (related?.includes(b.domain)) return 0.5;
|
|
131
|
+
|
|
132
|
+
return 0.1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Score whether both agents meet each other's authenticity requirements.
|
|
137
|
+
*/
|
|
138
|
+
private scoreAuthenticityCompatibility(
|
|
139
|
+
aAuth: AuthenticityVector,
|
|
140
|
+
bAuth: AuthenticityVector,
|
|
141
|
+
aIntent: Intent,
|
|
142
|
+
bIntent: Intent
|
|
143
|
+
): number {
|
|
144
|
+
const aMinScore = aIntent.filters.minAuthenticityScore;
|
|
145
|
+
const bMinScore = bIntent.filters.minAuthenticityScore;
|
|
146
|
+
|
|
147
|
+
// Both meet each other's minimums
|
|
148
|
+
const aMeetsBMin = bAuth.score >= aMinScore;
|
|
149
|
+
const bMeetsAMin = aAuth.score >= bMinScore;
|
|
150
|
+
|
|
151
|
+
if (aMeetsBMin && bMeetsAMin) {
|
|
152
|
+
// Both meet minimums — score based on how far above the minimum
|
|
153
|
+
const aExcess = (bAuth.score - aMinScore) / (100 - aMinScore);
|
|
154
|
+
const bExcess = (aAuth.score - bMinScore) / (100 - bMinScore);
|
|
155
|
+
return 0.6 + ((aExcess + bExcess) / 2) * 0.4;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (aMeetsBMin || bMeetsAMin) {
|
|
159
|
+
// Only one meets the other's minimum
|
|
160
|
+
return 0.3;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Neither meets the other's minimum
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Score preference compatibility (communication style, timing, language).
|
|
169
|
+
*/
|
|
170
|
+
private scorePreferenceMatch(a: AgentCard, b: AgentCard): number {
|
|
171
|
+
let score = 0;
|
|
172
|
+
let factors = 0;
|
|
173
|
+
|
|
174
|
+
// Language overlap
|
|
175
|
+
const langOverlap = a.preferences.languages.filter(
|
|
176
|
+
l => b.preferences.languages.includes(l)
|
|
177
|
+
);
|
|
178
|
+
if (langOverlap.length > 0) {
|
|
179
|
+
score += 1.0;
|
|
180
|
+
}
|
|
181
|
+
factors++;
|
|
182
|
+
|
|
183
|
+
// Introduction style compatibility
|
|
184
|
+
if (a.preferences.introductionStyle === b.preferences.introductionStyle) {
|
|
185
|
+
score += 1.0;
|
|
186
|
+
} else {
|
|
187
|
+
score += 0.5; // Different styles still work
|
|
188
|
+
}
|
|
189
|
+
factors++;
|
|
190
|
+
|
|
191
|
+
return factors > 0 ? score / factors : 0.5;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Score timing compatibility — are both intents active and aligned in urgency?
|
|
196
|
+
*/
|
|
197
|
+
private scoreTimingFit(a: Intent, b: Intent): number {
|
|
198
|
+
const now = Date.now();
|
|
199
|
+
|
|
200
|
+
// Check both intents are still within TTL
|
|
201
|
+
const aExpiry = new Date(a.created).getTime() + a.ttl * 1000;
|
|
202
|
+
const bExpiry = new Date(b.created).getTime() + b.ttl * 1000;
|
|
203
|
+
|
|
204
|
+
if (now > aExpiry || now > bExpiry) return 0;
|
|
205
|
+
|
|
206
|
+
// Check urgency alignment from context
|
|
207
|
+
const aUrgency = extractUrgency(a.intent.context);
|
|
208
|
+
const bUrgency = extractUrgency(b.intent.context);
|
|
209
|
+
|
|
210
|
+
if (aUrgency === bUrgency) return 1.0;
|
|
211
|
+
if (Math.abs(aUrgency - bUrgency) <= 1) return 0.7;
|
|
212
|
+
return 0.3;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ─── Helpers ──────────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Flatten an object's values into a string array for comparison.
|
|
220
|
+
*/
|
|
221
|
+
function flattenValues(obj: Record<string, unknown>): string[] {
|
|
222
|
+
const values: string[] = [];
|
|
223
|
+
for (const value of Object.values(obj)) {
|
|
224
|
+
if (typeof value === 'string') {
|
|
225
|
+
values.push(value.toLowerCase());
|
|
226
|
+
} else if (Array.isArray(value)) {
|
|
227
|
+
values.push(...value.filter(v => typeof v === 'string').map(v => (v as string).toLowerCase()));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return values;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Compute the overlap between two string arrays using Jaccard-like similarity.
|
|
235
|
+
*/
|
|
236
|
+
function computeOverlap(a: string[], b: string[]): number {
|
|
237
|
+
if (a.length === 0 || b.length === 0) return 0;
|
|
238
|
+
|
|
239
|
+
let matches = 0;
|
|
240
|
+
for (const term of a) {
|
|
241
|
+
for (const bTerm of b) {
|
|
242
|
+
if (term === bTerm || bTerm.includes(term) || term.includes(bTerm)) {
|
|
243
|
+
matches++;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return matches / Math.max(a.length, 1);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Extract urgency level from context (0 = passive, 3 = immediate).
|
|
254
|
+
*/
|
|
255
|
+
function extractUrgency(context: Record<string, unknown>): number {
|
|
256
|
+
const urgency = context['urgency'];
|
|
257
|
+
if (typeof urgency !== 'string') return 1; // default: moderate
|
|
258
|
+
|
|
259
|
+
switch (urgency.toLowerCase()) {
|
|
260
|
+
case 'immediate':
|
|
261
|
+
case 'asap':
|
|
262
|
+
return 3;
|
|
263
|
+
case 'active':
|
|
264
|
+
case 'urgent':
|
|
265
|
+
return 2;
|
|
266
|
+
case 'moderate':
|
|
267
|
+
case 'normal':
|
|
268
|
+
return 1;
|
|
269
|
+
case 'passive':
|
|
270
|
+
case 'low':
|
|
271
|
+
case 'whenever':
|
|
272
|
+
return 0;
|
|
273
|
+
default:
|
|
274
|
+
return 1;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Related domain mapping for cross-domain matching.
|
|
280
|
+
*/
|
|
281
|
+
const RELATED_DOMAINS = new Map<string, string[]>([
|
|
282
|
+
['professional', ['commerce', 'learning']],
|
|
283
|
+
['dating', []],
|
|
284
|
+
['local-services', ['commerce']],
|
|
285
|
+
['learning', ['professional']],
|
|
286
|
+
['commerce', ['professional', 'local-services']],
|
|
287
|
+
]);
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tacit Protocol — Core Type Definitions
|
|
3
|
+
* Version: 0.1.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ─── Identity Types ───────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
export type DID = `did:${string}:${string}`;
|
|
9
|
+
|
|
10
|
+
export interface AgentIdentity {
|
|
11
|
+
did: DID;
|
|
12
|
+
publicKey: Uint8Array;
|
|
13
|
+
privateKey?: Uint8Array;
|
|
14
|
+
created: Date;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type AnonymityLevel = 'anonymous' | 'pseudonymous' | 'semi-identified' | 'identified';
|
|
18
|
+
|
|
19
|
+
export interface SessionPersona {
|
|
20
|
+
displayName: string;
|
|
21
|
+
context: string;
|
|
22
|
+
anonymityLevel: AnonymityLevel;
|
|
23
|
+
sessionId: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ─── Agent Card Types ─────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
export interface AgentCard {
|
|
29
|
+
version: string;
|
|
30
|
+
agent: {
|
|
31
|
+
did: DID;
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
created: string;
|
|
35
|
+
protocols: string[];
|
|
36
|
+
transport: TransportConfig;
|
|
37
|
+
};
|
|
38
|
+
domains: Domain[];
|
|
39
|
+
authenticity: AuthenticityVector;
|
|
40
|
+
preferences: AgentPreferences;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface TransportConfig {
|
|
44
|
+
type: 'didcomm/v2';
|
|
45
|
+
endpoint: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface Domain {
|
|
49
|
+
type: DomainType;
|
|
50
|
+
seeking: string[];
|
|
51
|
+
offering: string[];
|
|
52
|
+
context: Record<string, string | string[] | number>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type DomainType =
|
|
56
|
+
| 'professional'
|
|
57
|
+
| 'dating'
|
|
58
|
+
| 'local-services'
|
|
59
|
+
| 'learning'
|
|
60
|
+
| 'commerce'
|
|
61
|
+
| 'custom';
|
|
62
|
+
|
|
63
|
+
export interface AgentPreferences {
|
|
64
|
+
introductionStyle: 'progressive' | 'direct';
|
|
65
|
+
initialAnonymity: boolean;
|
|
66
|
+
responseTime: string;
|
|
67
|
+
languages: string[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ─── Authenticity Vector Types ────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
export interface AuthenticityVector {
|
|
73
|
+
level: TrustLevel;
|
|
74
|
+
score: number;
|
|
75
|
+
dimensions: AuthenticityDimensions;
|
|
76
|
+
verifiableCredentials: VerifiableCredential[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface AuthenticityDimensions {
|
|
80
|
+
tenure: number;
|
|
81
|
+
consistency: number;
|
|
82
|
+
attestations: number;
|
|
83
|
+
networkTrust: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type TrustLevel = 'new' | 'emerging' | 'established' | 'trusted' | 'exemplary';
|
|
87
|
+
|
|
88
|
+
export interface VerifiableCredential {
|
|
89
|
+
type: string;
|
|
90
|
+
issuer: DID;
|
|
91
|
+
claim: string;
|
|
92
|
+
issued: string;
|
|
93
|
+
signature?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── Intent Types ─────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
export interface Intent {
|
|
99
|
+
id: string;
|
|
100
|
+
agentDid: DID;
|
|
101
|
+
type: IntentType;
|
|
102
|
+
domain: DomainType;
|
|
103
|
+
intent: {
|
|
104
|
+
seeking: Record<string, unknown>;
|
|
105
|
+
context: Record<string, unknown>;
|
|
106
|
+
};
|
|
107
|
+
filters: IntentFilters;
|
|
108
|
+
privacyLevel: PrivacyLevel;
|
|
109
|
+
ttl: number;
|
|
110
|
+
created: string;
|
|
111
|
+
signature: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type IntentType =
|
|
115
|
+
| 'introduction'
|
|
116
|
+
| 'service'
|
|
117
|
+
| 'collaboration'
|
|
118
|
+
| 'mentorship'
|
|
119
|
+
| 'commerce';
|
|
120
|
+
|
|
121
|
+
export type PrivacyLevel = 'public' | 'filtered' | 'private';
|
|
122
|
+
|
|
123
|
+
export interface IntentFilters {
|
|
124
|
+
minAuthenticityScore: number;
|
|
125
|
+
requiredCredentials: string[];
|
|
126
|
+
excludedDomains: string[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type IntentStatus =
|
|
130
|
+
| 'created'
|
|
131
|
+
| 'published'
|
|
132
|
+
| 'active'
|
|
133
|
+
| 'matched'
|
|
134
|
+
| 'fulfilled'
|
|
135
|
+
| 'expired'
|
|
136
|
+
| 'withdrawn';
|
|
137
|
+
|
|
138
|
+
// ─── Match Types ──────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
export interface MatchResult {
|
|
141
|
+
matchId: string;
|
|
142
|
+
agents: {
|
|
143
|
+
initiator: DID;
|
|
144
|
+
responder: DID;
|
|
145
|
+
};
|
|
146
|
+
score: MatchScore;
|
|
147
|
+
timestamp: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface MatchScore {
|
|
151
|
+
overall: number;
|
|
152
|
+
breakdown: {
|
|
153
|
+
intentAlignment: number;
|
|
154
|
+
domainFit: number;
|
|
155
|
+
authenticityCompatibility: number;
|
|
156
|
+
preferenceMatch: number;
|
|
157
|
+
timingFit: number;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type MatchAction = 'auto-propose' | 'suggest' | 'ignore';
|
|
162
|
+
|
|
163
|
+
// ─── Intro Proposal Types ─────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
export interface IntroProposal {
|
|
166
|
+
id: string;
|
|
167
|
+
type: IntentType;
|
|
168
|
+
initiator: {
|
|
169
|
+
agentDid: DID;
|
|
170
|
+
persona: SessionPersona;
|
|
171
|
+
};
|
|
172
|
+
responder: {
|
|
173
|
+
agentDid: DID;
|
|
174
|
+
persona?: SessionPersona;
|
|
175
|
+
};
|
|
176
|
+
match: {
|
|
177
|
+
score: number;
|
|
178
|
+
rationale: string;
|
|
179
|
+
domain: DomainType;
|
|
180
|
+
};
|
|
181
|
+
terms: IntroTerms;
|
|
182
|
+
status: ProposalStatus;
|
|
183
|
+
created: string;
|
|
184
|
+
signature: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface IntroTerms {
|
|
188
|
+
initialReveal: AnonymityLevel;
|
|
189
|
+
revealStages: RevealStage[];
|
|
190
|
+
communicationChannel: string;
|
|
191
|
+
expiry: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export type RevealStage =
|
|
195
|
+
| 'domain_context'
|
|
196
|
+
| 'professional_background'
|
|
197
|
+
| 'personal_background'
|
|
198
|
+
| 'identity'
|
|
199
|
+
| 'direct_contact';
|
|
200
|
+
|
|
201
|
+
export type ProposalStatus =
|
|
202
|
+
| 'pending'
|
|
203
|
+
| 'accepted_by_initiator'
|
|
204
|
+
| 'accepted_by_responder'
|
|
205
|
+
| 'active'
|
|
206
|
+
| 'completed'
|
|
207
|
+
| 'declined'
|
|
208
|
+
| 'expired'
|
|
209
|
+
| 'withdrawn';
|
|
210
|
+
|
|
211
|
+
// ─── Service / Commerce Types ─────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
export interface ServiceIntent {
|
|
214
|
+
serviceType: string;
|
|
215
|
+
description: string;
|
|
216
|
+
location?: {
|
|
217
|
+
city: string;
|
|
218
|
+
radiusKm: number;
|
|
219
|
+
};
|
|
220
|
+
budget?: {
|
|
221
|
+
currency: string;
|
|
222
|
+
max: number;
|
|
223
|
+
};
|
|
224
|
+
urgency: string;
|
|
225
|
+
requirements: {
|
|
226
|
+
minAuthenticity: number;
|
|
227
|
+
requiredCredentials: string[];
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface ServiceAttestation {
|
|
232
|
+
type: 'ServiceAttestation';
|
|
233
|
+
issuer: DID;
|
|
234
|
+
subject: DID;
|
|
235
|
+
claims: {
|
|
236
|
+
serviceType: string;
|
|
237
|
+
completed: boolean;
|
|
238
|
+
qualityRating: number;
|
|
239
|
+
onTime: boolean;
|
|
240
|
+
wouldRecommend: boolean;
|
|
241
|
+
};
|
|
242
|
+
issued: string;
|
|
243
|
+
signature: string;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ─── Event Types ──────────────────────────────────────────────────
|
|
247
|
+
|
|
248
|
+
export type TacitEvent =
|
|
249
|
+
| { type: 'intent:published'; intent: Intent }
|
|
250
|
+
| { type: 'intent:matched'; match: MatchResult }
|
|
251
|
+
| { type: 'intent:expired'; intentId: string }
|
|
252
|
+
| { type: 'proposal:received'; proposal: IntroProposal }
|
|
253
|
+
| { type: 'proposal:accepted'; proposal: IntroProposal }
|
|
254
|
+
| { type: 'proposal:declined'; proposalId: string }
|
|
255
|
+
| { type: 'intro:started'; proposal: IntroProposal }
|
|
256
|
+
| { type: 'intro:context-revealed'; stage: RevealStage; data: Record<string, unknown> }
|
|
257
|
+
| { type: 'intro:completed'; proposalId: string }
|
|
258
|
+
| { type: 'connection:established'; endpoint: string }
|
|
259
|
+
| { type: 'connection:lost'; reason: string }
|
|
260
|
+
| { type: 'error'; error: Error };
|
|
261
|
+
|
|
262
|
+
// ─── Configuration Types ──────────────────────────────────────────
|
|
263
|
+
|
|
264
|
+
export interface TacitConfig {
|
|
265
|
+
identity?: AgentIdentity;
|
|
266
|
+
relayUrl?: string;
|
|
267
|
+
profile?: {
|
|
268
|
+
name: string;
|
|
269
|
+
description?: string;
|
|
270
|
+
domain: DomainType;
|
|
271
|
+
seeking: string;
|
|
272
|
+
offering: string;
|
|
273
|
+
};
|
|
274
|
+
matchThresholds?: {
|
|
275
|
+
autoPropose: number;
|
|
276
|
+
suggest: number;
|
|
277
|
+
};
|
|
278
|
+
preferences?: Partial<AgentPreferences>;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export interface RelayConfig {
|
|
282
|
+
url: string;
|
|
283
|
+
maxRetries: number;
|
|
284
|
+
retryDelayMs: number;
|
|
285
|
+
heartbeatIntervalMs: number;
|
|
286
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2022", "DOM"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"noUnusedLocals": true,
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["src/**/*"],
|
|
23
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
24
|
+
}
|