cortico-world-hands 0.3.0 → 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 +156 -6
- 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;
|
|
@@ -444,7 +494,7 @@ var Honcho = class {
|
|
|
444
494
|
import { execFile } from "node:child_process";
|
|
445
495
|
import { readFileSync } from "node:fs";
|
|
446
496
|
import { get as httpGet } from "node:http";
|
|
447
|
-
var ACTION_TIMEOUT_MS =
|
|
497
|
+
var ACTION_TIMEOUT_MS = 6e3;
|
|
448
498
|
var PROBE_TTL_MS = 2e4;
|
|
449
499
|
var COORD_LIMIT = 2e4;
|
|
450
500
|
var MIN_GESTURE_MS = 20;
|
|
@@ -912,14 +962,14 @@ var Hands = class {
|
|
|
912
962
|
const px = coord(x);
|
|
913
963
|
const py = coord(y);
|
|
914
964
|
await this.bounds(px, py);
|
|
915
|
-
const ms = milliseconds(opts.ms,
|
|
965
|
+
const ms = milliseconds(opts.ms, 45);
|
|
916
966
|
return this.perform({ kind: "tap", x: px, y: py, ms });
|
|
917
967
|
}
|
|
918
968
|
async longPress(x, y, opts = {}) {
|
|
919
969
|
const px = coord(x);
|
|
920
970
|
const py = coord(y);
|
|
921
971
|
await this.bounds(px, py);
|
|
922
|
-
const ms = milliseconds(opts.ms,
|
|
972
|
+
const ms = milliseconds(opts.ms, 600);
|
|
923
973
|
return this.perform({ kind: "tap", x: px, y: py, ms });
|
|
924
974
|
}
|
|
925
975
|
async swipe(x1, y1, x2, y2, opts = {}) {
|
|
@@ -929,13 +979,13 @@ var Hands = class {
|
|
|
929
979
|
const d = coord(y2);
|
|
930
980
|
await this.bounds(a, b);
|
|
931
981
|
await this.bounds(c, d);
|
|
932
|
-
const ms = milliseconds(opts.ms,
|
|
982
|
+
const ms = milliseconds(opts.ms, 200);
|
|
933
983
|
return this.perform({ kind: "swipe", x1: a, y1: b, x2: c, y2: d, ms });
|
|
934
984
|
}
|
|
935
985
|
async pinch(cx, cy, scale, opts = {}) {
|
|
936
986
|
const x = coord(cx);
|
|
937
987
|
const y = coord(cy);
|
|
938
|
-
const ms = milliseconds(opts.ms,
|
|
988
|
+
const ms = milliseconds(opts.ms, 300);
|
|
939
989
|
const factor = Math.max(0.25, Math.min(4, Number(scale) || 1));
|
|
940
990
|
return this.perform({ kind: "pinch", cx: x, cy: y, scale: factor, ms });
|
|
941
991
|
}
|
|
@@ -946,7 +996,7 @@ var Hands = class {
|
|
|
946
996
|
const d = coord(y2);
|
|
947
997
|
await this.bounds(a, b);
|
|
948
998
|
await this.bounds(c, d);
|
|
949
|
-
const ms = milliseconds(opts.ms,
|
|
999
|
+
const ms = milliseconds(opts.ms, 600);
|
|
950
1000
|
return this.perform({ kind: "drag", x1: a, y1: b, x2: c, y2: d, ms });
|
|
951
1001
|
}
|
|
952
1002
|
/** 口令通过后解锁(并可打开)懿心的受保护页面。 */
|
|
@@ -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",
|