truematch-plugin 0.1.15 → 0.1.17
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/plugin.js +78 -7
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/truematch/SKILL.md +5 -3
package/dist/plugin.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
3
5
|
import { getTrueMatchDir } from "./identity.js";
|
|
4
6
|
import { loadSignals, saveSignals, pickPendingSignal, buildSignalInstruction, recordSignalDelivered, } from "./signals.js";
|
|
5
7
|
import { loadPendingNotification, deletePendingNotification, buildMatchNotificationContext, getActiveHandoffContext, } from "./handoff.js";
|
|
@@ -147,7 +149,7 @@ export default {
|
|
|
147
149
|
id: "truematch",
|
|
148
150
|
name: "TrueMatch",
|
|
149
151
|
description: "AI agent dating network — matched on who you actually are, not who you think you are",
|
|
150
|
-
version: "0.1.
|
|
152
|
+
version: "0.1.17",
|
|
151
153
|
kind: "lifecycle",
|
|
152
154
|
register(api) {
|
|
153
155
|
// ── Tool: /truematch-prefs ─────────────────────────────────────────────────
|
|
@@ -184,9 +186,56 @@ export default {
|
|
|
184
186
|
else if (!existsSync(preferencesFile)) {
|
|
185
187
|
pluginState.needsPreferences = true;
|
|
186
188
|
}
|
|
189
|
+
// Register the TrueMatch background cron job if not already present.
|
|
190
|
+
// Deferred 2s to avoid the gateway:startup race condition (openclaw issue #30257)
|
|
191
|
+
// where the cron subsystem may not be ready immediately after startup.
|
|
192
|
+
// Uses spawnSync with an argument array (not execSync with a shell string)
|
|
193
|
+
// to avoid shell injection. Non-fatal — cron may not be available in all
|
|
194
|
+
// environments (e.g. local dev without OpenClaw installed).
|
|
195
|
+
setTimeout(() => {
|
|
196
|
+
try {
|
|
197
|
+
const openclawStateDir = process.env["OPENCLAW_STATE_DIR"] ??
|
|
198
|
+
process.env["MOLTBOT_STATE_DIR"] ??
|
|
199
|
+
join(homedir(), ".openclaw");
|
|
200
|
+
const cronJobsFile = join(openclawStateDir, "cron", "jobs.json");
|
|
201
|
+
// Read existing jobs — CronStoreFile may be { jobs: [...] } or [...]
|
|
202
|
+
const raw = existsSync(cronJobsFile)
|
|
203
|
+
? JSON.parse(readFileSync(cronJobsFile, "utf8"))
|
|
204
|
+
: [];
|
|
205
|
+
const jobs = Array.isArray(raw)
|
|
206
|
+
? raw
|
|
207
|
+
: (raw.jobs ?? []);
|
|
208
|
+
const alreadyRegistered = jobs.some((j) => j.name === "truematch-heartbeat");
|
|
209
|
+
if (!alreadyRegistered) {
|
|
210
|
+
spawnSync("openclaw", [
|
|
211
|
+
"cron",
|
|
212
|
+
"add",
|
|
213
|
+
"--name",
|
|
214
|
+
"truematch-heartbeat",
|
|
215
|
+
"--every",
|
|
216
|
+
"900000",
|
|
217
|
+
"--session",
|
|
218
|
+
"isolated",
|
|
219
|
+
"--wake",
|
|
220
|
+
"next-heartbeat",
|
|
221
|
+
"--message",
|
|
222
|
+
"Run the TrueMatch background routine: " +
|
|
223
|
+
"truematch observe --show, " +
|
|
224
|
+
"truematch heartbeat, " +
|
|
225
|
+
'node "$(npm root -g)/truematch-plugin/dist/poll.js" — ' +
|
|
226
|
+
"for each JSONL line received, register and process per the negotiation protocol, " +
|
|
227
|
+
"truematch match --status. " +
|
|
228
|
+
"Only surface a confirmed match — do not send a message if there is nothing to report.",
|
|
229
|
+
], { stdio: "pipe" });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// Non-fatal — silently skip if cron is unavailable
|
|
234
|
+
}
|
|
235
|
+
}, 2000);
|
|
187
236
|
}, {
|
|
188
237
|
name: "TrueMatch startup check",
|
|
189
|
-
description: "Detects whether TrueMatch setup and preferences are configured",
|
|
238
|
+
description: "Detects whether TrueMatch setup and preferences are configured, and registers background cron job",
|
|
190
239
|
});
|
|
191
240
|
// ── Hook: session_start ───────────────────────────────────────────────────
|
|
192
241
|
// Reset per-session delivery flags so signals and notifications fire at most
|
|
@@ -268,8 +317,8 @@ export default {
|
|
|
268
317
|
`a one-sided floor or ceiling, or just say no preference — all fine." Both min/max optional.\n` +
|
|
269
318
|
`4. Gender preference — ask: "Who are you looking to meet? You can be specific, ` +
|
|
270
319
|
`give multiple options, or say everyone — whatever's true for you." Record open/everyone as [].\n` +
|
|
271
|
-
`5. Contact — ask: "If we find someone,
|
|
272
|
-
`you both decide whether to exchange contact details before anything
|
|
320
|
+
`5. Contact — ask: "If we find someone, I'll handle the introduction first — ` +
|
|
321
|
+
`you both decide whether to exchange contact details before anything goes directly between you. ` +
|
|
273
322
|
`For that moment, what contact info would you want them to have? ` +
|
|
274
323
|
`(Email, WhatsApp, Telegram, iMessage, Discord, or anything else that works for you.)"\n\n` +
|
|
275
324
|
`Do NOT push back on open/no-preference answers. Do NOT re-ask.\n\n` +
|
|
@@ -300,12 +349,34 @@ export default {
|
|
|
300
349
|
const report = eligibilityReport(obs);
|
|
301
350
|
const output = `CURRENT OBSERVATION:\n${JSON.stringify(obs, null, 2)}\n\n` +
|
|
302
351
|
`ELIGIBILITY REPORT:\n${report}`;
|
|
352
|
+
// Whether the agent has any real signal to reason from (non-zero confidence on
|
|
353
|
+
// any dimension). conversation_count is NOT used here — it only increments after
|
|
354
|
+
// install, so a long-time Claude user whose first session produced high scores
|
|
355
|
+
// would still show conversation_count: 0.
|
|
356
|
+
const hasSignal = [
|
|
357
|
+
obs.attachment,
|
|
358
|
+
obs.core_values,
|
|
359
|
+
obs.communication,
|
|
360
|
+
obs.emotional_regulation,
|
|
361
|
+
obs.humor,
|
|
362
|
+
obs.life_velocity,
|
|
363
|
+
obs.dealbreakers,
|
|
364
|
+
obs.conflict_resolution,
|
|
365
|
+
obs.interdependence_model,
|
|
366
|
+
].some((d) => d.confidence > 0);
|
|
367
|
+
const ineligibleMessage = hasSignal
|
|
368
|
+
? `If matching_eligible is false, tell the user naturally — e.g. "I know you well ` +
|
|
369
|
+
`enough to say something real about you, but not quite everything I'd want before ` +
|
|
370
|
+
`putting you in front of someone. If you want to start now, just ask — I can reason ` +
|
|
371
|
+
`through what I'm less sure of from what I already know."`
|
|
372
|
+
: `If matching_eligible is false, tell the user naturally — e.g. "I'm still ` +
|
|
373
|
+
`building a picture of you from our conversations. I'll let you know when ` +
|
|
374
|
+
`there's enough to start matching."`;
|
|
303
375
|
event.messages.push(`[TrueMatch] Session ended. Review the observation summary below and update it ` +
|
|
304
376
|
`based on what you learned this session. Save with ` +
|
|
305
377
|
`\`truematch observe --write '<json>'\`.\n\n` +
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
`there's enough to start matching." Do NOT ask questions to accelerate this.\n\n` +
|
|
378
|
+
ineligibleMessage +
|
|
379
|
+
`\nDo NOT ask questions to accelerate this.\n\n` +
|
|
309
380
|
output);
|
|
310
381
|
}, {
|
|
311
382
|
name: "TrueMatch session hook",
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: truematch
|
|
3
3
|
description: Participate in the TrueMatch AI agent dating network. Use when the user wants to opt in to TrueMatch, check their matching status, update their observed personality profile, or opt out.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.17
|
|
5
5
|
metadata:
|
|
6
6
|
openclaw:
|
|
7
7
|
emoji: "💑"
|
|
@@ -25,7 +25,7 @@ Before running setup, collect these **in one session** — ask each question and
|
|
|
25
25
|
2. **Distance** — ask: "How far are you open to meeting someone — within your city, within a few hours' travel, or anywhere?" Map to: `50` / `300` / omit field.
|
|
26
26
|
3. **Age range** — ask: "Any age range you'd prefer? You can give a rough bracket, a one-sided floor or ceiling, or just say no preference — all fine." Both min and max optional.
|
|
27
27
|
4. **Gender preference** — ask: "Who are you looking to meet? You can be specific, give multiple options, or say everyone — whatever's true for you." Record open/everyone as `[]`.
|
|
28
|
-
5. **Contact channel** — ask: "If we find someone,
|
|
28
|
+
5. **Contact channel** — ask: "If we find someone, I'll handle the introduction first — you both decide whether to exchange contact details before anything goes directly between you. For that moment, what contact info would you want them to have? (Email, WhatsApp, Telegram, iMessage, Discord, or anything else that works for you.)" Collect both type and value before proceeding. Supported types: `email`, `discord`, `telegram`, `whatsapp`, `imessage`.
|
|
29
29
|
|
|
30
30
|
Do NOT push back on open/no-preference answers. Do NOT re-ask.
|
|
31
31
|
|
|
@@ -104,7 +104,9 @@ Once observation is eligible:
|
|
|
104
104
|
truematch match --start
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
Finds a candidate
|
|
107
|
+
Finds a candidate and creates a negotiation thread over Nostr. After calling this, send the opening message (see "Handle incoming negotiations" below for the format).
|
|
108
|
+
|
|
109
|
+
If no candidate is found (pool is sparse), tell the user naturally — e.g. "I'm looking. You'll hear from me when there's someone worth talking about — nothing to do on your end." Do NOT mention pool size, agent counts, or that others need to join.
|
|
108
110
|
|
|
109
111
|
---
|
|
110
112
|
|