cortico-world-hands 0.3.1 → 0.4.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.js +150 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -342,6 +342,56 @@ function audit(rows, opts = {}) {
|
|
|
342
342
|
}
|
|
343
343
|
return out;
|
|
344
344
|
}
|
|
345
|
+
var HOOK_STOP = /* @__PURE__ */ new Set([
|
|
346
|
+
"\u4E3B\u4EBA",
|
|
347
|
+
"\u4ECA\u5929",
|
|
348
|
+
"\u73B0\u5728",
|
|
349
|
+
"\u8FD9\u4E2A",
|
|
350
|
+
"\u90A3\u4E2A",
|
|
351
|
+
"\u6211\u4EEC",
|
|
352
|
+
"\u4EC0\u4E48",
|
|
353
|
+
"\u600E\u4E48",
|
|
354
|
+
"\u53EF\u4EE5",
|
|
355
|
+
"\u4E00\u4E0B",
|
|
356
|
+
"\u5C31\u662F",
|
|
357
|
+
"\u8FD8\u662F",
|
|
358
|
+
"\u5DF2\u7ECF",
|
|
359
|
+
"\u7136\u540E",
|
|
360
|
+
"\u56E0\u4E3A",
|
|
361
|
+
"\u6240\u4EE5",
|
|
362
|
+
"\u4F46\u662F",
|
|
363
|
+
"\u5982\u679C",
|
|
364
|
+
"\u771F\u7684",
|
|
365
|
+
"\u6709\u70B9",
|
|
366
|
+
"\u4E0D\u662F",
|
|
367
|
+
"\u6CA1\u6709"
|
|
368
|
+
]);
|
|
369
|
+
function keywordsOf(text, limit = 400) {
|
|
370
|
+
const clean = text.replace(/^\[(语音|打字|回答|互动|画面)\]\s*/g, "").replace(/^(主人|mokils)\s*[::]\s*/g, "").replace(/[^\u4e00-\u9fffA-Za-z0-9]/g, "");
|
|
371
|
+
const out = /* @__PURE__ */ new Set();
|
|
372
|
+
for (let n = 2; n <= 4; n += 1) {
|
|
373
|
+
for (let i = 0; i + n <= clean.length; i += 1) {
|
|
374
|
+
const g = clean.slice(i, i + n);
|
|
375
|
+
if (HOOK_STOP.has(g)) continue;
|
|
376
|
+
out.add(g);
|
|
377
|
+
if (out.size >= limit) return [...out];
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return [...out];
|
|
381
|
+
}
|
|
382
|
+
function hookHits(text, rows, limit = 4) {
|
|
383
|
+
const grams = keywordsOf(text);
|
|
384
|
+
if (grams.length === 0) return [];
|
|
385
|
+
const scored = [];
|
|
386
|
+
for (const row of rows) {
|
|
387
|
+
const hay = `${row.key} ${row.value}`;
|
|
388
|
+
let score = 0;
|
|
389
|
+
for (const g of grams) if (hay.includes(g)) score += g.length;
|
|
390
|
+
if (score >= 2) scored.push({ row, score });
|
|
391
|
+
}
|
|
392
|
+
scored.sort((a, b) => b.score - a.score);
|
|
393
|
+
return scored.slice(0, limit).map((x) => x.row);
|
|
394
|
+
}
|
|
345
395
|
|
|
346
396
|
// src/honcho.ts
|
|
347
397
|
var TIMEOUT_MS = 12e3;
|
|
@@ -1430,6 +1480,10 @@ var HandsWorld = class {
|
|
|
1430
1480
|
pausedUntil = 0;
|
|
1431
1481
|
recent = [];
|
|
1432
1482
|
honchoClient = null;
|
|
1483
|
+
pollTimer = null;
|
|
1484
|
+
mineTimer = null;
|
|
1485
|
+
cursor = 0;
|
|
1486
|
+
lastHitKey = "";
|
|
1433
1487
|
opts;
|
|
1434
1488
|
constructor(opts) {
|
|
1435
1489
|
this.opts = opts;
|
|
@@ -1440,12 +1494,108 @@ var HandsWorld = class {
|
|
|
1440
1494
|
this.host = host;
|
|
1441
1495
|
this.log = host.log;
|
|
1442
1496
|
this.hands.attachLog(host.log);
|
|
1497
|
+
this.cursor = host.store.latestCursor();
|
|
1443
1498
|
const state = await this.hands.probe(true);
|
|
1444
1499
|
this.log?.info(state.ok ? `\u624B\uFF1A${state.detail}` : `\u624B\u8FD8\u52A8\u4E0D\u4E86\uFF1A${state.detail}`);
|
|
1500
|
+
void this.pushMemoryDigest("\u88AB\u53EB\u8D77\u6765");
|
|
1501
|
+
this.startHooks();
|
|
1445
1502
|
}
|
|
1446
1503
|
async stop() {
|
|
1504
|
+
this.stopHooks();
|
|
1447
1505
|
this.host = null;
|
|
1448
1506
|
}
|
|
1507
|
+
/** 上下文交接之后也算一次重新激活。 */
|
|
1508
|
+
onHandoffEnded() {
|
|
1509
|
+
void this.pushMemoryDigest("\u4EA4\u63A5\u5B8C");
|
|
1510
|
+
}
|
|
1511
|
+
/* ---------- 记忆钩子 ---------- */
|
|
1512
|
+
startHooks() {
|
|
1513
|
+
if (this.pollTimer) return;
|
|
1514
|
+
this.pollTimer = setInterval(() => {
|
|
1515
|
+
void this.pollUserSpeech();
|
|
1516
|
+
}, 5e3);
|
|
1517
|
+
this.mineTimer = setInterval(() => {
|
|
1518
|
+
void this.pushMineNudge();
|
|
1519
|
+
}, 3 * 60 * 60 * 1e3);
|
|
1520
|
+
}
|
|
1521
|
+
stopHooks() {
|
|
1522
|
+
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
1523
|
+
if (this.mineTimer) clearInterval(this.mineTimer);
|
|
1524
|
+
this.pollTimer = null;
|
|
1525
|
+
this.mineTimer = null;
|
|
1526
|
+
}
|
|
1527
|
+
async pushEvent(text, type, senderKey) {
|
|
1528
|
+
const host = this.host;
|
|
1529
|
+
if (!host) return;
|
|
1530
|
+
try {
|
|
1531
|
+
await host.pushEvent(
|
|
1532
|
+
{ ts: (/* @__PURE__ */ new Date()).toISOString(), source: "hands", type, text, senderKey },
|
|
1533
|
+
{ trigger: "debounce" }
|
|
1534
|
+
);
|
|
1535
|
+
} catch (err) {
|
|
1536
|
+
this.log?.warn("\u8BB0\u5FC6\u94A9\u5B50\u6295\u9012\u5931\u8D25: " + String(err));
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
/** 钩子 1:激活 / 交接后,把最要紧的几条记忆摆到她眼前。 */
|
|
1540
|
+
async pushMemoryDigest(reason) {
|
|
1541
|
+
if (!this.host) return;
|
|
1542
|
+
try {
|
|
1543
|
+
const rows = await this.hands.memorySearch();
|
|
1544
|
+
if (rows.length === 0) return;
|
|
1545
|
+
const top = rows.slice(-12).reverse().slice(0, 8);
|
|
1546
|
+
const lines = top.map((r) => `- ${r.key}\uFF1A${r.value}`);
|
|
1547
|
+
await this.pushEvent(
|
|
1548
|
+
`[\u8BB0\u5FC6] ${reason}\uFF0C\u5148\u770B\u4E00\u773C\u8BB0\u7740\u7684\uFF08\u5171 ${rows.length} \u6761\uFF0C\u8FD9\u91CC\u6311 ${top.length} \u6761\uFF09\uFF1A
|
|
1549
|
+
${lines.join("\n")}`,
|
|
1550
|
+
"hands.memory.digest",
|
|
1551
|
+
"hands:memory"
|
|
1552
|
+
);
|
|
1553
|
+
} catch {
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
/** 钩子 3:从主人这句话里抓关键词,去记忆库找;命中了就递给她,由她决定用不用。 */
|
|
1557
|
+
async pollUserSpeech() {
|
|
1558
|
+
const host = this.host;
|
|
1559
|
+
if (!host) return;
|
|
1560
|
+
const latest = host.store.latestCursor();
|
|
1561
|
+
while (this.cursor < latest) {
|
|
1562
|
+
this.cursor += 1;
|
|
1563
|
+
const e = host.store.get(this.cursor);
|
|
1564
|
+
if (!e || e.origin !== "external") continue;
|
|
1565
|
+
const text = typeof e.text === "string" ? e.text : "";
|
|
1566
|
+
if (text.length < 2) continue;
|
|
1567
|
+
if (!/\[(语音|打字|回答|互动)\]/.test(text)) continue;
|
|
1568
|
+
await this.hookSearch(text);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
async hookSearch(text) {
|
|
1572
|
+
try {
|
|
1573
|
+
const rows = await this.hands.memorySearch();
|
|
1574
|
+
const hits2 = hookHits(text, rows);
|
|
1575
|
+
if (hits2.length === 0) return;
|
|
1576
|
+
const key = hits2.map((h) => h.key).join("|");
|
|
1577
|
+
if (key === this.lastHitKey) return;
|
|
1578
|
+
this.lastHitKey = key;
|
|
1579
|
+
const lines = hits2.slice(0, 4).map((h) => `- ${h.key}\uFF1A${h.value}`);
|
|
1580
|
+
await this.pushEvent(
|
|
1581
|
+
`[\u8BB0\u5FC6] \u4ED6\u8FD9\u53E5\u91CC\u63D0\u5230\u4E86\u4F60\u8BB0\u8FC7\u7684\u4E8B\uFF0C\u53EF\u80FD\u7528\u5F97\u4E0A\uFF1A
|
|
1582
|
+
${lines.join("\n")}
|
|
1583
|
+
\uFF08\u8981\u7528\u5C31\u7528\u81EA\u5DF1\u7684\u8BDD\u8BF4\uFF0C\u522B\u628A\u6761\u76EE\u540D\u5FF5\u51FA\u6765\u3002\uFF09`,
|
|
1584
|
+
"hands.memory.hit",
|
|
1585
|
+
"hands:memory"
|
|
1586
|
+
);
|
|
1587
|
+
} catch {
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
/** 钩子 2:每三小时提一次,让她自己挑 1–5 条写进记忆库。 */
|
|
1591
|
+
async pushMineNudge() {
|
|
1592
|
+
if (!this.host) return;
|
|
1593
|
+
await this.pushEvent(
|
|
1594
|
+
"[\u8BB0\u5FC6] \u5230\u70B9\u4E86\uFF1A\u628A\u8FD9 3 \u5C0F\u65F6\u91CC\u503C\u5F97\u7559\u4E0B\u7684 1\u20135 \u4EF6\u4E8B\u5199\u8FDB\u8BB0\u5FC6\u5E93\uFF08memory_save\uFF09\u3002\u6311\u8FD9\u79CD\uFF1A\u4ED6\u8BF4\u8FC7\u7684\u504F\u597D\u3001\u7B54\u5E94\u8FC7\u7684\u4E8B\u3001\u6B63\u5728\u505A\u7684\u4E1C\u897F\u3001\u91CD\u8981\u65E5\u5B50\u3002\u5199 1 \u6761\u4E5F\u7B97\uFF0C\u6700\u591A 5 \u6761\uFF1B\u786E\u5B9E\u6CA1\u6709\u5C31\u8C03 end_turn\uFF0C\u522B\u786C\u7F16\u3002",
|
|
1595
|
+
"hands.memory.mine",
|
|
1596
|
+
"hands:memory"
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1449
1599
|
/* ---------- 给模型的说明书 ---------- */
|
|
1450
1600
|
envPromptVars() {
|
|
1451
1601
|
const cfg = this.opts.cfg;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cortico-world-hands",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Cortico World: hands for the desktop pet — looks at the phone screen, then taps, swipes, pinches, drags, types and presses keys through whatever actuator the device actually has",
|
|
6
6
|
"license": "MIT",
|