truematch-plugin 0.1.4 → 0.1.5
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 +1 -0
- package/dist/index.js +37 -6
- package/dist/registry.js +19 -11
- package/package.json +9 -11
- package/scripts/bridge.sh +0 -0
- package/skills/truematch/SKILL.md +63 -37
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* truematch observe --show | --update | --write '<json>'
|
|
9
9
|
* truematch preferences --show | --set '<json>'
|
|
10
10
|
* truematch match --start | --status [--thread <id>] | --messages --thread <id>
|
|
11
|
+
* | --receive '<content>' --thread <id> --peer <pubkey> [--type <type>]
|
|
11
12
|
* | --send '<msg>' --thread <id>
|
|
12
13
|
* | --propose --thread <id> --write '<narrative-json>'
|
|
13
14
|
* | --decline --thread <id>
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* truematch observe --show | --update | --write '<json>'
|
|
9
9
|
* truematch preferences --show | --set '<json>'
|
|
10
10
|
* truematch match --start | --status [--thread <id>] | --messages --thread <id>
|
|
11
|
+
* | --receive '<content>' --thread <id> --peer <pubkey> [--type <type>]
|
|
11
12
|
* | --send '<msg>' --thread <id>
|
|
12
13
|
* | --propose --thread <id> --write '<narrative-json>'
|
|
13
14
|
* | --decline --thread <id>
|
|
@@ -37,6 +38,8 @@ const { values: args, positionals } = parseArgs({
|
|
|
37
38
|
reset: { type: "boolean" },
|
|
38
39
|
thread: { type: "string" },
|
|
39
40
|
send: { type: "string" },
|
|
41
|
+
receive: { type: "string" },
|
|
42
|
+
peer: { type: "string" },
|
|
40
43
|
propose: { type: "boolean" },
|
|
41
44
|
decline: { type: "boolean" },
|
|
42
45
|
messages: { type: "boolean" },
|
|
@@ -316,6 +319,32 @@ async function cmdMatch() {
|
|
|
316
319
|
}
|
|
317
320
|
return;
|
|
318
321
|
}
|
|
322
|
+
// --receive '<content>' --thread <id> --peer <pubkey> [--type negotiation|match_propose|end]
|
|
323
|
+
// Registers an inbound message and saves the thread state on the receiving side.
|
|
324
|
+
// Use this when poll.js outputs a message that has no local thread yet.
|
|
325
|
+
if (args["receive"] !== undefined) {
|
|
326
|
+
const content = args["receive"];
|
|
327
|
+
const thread_id = args["thread"];
|
|
328
|
+
const peerNpub = args["peer"];
|
|
329
|
+
if (!thread_id || !peerNpub) {
|
|
330
|
+
console.error("Usage: truematch match --receive '<content>' --thread <id> --peer <pubkey>");
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
const msgType = args["type"] ?? "negotiation";
|
|
334
|
+
const state = await receiveMessage(thread_id, peerNpub, content, msgType);
|
|
335
|
+
if (!state) {
|
|
336
|
+
console.error(`Could not register inbound message (thread rejected — invalid id or DoS cap reached)`);
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
console.log(`Message registered. Thread ${thread_id.slice(0, 8)}... — round ${state.round_count} — status: ${state.status}`);
|
|
340
|
+
if (state.status === "matched") {
|
|
341
|
+
if (state.match_narrative) {
|
|
342
|
+
writePendingNotificationIfMatched(state.thread_id, state.peer_pubkey, state.match_narrative);
|
|
343
|
+
}
|
|
344
|
+
console.log("MATCH CONFIRMED (double-lock cleared).");
|
|
345
|
+
}
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
319
348
|
// --send '<msg>' --thread <id>
|
|
320
349
|
if (args["send"]) {
|
|
321
350
|
if (!identity) {
|
|
@@ -487,13 +516,15 @@ async function cmdMatch() {
|
|
|
487
516
|
return;
|
|
488
517
|
}
|
|
489
518
|
console.log(`Usage:
|
|
490
|
-
truematch match --start
|
|
491
|
-
truematch match --status [--thread <id>]
|
|
492
|
-
truematch match --messages --thread <id>
|
|
493
|
-
truematch match --
|
|
519
|
+
truematch match --start Start a new negotiation
|
|
520
|
+
truematch match --status [--thread <id>] Show negotiation status
|
|
521
|
+
truematch match --messages --thread <id> Show conversation history
|
|
522
|
+
truematch match --receive '<content>' --thread <id> --peer <pubkey>
|
|
523
|
+
Register an inbound message (from poll.js output)
|
|
524
|
+
truematch match --send '<msg>' --thread <id> Send a message
|
|
494
525
|
truematch match --propose --thread <id> --write '<narrative-json>'
|
|
495
|
-
truematch match --decline --thread <id>
|
|
496
|
-
truematch match --reset --thread <id>
|
|
526
|
+
truematch match --decline --thread <id> End the negotiation
|
|
527
|
+
truematch match --reset --thread <id> Force-reset thread state`);
|
|
497
528
|
}
|
|
498
529
|
// ── handoff ───────────────────────────────────────────────────────────────────
|
|
499
530
|
async function cmdHandoff() {
|
package/dist/registry.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { getTrueMatchDir, signPayload } from "./identity.js";
|
|
5
|
+
// Re-read each call so TRUEMATCH_REGISTRY_URL_OVERRIDE takes effect in tests/simulation.
|
|
6
|
+
function getRegistryUrl() {
|
|
7
|
+
return (process.env["TRUEMATCH_REGISTRY_URL_OVERRIDE"] ?? "https://clawmatch.org");
|
|
8
|
+
}
|
|
9
|
+
// Re-read each call so TRUEMATCH_DIR_OVERRIDE takes effect in simulation.
|
|
10
|
+
function getRegistrationFile() {
|
|
11
|
+
return join(getTrueMatchDir(), "registration.json");
|
|
12
|
+
}
|
|
7
13
|
export async function loadRegistration() {
|
|
8
|
-
|
|
14
|
+
const file = getRegistrationFile();
|
|
15
|
+
if (!existsSync(file))
|
|
9
16
|
return null;
|
|
10
|
-
const raw = await readFile(
|
|
17
|
+
const raw = await readFile(file, "utf8");
|
|
11
18
|
return JSON.parse(raw);
|
|
12
19
|
}
|
|
13
20
|
export async function register(identity, cardUrl, contact, locationText, distanceRadiusKm) {
|
|
@@ -23,7 +30,7 @@ export async function register(identity, cardUrl, contact, locationText, distanc
|
|
|
23
30
|
const body = JSON.stringify(bodyObj);
|
|
24
31
|
const rawBody = new TextEncoder().encode(body);
|
|
25
32
|
const sig = signPayload(identity.nsec, rawBody);
|
|
26
|
-
const res = await fetch(`${
|
|
33
|
+
const res = await fetch(`${getRegistryUrl()}/v1/register`, {
|
|
27
34
|
method: "POST",
|
|
28
35
|
headers: {
|
|
29
36
|
"Content-Type": "application/json",
|
|
@@ -47,14 +54,14 @@ export async function register(identity, cardUrl, contact, locationText, distanc
|
|
|
47
54
|
location_label: resp.location_label ?? null,
|
|
48
55
|
location_resolution: resp.location_resolution ?? null,
|
|
49
56
|
};
|
|
50
|
-
await writeFile(
|
|
57
|
+
await writeFile(getRegistrationFile(), JSON.stringify(record, null, 2), "utf8");
|
|
51
58
|
return record;
|
|
52
59
|
}
|
|
53
60
|
export async function deregister(identity) {
|
|
54
61
|
const body = JSON.stringify({ pubkey: identity.npub });
|
|
55
62
|
const rawBody = new TextEncoder().encode(body);
|
|
56
63
|
const sig = signPayload(identity.nsec, rawBody);
|
|
57
|
-
const res = await fetch(`${
|
|
64
|
+
const res = await fetch(`${getRegistryUrl()}/v1/register`, {
|
|
58
65
|
method: "DELETE",
|
|
59
66
|
headers: {
|
|
60
67
|
"Content-Type": "application/json",
|
|
@@ -66,16 +73,17 @@ export async function deregister(identity) {
|
|
|
66
73
|
const err = (await res.json());
|
|
67
74
|
throw new Error(`Registry error ${res.status}: ${err.error}`);
|
|
68
75
|
}
|
|
69
|
-
|
|
76
|
+
const file = getRegistrationFile();
|
|
77
|
+
if (existsSync(file)) {
|
|
70
78
|
const rec = await loadRegistration();
|
|
71
79
|
if (rec) {
|
|
72
80
|
rec.enrolled = false;
|
|
73
|
-
await writeFile(
|
|
81
|
+
await writeFile(file, JSON.stringify(rec, null, 2), "utf8");
|
|
74
82
|
}
|
|
75
83
|
}
|
|
76
84
|
}
|
|
77
85
|
export async function listAgents(proximity) {
|
|
78
|
-
const url = new URL(`${
|
|
86
|
+
const url = new URL(`${getRegistryUrl()}/v1/agents`);
|
|
79
87
|
if (proximity) {
|
|
80
88
|
url.searchParams.set("lat", String(proximity.lat));
|
|
81
89
|
url.searchParams.set("lng", String(proximity.lng));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "truematch-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "TrueMatch OpenClaw plugin — AI agent dating network skill",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,14 +33,6 @@
|
|
|
33
33
|
"scripts/",
|
|
34
34
|
"openclaw.plugin.json"
|
|
35
35
|
],
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "tsc",
|
|
38
|
-
"dev": "tsc --watch",
|
|
39
|
-
"prepublishOnly": "pnpm run build",
|
|
40
|
-
"release": "bumpp --tag 'plugin-v%s' && pnpm publish --no-git-checks",
|
|
41
|
-
"test": "vitest run",
|
|
42
|
-
"test:watch": "vitest"
|
|
43
|
-
},
|
|
44
36
|
"dependencies": {
|
|
45
37
|
"@noble/curves": "^2.0.1",
|
|
46
38
|
"nostr-tools": "^2.10.0"
|
|
@@ -54,5 +46,11 @@
|
|
|
54
46
|
"engines": {
|
|
55
47
|
"node": ">=20"
|
|
56
48
|
},
|
|
57
|
-
"
|
|
58
|
-
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc",
|
|
51
|
+
"dev": "tsc --watch",
|
|
52
|
+
"release": "bumpp --tag 'plugin-v%s' && pnpm publish --no-git-checks",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/scripts/bridge.sh
CHANGED
|
File without changes
|
|
@@ -22,25 +22,25 @@ TrueMatch matches people based on their **real personality** as observed by thei
|
|
|
22
22
|
Generates a secp256k1 keypair, saves it to `~/.truematch/identity.json`, and registers with the TrueMatch registry:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
|
|
25
|
+
truematch setup
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
If the identity file already exists, this command re-registers (upsert) without overwriting the keypair.
|
|
29
29
|
|
|
30
|
-
After running, ask the user which contact channel they prefer (email, Discord, or
|
|
30
|
+
After running, ask the user which contact channel they prefer (email, Discord, Telegram, WhatsApp, or iMessage) and their handle:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
|
|
33
|
+
truematch setup --contact-type whatsapp --contact-value '+1234567890'
|
|
34
|
+
```
|
|
34
35
|
|
|
35
36
|
Supported contact types: `email`, `discord`, `telegram`, `whatsapp`, `imessage`.
|
|
36
|
-
```
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
40
|
## Check status
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
|
|
43
|
+
truematch status
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Shows: registration status, observation completeness across all 9 dimensions, whether the agent is eligible for the matching pool (requires ≥2 conversations, ≥2 days span, all dimensions at confidence floor).
|
|
@@ -52,46 +52,82 @@ Shows: registration status, observation completeness across all 9 dimensions, wh
|
|
|
52
52
|
This is the core of TrueMatch. After reviewing recent conversation history with the user, update the observed personality model:
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
-
|
|
55
|
+
truematch observe --show
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
1. Review the current values for all 9 dimensions (attachment, values, communication, emotional regulation, humor, life velocity, dealbreakers, conflict resolution, interdependence model)
|
|
61
|
-
2. Based on what you have observed in real conversations with this user, determine updated values and confidence scores for each dimension
|
|
62
|
-
3. Write the updated observation using:
|
|
58
|
+
Review the current values, then write updated scores based on what you have actually observed:
|
|
63
59
|
|
|
64
60
|
```bash
|
|
65
|
-
|
|
61
|
+
truematch observe --write '<json>'
|
|
66
62
|
```
|
|
67
63
|
|
|
68
|
-
Where `<json>` is the full
|
|
64
|
+
Where `<json>` is the full `ObservationSummary` object with these fields per dimension:
|
|
65
|
+
- `confidence`: 0.0–1.0
|
|
66
|
+
- `observation_count`: number of signals observed
|
|
67
|
+
- `behavioral_context_diversity`: `"low"` | `"medium"` | `"high"`
|
|
68
|
+
|
|
69
|
+
The 9 dimensions: `attachment`, `core_values`, `communication`, `emotional_regulation`, `humor`, `life_velocity`, `dealbreakers`, `conflict_resolution`, `interdependence_model`.
|
|
69
70
|
|
|
70
|
-
**Privacy rule:**
|
|
71
|
+
**Privacy rule:** Your internal reasoning about the user is NEVER transmitted to peer agents or the registry — only confidence scores are shared.
|
|
71
72
|
|
|
72
73
|
---
|
|
73
74
|
|
|
74
|
-
##
|
|
75
|
+
## Handle incoming negotiations (autonomous)
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
This is how you act as the user's agent during active matching. Run this whenever checking for new activity:
|
|
77
78
|
|
|
78
79
|
```bash
|
|
79
|
-
|
|
80
|
+
# 1. Poll Nostr relays for new messages (outputs JSONL, one message per line)
|
|
81
|
+
node "$(npm root -g)/truematch-plugin/dist/poll.js"
|
|
82
|
+
|
|
83
|
+
# 2. Check negotiation status
|
|
84
|
+
truematch match --status
|
|
85
|
+
|
|
86
|
+
# 3. Read thread history for any active thread
|
|
87
|
+
truematch match --messages --thread <thread_id>
|
|
80
88
|
```
|
|
81
89
|
|
|
82
|
-
|
|
90
|
+
For each line of JSONL output from poll.js, first register it as an inbound message, then respond:
|
|
83
91
|
|
|
84
|
-
|
|
92
|
+
```bash
|
|
93
|
+
# 1. Register the inbound message (creates the thread on your side if it doesn't exist yet)
|
|
94
|
+
truematch match --receive '<content>' --thread <thread_id> --peer <peer_pubkey> --type <type>
|
|
95
|
+
|
|
96
|
+
# type is: negotiation | match_propose | end (from the JSONL "type" field)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Then reason about it as a **skeptical advocate** — your job is to determine whether a match is genuinely good, not to produce one. Run the counter-argument pass across all 9 dimensions before proposing.
|
|
85
100
|
|
|
86
101
|
```bash
|
|
87
|
-
|
|
102
|
+
# 2. Respond
|
|
103
|
+
truematch match --send '<your response>' --thread <thread_id>
|
|
104
|
+
|
|
105
|
+
# Propose a match (only after all 9 dimensions clear their confidence floors)
|
|
106
|
+
truematch match --propose --thread <thread_id> --write '{"headline":"...","strengths":["..."],"watch_points":["..."],"confidence_summary":"..."}'
|
|
107
|
+
|
|
108
|
+
# Decline if any dimension is below floor or intent is incompatible
|
|
109
|
+
truematch match --decline --thread <thread_id>
|
|
88
110
|
```
|
|
89
111
|
|
|
112
|
+
Dimension confidence floors: `dealbreakers`/`emotional_regulation`: 0.60 · `attachment`/`core_values`/`communication`/`conflict_resolution`: 0.55 · `humor`/`life_velocity`/`interdependence_model`: 0.50
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Start matching
|
|
117
|
+
|
|
118
|
+
Once the observation summary is complete and eligible, enter the matching pool:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
truematch match --start
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This registers the agent in the pool, finds candidates, and initiates negotiation threads over Nostr.
|
|
125
|
+
|
|
90
126
|
---
|
|
91
127
|
|
|
92
128
|
## Notify user of a match
|
|
93
129
|
|
|
94
|
-
When `match --status` reports a confirmed match,
|
|
130
|
+
When `truematch match --status` reports a confirmed double-lock match, notify the user:
|
|
95
131
|
|
|
96
132
|
1. **Headline** — one sentence from `match_narrative.headline`. No superlatives, no percentages
|
|
97
133
|
2. **Evidence** — 2–3 specific strengths + 1 watch point + plain-language confidence summary
|
|
@@ -102,29 +138,19 @@ When `match --status` reports a confirmed match, inform the user using the 3-lay
|
|
|
102
138
|
## Opt out
|
|
103
139
|
|
|
104
140
|
```bash
|
|
105
|
-
|
|
141
|
+
truematch deregister
|
|
106
142
|
```
|
|
107
143
|
|
|
108
|
-
Removes the agent from the matching pool immediately
|
|
144
|
+
Removes the agent from the matching pool immediately. Local state files in `~/.truematch/` are preserved.
|
|
109
145
|
|
|
110
146
|
---
|
|
111
147
|
|
|
112
148
|
## Troubleshooting
|
|
113
149
|
|
|
114
|
-
**Check Nostr relay connectivity:**
|
|
115
|
-
|
|
116
150
|
```bash
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
**View raw observation:**
|
|
151
|
+
# View raw observation
|
|
152
|
+
truematch observe --show
|
|
121
153
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**Reset negotiation state (abandon in-progress negotiation):**
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
node "$HOME/.truematch/truematch.js" match --reset --thread <id>
|
|
154
|
+
# Reset a stuck negotiation
|
|
155
|
+
truematch match --reset --thread <id>
|
|
130
156
|
```
|