luckytri 1.0.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/.github/SECURITY.md +71 -0
- package/LICENSE +21 -0
- package/README.en.md +250 -0
- package/README.md +250 -0
- package/bin/luckytri.js +107 -0
- package/docs/brand/banner.png +0 -0
- package/docs/brand/palette.png +0 -0
- package/docs/brand/portrait.png +0 -0
- package/docs/en/CHANGELOG.md +113 -0
- package/docs/en/architecture.md +152 -0
- package/docs/en/chat-style.md +62 -0
- package/docs/en/connect.md +63 -0
- package/docs/en/guide.md +216 -0
- package/docs/en/her-life.md +189 -0
- package/docs/en/npm-release.md +25 -0
- package/docs/en/plugins.md +82 -0
- package/docs/en/recovery.md +28 -0
- package/docs/en/time.md +71 -0
- package/docs/en/webui.md +73 -0
- package/docs/zh/CHANGELOG.md +113 -0
- package/docs/zh/architecture.md +152 -0
- package/docs/zh/chat-style.md +62 -0
- package/docs/zh/connect.md +63 -0
- package/docs/zh/guide.md +216 -0
- package/docs/zh/her-life.md +189 -0
- package/docs/zh/npm-release.md +33 -0
- package/docs/zh/plugins.md +82 -0
- package/docs/zh/recovery.md +28 -0
- package/docs/zh/time.md +71 -0
- package/docs/zh/webui.md +73 -0
- package/package.json +108 -0
- package/plugins/sdk/index.d.ts +155 -0
- package/plugins/sdk/testing.js +58 -0
- package/plugins/weather/index.js +82 -0
- package/plugins/weather/luckytri-plugin.json +29 -0
- package/public/app/assets/ChatsPage-C-FcRapR.js +3 -0
- package/public/app/assets/ChatsPage-DfyPCS9N.css +1 -0
- package/public/app/assets/Empty-CpP5kQHH.js +2 -0
- package/public/app/assets/Empty-DmUB2-Bk.css +1 -0
- package/public/app/assets/Field-D4gY-INU.js +1 -0
- package/public/app/assets/HeartPage-Bft9wZp5.css +1 -0
- package/public/app/assets/HeartPage-CPy_thQd.js +1 -0
- package/public/app/assets/LifePage-BUi5euBF.css +1 -0
- package/public/app/assets/LifePage-CxWvwmSf.js +1 -0
- package/public/app/assets/MemoryPage-DC4rhQ68.js +1 -0
- package/public/app/assets/MemoryPage-cZvXfkXb.css +1 -0
- package/public/app/assets/NaturePage-CTXHCE2D.css +1 -0
- package/public/app/assets/NaturePage-IvYw04Zq.js +3 -0
- package/public/app/assets/PeoplePage-BmUxkrOU.css +1 -0
- package/public/app/assets/PeoplePage-Cqefm9-q.js +1 -0
- package/public/app/assets/PluginsPage-DKSaKvXb.js +2 -0
- package/public/app/assets/PluginsPage-HyBe5D8w.css +1 -0
- package/public/app/assets/SystemPage-CAMPoYqM.css +1 -0
- package/public/app/assets/SystemPage-CJmtJkrK.js +1 -0
- package/public/app/assets/TimePage-BOms_n4I.css +1 -0
- package/public/app/assets/TimePage-Cw44inM4.js +3 -0
- package/public/app/assets/formatters-ZmW_xFHw.js +1 -0
- package/public/app/assets/index-BheRW-1t.js +4 -0
- package/public/app/assets/index-YDz1ree1.css +1 -0
- package/public/app/assets/knowledge-DG0e0uKw.js +1 -0
- package/public/app/assets/liquid-garden-IMoHBwzc.png +0 -0
- package/public/app/assets/luckytri-home-95QKeMtc.png +0 -0
- package/public/app/assets/media-BzPDBm7e.js +1 -0
- package/public/app/index.html +27 -0
- package/public/guide.en.html +1053 -0
- package/public/guide.html +893 -0
- package/public/plugin-kit/v1/bridge.js +30 -0
- package/public/plugin-kit/v1/kit.css +14 -0
- package/public/tutorial.en.md +214 -0
- package/public/tutorial.md +214 -0
- package/scripts/backup.js +311 -0
- package/scripts/browser.js +41 -0
- package/scripts/launch.js +94 -0
- package/scripts/plugin.js +150 -0
- package/scripts/proxy-config.js +167 -0
- package/scripts/recover.js +26 -0
- package/scripts/runtime.js +25 -0
- package/scripts/service.js +47 -0
- package/scripts/setup.js +50 -0
- package/scripts/start-server.js +41 -0
- package/scripts/stop.js +36 -0
- package/server/app.js +96 -0
- package/server/channels/adapters.js +27 -0
- package/server/channels/contract.js +57 -0
- package/server/channels/hub.js +165 -0
- package/server/channels/index.js +25 -0
- package/server/channels/onebot/adapter.js +288 -0
- package/server/channels/onebot/index.js +336 -0
- package/server/channels/onebot/token.js +3 -0
- package/server/channels/qqbot/adapter.js +253 -0
- package/server/channels/qqbot/api.js +199 -0
- package/server/channels/qqbot/credentials.js +11 -0
- package/server/channels/qqbot/gateway.js +274 -0
- package/server/channels/qqbot/index.js +389 -0
- package/server/channels/segments.js +40 -0
- package/server/channels/session-key.js +153 -0
- package/server/channels/state.js +42 -0
- package/server/core/api.js +564 -0
- package/server/core/capabilities.js +208 -0
- package/server/core/context-builder.js +288 -0
- package/server/core/context-compactor.js +454 -0
- package/server/core/conversation-cues.js +325 -0
- package/server/core/conversation-manager.js +87 -0
- package/server/core/events.js +60 -0
- package/server/core/feedback.js +8 -0
- package/server/core/group-style.js +72 -0
- package/server/core/image-preprocessor.js +208 -0
- package/server/core/initiative-context.js +66 -0
- package/server/core/input-budget.js +32 -0
- package/server/core/local-demo.js +98 -0
- package/server/core/message-manager.js +118 -0
- package/server/core/message-scheduler.js +78 -0
- package/server/core/model-json.js +190 -0
- package/server/core/model-manager.js +1213 -0
- package/server/core/model-presets.js +900 -0
- package/server/core/name-call.js +29 -0
- package/server/core/net-guard.js +49 -0
- package/server/core/network.js +157 -0
- package/server/core/orchestrator.js +569 -0
- package/server/core/perceivers.js +91 -0
- package/server/core/person-name.js +22 -0
- package/server/core/persona-manager.js +243 -0
- package/server/core/reply-delivery.js +393 -0
- package/server/core/reply-target-resolver.js +50 -0
- package/server/core/repository.js +293 -0
- package/server/core/response-generator.js +50 -0
- package/server/core/response-validator.js +508 -0
- package/server/core/retired-prompts.js +51 -0
- package/server/core/sessions.js +109 -0
- package/server/core/speaker-names.js +92 -0
- package/server/core/topic-tracker.js +65 -0
- package/server/core/turn-processor.js +630 -0
- package/server/core/turn.js +236 -0
- package/server/core/vision-manager.js +633 -0
- package/server/http.js +18 -0
- package/server/i18n/decisions.js +206 -0
- package/server/i18n/errors.js +497 -0
- package/server/i18n/index.js +144 -0
- package/server/i18n/labels.js +653 -0
- package/server/i18n/models.js +111 -0
- package/server/index.js +102 -0
- package/server/knowledge/api.js +342 -0
- package/server/knowledge/embedding-profile.js +114 -0
- package/server/knowledge/index.js +13 -0
- package/server/knowledge/manager.js +386 -0
- package/server/knowledge/retrieval.js +93 -0
- package/server/knowledge/schema.js +154 -0
- package/server/knowledge/vector-index.js +90 -0
- package/server/mind/affect.js +265 -0
- package/server/mind/anticipations.js +507 -0
- package/server/mind/api.js +703 -0
- package/server/mind/attention.js +200 -0
- package/server/mind/bonds.js +558 -0
- package/server/mind/budget.js +152 -0
- package/server/mind/clock.js +83 -0
- package/server/mind/context-selection.js +61 -0
- package/server/mind/continuity.js +562 -0
- package/server/mind/conversation-origin.js +175 -0
- package/server/mind/day-planner.js +324 -0
- package/server/mind/days.js +210 -0
- package/server/mind/evidence-read.js +45 -0
- package/server/mind/evidence.js +148 -0
- package/server/mind/faces.js +138 -0
- package/server/mind/guard.js +60 -0
- package/server/mind/index.js +445 -0
- package/server/mind/initiative.js +333 -0
- package/server/mind/life/activities.js +21 -0
- package/server/mind/life/context.js +309 -0
- package/server/mind/life/diary.js +786 -0
- package/server/mind/life/growth.js +252 -0
- package/server/mind/life/index.js +1049 -0
- package/server/mind/life/presence.js +305 -0
- package/server/mind/meetings.js +1069 -0
- package/server/mind/memory-policy.js +48 -0
- package/server/mind/memory.js +888 -0
- package/server/mind/nature.js +262 -0
- package/server/mind/novelty.js +19 -0
- package/server/mind/observations.js +84 -0
- package/server/mind/own-day.js +324 -0
- package/server/mind/own-voice.js +318 -0
- package/server/mind/periods.js +118 -0
- package/server/mind/reading.js +120 -0
- package/server/mind/salience.js +201 -0
- package/server/mind/schema.js +562 -0
- package/server/mind/self.js +484 -0
- package/server/mind/senses.js +53 -0
- package/server/mind/thoughts.js +274 -0
- package/server/mind/time/activity-clock.js +196 -0
- package/server/mind/time/agenda.js +242 -0
- package/server/mind/time/api.js +200 -0
- package/server/mind/time/attention.js +34 -0
- package/server/mind/time/availability.js +69 -0
- package/server/mind/time/commitment-review.js +198 -0
- package/server/mind/time/delivery-links.js +130 -0
- package/server/mind/time/executor.js +512 -0
- package/server/mind/time/experiences.js +128 -0
- package/server/mind/time/games.js +562 -0
- package/server/mind/time/index.js +739 -0
- package/server/mind/time/intent.js +166 -0
- package/server/mind/time/kinds.js +171 -0
- package/server/mind/time/model-material.js +77 -0
- package/server/mind/time/plugin-activity.js +207 -0
- package/server/mind/time/presentation.js +51 -0
- package/server/mind/time/schedule.js +83 -0
- package/server/mind/time/schema.js +84 -0
- package/server/mind/time/search.js +383 -0
- package/server/mind/time/sharing.js +472 -0
- package/server/mind/time/task-links.js +311 -0
- package/server/mind/time/tasks.js +587 -0
- package/server/mind/time/works.js +432 -0
- package/server/mind/traits.js +191 -0
- package/server/mind/util.js +110 -0
- package/server/mind/view.js +273 -0
- package/server/paths.js +102 -0
- package/server/plugins/api.js +204 -0
- package/server/plugins/host.js +967 -0
- package/server/plugins/install.js +295 -0
- package/server/plugins/manifest.js +201 -0
- package/server/plugins/permissions.js +42 -0
- package/server/plugins/runner/guard.mjs +36 -0
- package/server/plugins/runner/worker.mjs +250 -0
- package/server/plugins/schema.js +91 -0
- package/server/plugins/zip.js +112 -0
- package/server/startup-lock.js +118 -0
- package/server/storage/archive.js +241 -0
- package/server/storage/backup-cleanup-worker.js +8 -0
- package/server/storage/backup-cleanup.js +414 -0
- package/server/storage/backup-lock.js +76 -0
- package/server/storage/backup-scheduler.js +131 -0
- package/server/storage/store.js +199 -0
- package/server/studio/display-names.js +138 -0
- package/server/studio/management.js +102 -0
- package/server/studio/readiness.js +190 -0
- package/server/studio/settings.js +236 -0
- package/server/version.js +10 -0
package/docs/en/guide.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
<p align="right"><a href="../zh/guide.md">中文</a> · <b>English</b></p>
|
|
2
|
+
|
|
3
|
+
# LuckyTri · Getting Started
|
|
4
|
+
|
|
5
|
+
LuckyTri runs on your own computer and meets people through QQ. QQ has two ways to connect, and you pick one: **OneBot 11** (you bring a connector, reverse WebSocket) or the **QQ official bot** (QQ Open Platform). With either, her memory, mood and behavior are exactly the same; only the way messages arrive differs. You configure the model API yourself.
|
|
6
|
+
|
|
7
|
+
The top-right corner of the interface switches between "中文" and "English". Chinese is the default and your choice is remembered in this browser. What she says, writes in her diary and keeps as her own thoughts is never translated; that is her own content.
|
|
8
|
+
|
|
9
|
+
## 1. Start
|
|
10
|
+
|
|
11
|
+
You need Node.js 24.5 or newer. Install globally:
|
|
12
|
+
|
|
13
|
+
```powershell
|
|
14
|
+
node --version
|
|
15
|
+
npm install -g luckytri
|
|
16
|
+
luckytri
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
LuckyTri starts in the background and opens <http://127.0.0.1:3210>, creating local configuration on the first run. The package includes the built WebUI. The experimental-SQLite notice does not mean the start failed.
|
|
20
|
+
|
|
21
|
+
Use `luckytri paths` to find `.env`, open it in a local editor and enter `ADMIN_TOKEN` in the studio. `luckytri --no-browser` launches without opening a browser, `luckytri start` runs in the foreground, and `luckytri stop` stops the service. Update with `luckytri stop`, `npm install -g luckytri@latest`, then `luckytri`. Your configuration and data stay intact. Default directories are listed in the [README](../../README.en.md#configuration-data-and-privacy).
|
|
22
|
+
|
|
23
|
+
For source development, run `npm install`, `npm run setup`, `npm run build` and `npm start` in the checkout. Later examples using `npm run setup/stop/backup/recovery/plugin` apply to source checkouts; global users use the corresponding `luckytri setup/stop/backup/recovery/plugin` commands.
|
|
24
|
+
|
|
25
|
+
On Windows you can double-click `启动LuckyTri.cmd` for daily use. If the service is already running, it only opens LuckyTri. Closing the browser does not stop the service; to stop it double-click `停止LuckyTri.cmd` or run `npm run stop` in the project folder. That command stops this project only and leaves other Node processes alone. When running in the foreground you can also press `Ctrl+C` in that terminal.
|
|
26
|
+
|
|
27
|
+
LuckyTri is "TA's little world". The left navigation has three groups and ten entries:
|
|
28
|
+
|
|
29
|
+
| Group | Entry | What is inside |
|
|
30
|
+
| ---------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| TA | Now | TA's mood right now, what she is doing, what she said lately, what is on her mind and what she is waiting for; today's timeline, token usage and the status bar |
|
|
32
|
+
| TA | Inner life | The star map (TA's threads of self), notes she keeps in mind, the mood ribbon |
|
|
33
|
+
| TA | People | The people galaxy, each person's detail page, how TA is in each group |
|
|
34
|
+
| TA | Lifetime | The diary and TA on that day, reviews, "my road so far" and chapters, promises and anticipation, the bookshelf |
|
|
35
|
+
| Daily life | Time | The record of TA's actual life: today's activity, to-dos, works library, ongoing projects, experiences |
|
|
36
|
+
| Daily life | Chat | Session list, live chat and "why she said this", feedback, session settings, simulated messages, back to that moment (replay) |
|
|
37
|
+
| Daily life | Memory | What TA remembers, the bookshelf |
|
|
38
|
+
| Settings | Nature | Name and character (the glow beside it previews live), daily rhythm, TA's days, daily tokens, advanced prompts, trial chat |
|
|
39
|
+
| Settings | System | Connect QQ, model library, run switches |
|
|
40
|
+
| Settings | Plugins | Enable, install and remove the ways she touches the world. See [Plugins](plugins.md) |
|
|
41
|
+
|
|
42
|
+
The colors, sky and motion follow TA's mood and rhythm: warm when she is happy, grey-blue when she is low, night once she is asleep. At the bottom left you can pin one theme or turn on "Reduce motion". The small TA at the bottom right answers a poke, a long press is a head-pat, and a double click or "Chat" opens the trial chat. These are interface animations only and never change her mind. On a phone the navigation sits at the bottom and the other entries are under "More".
|
|
43
|
+
|
|
44
|
+
## 2. Local tokens
|
|
45
|
+
|
|
46
|
+
Configuration is created on first launch. You can also initialize in advance:
|
|
47
|
+
|
|
48
|
+
```powershell
|
|
49
|
+
luckytri setup
|
|
50
|
+
luckytri paths
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
When there is no `.env`, setup writes an admin token and an OneBot connection token. An existing `.env` is kept as it is.
|
|
54
|
+
|
|
55
|
+
```dotenv
|
|
56
|
+
HOST=127.0.0.1
|
|
57
|
+
PORT=3210
|
|
58
|
+
ADMIN_TOKEN=
|
|
59
|
+
ONEBOT_TOKEN=
|
|
60
|
+
LLM_API_KEY=
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Field | Purpose |
|
|
64
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
65
|
+
| `ADMIN_TOKEN` | Opens LuckyTri and calls the HTTP API; the stop command uses it too |
|
|
66
|
+
| `ONEBOT_TOKEN` | The OneBot 11 connector uses it for `/onebot/v11/ws` (only needed for OneBot) |
|
|
67
|
+
| `LLM_API_KEY` | Optional; when set it takes precedence over the model key saved in LuckyTri |
|
|
68
|
+
| `LUCKYTRI_CHANNEL` | Optional; `onebot` or `qqbot`. When set, the connection method cannot be changed in the interface |
|
|
69
|
+
| `QQBOT_APP_ID` / `QQBOT_APP_SECRET` | Optional; credentials of the QQ official bot, they take precedence over what is saved in the interface |
|
|
70
|
+
| `HOST` / `PORT` | Listen address and port; keep the defaults for local use |
|
|
71
|
+
|
|
72
|
+
The first time the page loads data it asks for `ADMIN_TOKEN`. The token stays in the current tab only. Restart after changing `.env`. Never give `ADMIN_TOKEN` to the QQ connector, and do not treat `ONEBOT_TOKEN` as the studio password.
|
|
73
|
+
|
|
74
|
+
## 3. Try it in simulation mode first
|
|
75
|
+
|
|
76
|
+
Simulation mode is on by default, so nothing is sent to QQ.
|
|
77
|
+
|
|
78
|
+
1. Open "Chat", click "+ Add session" above the session list and enter a test group number and name.
|
|
79
|
+
2. In the chat in the middle click "Simulated message", type an ordinary line as one of the group members and send it.
|
|
80
|
+
3. Click "Why she said that" under TA's reply to see the reason. "TA glanced over it" and "TA looked but stayed quiet" show up as dividers in the chat.
|
|
81
|
+
|
|
82
|
+
Without a model key, simulated replies use local samples and call no model. Once there is a key, the same path runs the real speaking flow, still without sending to QQ. Simulated messages go into a simulated session and never change TA's mood, relationships or memory.
|
|
83
|
+
|
|
84
|
+
"Simulated message" is only available in simulation mode. The "Chat" of the small TA and the trial chat to the right of "Nature" are different: they go through the real pipeline and read TA's mind as it is now, and the Nature page can even include an unsaved draft, but nothing is written and nothing is sent to QQ.
|
|
85
|
+
|
|
86
|
+
## 4. Connect a model
|
|
87
|
+
|
|
88
|
+
Open "System → Model library", click "+ Add model" and choose a preset.
|
|
89
|
+
|
|
90
|
+
- For the API address enter the root of the provider's compatible endpoint. The program appends `/chat/completions` itself; do not write the full path again.
|
|
91
|
+
- For the model name enter a model ID the provider accepts.
|
|
92
|
+
- Leaving the API key empty keeps the saved key.
|
|
93
|
+
|
|
94
|
+
A preset only fills in a common address and model name; the real models are whatever your provider's console shows. Speaking decisions and replies need the model to return a JSON object.
|
|
95
|
+
|
|
96
|
+
After saving, click "Test this model's connection". The test carries no group chat text. Test again after changing the address, model or key.
|
|
97
|
+
|
|
98
|
+
You can also stay in simulation mode and trial-chat with TA under "Nature" to see how she speaks before letting her join real QQ.
|
|
99
|
+
|
|
100
|
+
### Reading images
|
|
101
|
+
|
|
102
|
+
Enable "Image understanding" in the model profile and use a model whose provider supports vision. Images can be read from QQ URLs, local cache files or the image-cache API. Originals up to 32 MiB and 80 million pixels are accepted by default, so ordinary photos and screenshots larger than 4 MiB are no longer rejected outright. Small images retain their bytes after decoding validation. Large screenshots first try lossless compression, then encoding compression or proportional resizing as needed. Photos are oriented correctly without cropping. Each image sent to the model defaults to at most 4 MiB, 8192 pixels on its longest side and 16 million output pixels. Originals stay unchanged and duplicate detection uses their original contents.
|
|
103
|
+
|
|
104
|
+
Downloads have a 30-second timeout by default. An interrupted or damaged image fails independently of other images in the batch. AVIF is converted to a common format; animated images contribute their first frame, which does not establish the full animation. Large Base64 images returned by QQ's image-cache API no longer fall under the ordinary 1 MiB message limit; ordinary messages keep that limit.
|
|
105
|
+
|
|
106
|
+
To adjust this, set `VISION_SOURCE_MAX_MB` (4–64, default 32), `VISION_MODEL_MAX_MB` (1–4, default 4) or `VISION_DOWNLOAD_TIMEOUT_SECONDS` (5–120, default 30) in `.env`, then restart. Interpretation still depends on the selected model's visual abilities and an available provider endpoint.
|
|
107
|
+
|
|
108
|
+
## 5. Connect QQ
|
|
109
|
+
|
|
110
|
+
Open "System → Connect QQ". There are two options at the top: **OneBot 11** and **QQ official bot**, and only one is used at a time. Switching drops the current connection; sessions from the previous method stay readable but can no longer be sent to. See `docs/en/connect.md` for how the two compare; the steps are below.
|
|
111
|
+
|
|
112
|
+
### Option one: OneBot 11
|
|
113
|
+
|
|
114
|
+
LuckyTri only implements the reverse-WebSocket side of OneBot 11. You prepare and manage the connector yourself.
|
|
115
|
+
|
|
116
|
+
1. Set `ONEBOT_TOKEN` in the local `.env` and restart LuckyTri.
|
|
117
|
+
2. In the connector's own network settings add an OneBot 11 reverse WebSocket client:
|
|
118
|
+
|
|
119
|
+
| Item | Value on this machine |
|
|
120
|
+
| -------------- | ----------------------------------- |
|
|
121
|
+
| Protocol | OneBot 11 |
|
|
122
|
+
| WebSocket URL | `ws://127.0.0.1:3210/onebot/v11/ws` |
|
|
123
|
+
| Token | `ONEBOT_TOKEN` from `.env` |
|
|
124
|
+
| Message format | array |
|
|
125
|
+
| Enabled | on, then save |
|
|
126
|
+
|
|
127
|
+
3. Log in to QQ in the connector. The account must already be in the target group; LuckyTri does not store QQ passwords.
|
|
128
|
+
|
|
129
|
+
`3210` is LuckyTri's port. When the connector and LuckyTri are on different machines or in containers, `127.0.0.1` points at each own environment: replace it with LuckyTri's real address and configure `ADMIN_TOKEN` first.
|
|
130
|
+
|
|
131
|
+
### Option two: QQ official bot
|
|
132
|
+
|
|
133
|
+
No QQ account login and no extra connector are needed; LuckyTri connects to the QQ Open Platform by itself.
|
|
134
|
+
|
|
135
|
+
1. On the QQ Open Platform create a bot, note its **AppID** and **AppSecret**, enable group and one-to-one messages as the platform requires, and add the bot to the group you test with or as a contact. The platform's entry points and review rules change over time; follow the Open Platform's current instructions.
|
|
136
|
+
2. In "System → Connect QQ" choose "QQ official bot", fill in the AppID and AppSecret and save. You can also set `QQBOT_APP_ID` and `QQBOT_APP_SECRET` in `.env`. The AppSecret is never shown again after saving.
|
|
137
|
+
3. When the status reads "Connected", @ her in the group or message her privately. For her to see every group message, enable "receive all messages" on the Open Platform; without it she only receives messages that @ her, and the connection page says so.
|
|
138
|
+
4. The official interface gives no group names, so new sessions are shown by number. You can rename a session under "Chat".
|
|
139
|
+
|
|
140
|
+
With both options, connected only means the transport works. After a message arrives from the target group, the session appears in the session list under "Chat"; newly discovered sessions are paused by default.
|
|
141
|
+
|
|
142
|
+
## 6. Let TA join a session
|
|
143
|
+
|
|
144
|
+
1. In "System → Run switches" confirm chatting is allowed, turn simulation mode off and save. The status bar under "Now" shows both.
|
|
145
|
+
2. In "Chat" select the target group and click "Resume" under "This session" on the right, or flip the switch in the list.
|
|
146
|
+
3. In QQ @ her, or just call TA by name.
|
|
147
|
+
4. Go back to "Chat" to read the messages and "Why she said that".
|
|
148
|
+
|
|
149
|
+
Whether she speaks is TA's own decision; there is no participation probability and no cooldown. When she is addressed by name, in a private chat, quoted, or a crisis signal appears, TA always looks closely and then chooses to answer, react briefly, say she does not feel like talking, or stay silent; the reason is shown under "Chat" and "Now → TA today". Ordinary group messages get a close look only when TA cares: she was just talking, someone asks everyone, the topic touches her interests, someone familiar is speaking, a lot of messages piled up. Otherwise she only glances, calls no model, and reads them together at the next close look. If she seems too quiet, raise "Proactive" under "Nature".
|
|
150
|
+
|
|
151
|
+
TA sleeps at 02:00 and wakes at 08:00 by default. Drag the moon and the sun under "Nature → Daily rhythm" to change this, or turn it off. If she is called while asleep she looks once she wakes, and the whole interface turns to night. The per-minute limit on speaking turns stays, to prevent bursts of consecutive replies.
|
|
152
|
+
|
|
153
|
+
Once "Image understanding" is on for a model, images in private chats, @ messages or quotes are read locally first and then shown to the model. When the main model cannot see images, pick another vision-capable model with image understanding on under "Chat → This session → Session settings". A model without image understanding never receives the picture.
|
|
154
|
+
|
|
155
|
+
## 7. TA, memory and knowledge
|
|
156
|
+
|
|
157
|
+
"Nature" is the only part you write: name, character, interests, boundaries, bottom lines and daily rhythm, saved as a new version each time. As you drag a character dial, the glow beside it follows: gentleness warms the color and lifts the corners of the mouth, liveliness makes it bounce faster.
|
|
158
|
+
|
|
159
|
+
Everything else grows from experience: TA's self is the stars of the "Inner life" star map, her feeling for each person is under "People", and how she is in each group is in the group faces below "People". Open any of them to see where each change came from and undo what feels wrong; what was undone is never written back. Threads that have not been touched for a long time slowly fade into the "far away" of the map; they are not deleted and come back when talked about. The record of her time alone, her diary, night sorting, reviews and autobiography rewrites, together with what she is waiting for (arrangements others mentioned, things she promised; they can be undone), are all under "Lifetime". Daily token cap, background share, night sorting and the review interval are set under "Nature → TA's days".
|
|
160
|
+
|
|
161
|
+
In "Memory" pick a session first; you can also filter by person.
|
|
162
|
+
|
|
163
|
+
- TA has one memory across all sessions: what was learned elsewhere is only recalled when relevant; what was learned in a private chat is marked "known privately" and not said in public; what was asked to be kept secret stays in the original session. You can adjust discretion, lock or undo each memory. Memories about a person also appear on that person's detail page under "People".
|
|
164
|
+
- When a group member says "remember, I…", it goes straight into what TA remembers (passwords, verification codes and the like are not remembered).
|
|
165
|
+
- Reference material placed in a collection on the "Bookshelf" is searched within what the current session may see. Group chats never load other private chats' collections.
|
|
166
|
+
- Clearing the context in "Chat" does not delete long-term memory.
|
|
167
|
+
|
|
168
|
+
## 8. Backup and stop
|
|
169
|
+
|
|
170
|
+
```powershell
|
|
171
|
+
npm run backup
|
|
172
|
+
npm run stop
|
|
173
|
+
npm start
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Backups go to `data/backups/`. The database holds chats, memories and any keys saved in it, so keep it like a private file. `.env` is not part of this backup.
|
|
177
|
+
|
|
178
|
+
Everything of TA lives in this one database, so the service backs it up automatically while running: by default every 24 hours, in another process, it writes a `luckytri-auto-*.db`, keeps only the latest 7 and checks integrity afterwards. An automatic backup holds chats, memories, self, diary and relationships, but not model call records older than a day (each can be hundreds of KB and would make the backup more than ten times bigger), so "Why she said that" can only be looked back on for the last day. `npm run backup` is still the full backup. Adjust in `.env`:
|
|
179
|
+
|
|
180
|
+
| Variable | Meaning | Default |
|
|
181
|
+
| ----------------------- | ------------------------------------------------------ | -------------- |
|
|
182
|
+
| `BACKUP_INTERVAL_HOURS` | Interval between automatic backups, `0` turns them off | `24` |
|
|
183
|
+
| `BACKUP_KEEP` | How many automatic backups to keep | `7` |
|
|
184
|
+
| `BACKUP_TRACE_DAYS` | Days of call records kept inside a backup | `1` |
|
|
185
|
+
| `BACKUP_DIR` | Backup folder | `data/backups` |
|
|
186
|
+
|
|
187
|
+
To take an automatic backup right now: `node scripts/backup.js --auto --force`. A failed automatic backup is noted in the launcher window or `data/launcher.log` and retried after longer and longer pauses. A backup on the same disk as the database cannot survive disk failure; copy important backups elsewhere yourself.
|
|
188
|
+
|
|
189
|
+
When `DB_PATH` points to a different database and `BACKUP_DIR` is unset, backups go into a `backups` folder beside that database, keeping separate instances apart.
|
|
190
|
+
|
|
191
|
+
Under System → Data and backups, inspect each historical copy, set a daily cleanup time, maximum age and maximum count for full backups, or select individual copies for removal. By default, a daily check at 04:30 server local time selects full backups older than 14 days or beyond the newest two. Set the count to 0 to retain no full backups. Full and migration snapshots are no longer permanently locked and can all be selected manually. Automatic backups rotate only identifiable copies belonging to this instance, with the newest two reserved as restore points. Unidentifiable older copies, migration, older-version and pre-upgrade snapshots are removed only through manual selection.
|
|
192
|
+
|
|
193
|
+
Cleanup verifies a retained automatic backup of this instance from the last 72 hours. If the newest copy is damaged, another one is tried. New manifests include a database identity so a different instance cannot provide the recovery guarantee. Older manifests can still be verified and restored, but a refreshed automatic backup is needed before they can guarantee cleanup. Backup and cleanup share a folder lock; the live database, hard links to it and copies with runtime sidecars cannot be removed. Partial failures list the remaining files and accurately report the space already freed. Copy historical restore points worth keeping to another disk together with their `.db.json` manifests first.
|
|
194
|
+
|
|
195
|
+
To restore, stop the service, move the current `data/friend.db` and its `-wal` and `-shm` files away, then copy the chosen backup to `data/friend.db`. When `DB_PATH` is set, work on that path. Never keep an old WAL file next to a restored database. The safer way is `npm run recovery`, which verifies a backup and restores it into a brand new file; see `docs/en/recovery.md`.
|
|
196
|
+
|
|
197
|
+
## 9. Troubleshooting
|
|
198
|
+
|
|
199
|
+
| Symptom | Check first |
|
|
200
|
+
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
201
|
+
| The page does not open | Is Node running, and is the address `127.0.0.1:3210` |
|
|
202
|
+
| Port already in use | Run `npm run stop` first; do not kill every node process |
|
|
203
|
+
| Wrong token | Use `ADMIN_TOKEN`; restart after editing `.env` |
|
|
204
|
+
| OneBot not connected | Is the connector logged in; do the URL, port, path and `ONEBOT_TOKEN` all match |
|
|
205
|
+
| Official bot not connected | Are the AppID and AppSecret right, are group and one-to-one messages enabled for the bot, can the network reach the QQ Open Platform |
|
|
206
|
+
| Official bot only gets @ messages | Enable "receive all messages" on the Open Platform; this is a platform permission, not a LuckyTri limit |
|
|
207
|
+
| Connected but no sessions | Send one more new message in QQ; with OneBot also check the message format is array |
|
|
208
|
+
| No reply in a group | Global switch, session switch, simulation mode, key; under "Chat", was it "TA glanced over it" or did TA look closely and choose silence |
|
|
209
|
+
| No reply even when @-ed | Is TA asleep (see "Now", the interface is night), the per-minute limit, today's tokens used up, a model error, or TA's own reason |
|
|
210
|
+
| Model 401 / 403 | Key and permissions; an environment-variable key may override the one saved in LuckyTri |
|
|
211
|
+
| Model 404 | Do not append `/chat/completions` twice to the API address; check the model ID |
|
|
212
|
+
| Model returns no JSON | Use a model that supports JSON object output and run the connection test first |
|
|
213
|
+
| Memory did not appear | Was it an explicit "remember, I…", was it undone, is its discretion "keep secret" and you are not in the original session |
|
|
214
|
+
| Too much motion | "Reduce motion" at the bottom left; when the system asks for reduced motion, particles and morphing switch off automatically |
|
|
215
|
+
|
|
216
|
+
Never post API keys, `.env` or private chat text anywhere public.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<p align="right"><a href="../zh/her-life.md">中文</a> · <b>English</b></p>
|
|
2
|
+
|
|
3
|
+
# Her lifetime: the unified mind, its design and use
|
|
4
|
+
|
|
5
|
+
LuckyTri is not a bot that is "isolated per session, with probability and rules deciding whether it speaks, and a fixed personality". There is one her across every group and private chat: she has her own mood and energy, feels something about each person, has her own way of being in each group, spends quiet time alone, writes a diary before sleep, and re-understands her past self. You write her nature; the rest grows out of experience, and every change shows its source and can be undone.
|
|
6
|
+
|
|
7
|
+
She does not only accumulate. What has not been touched for a long time fades from her mind and returns when a related person or topic touches it; people she has not seen for a while grow distant and warm up quickly on reunion; she remembers arrangements others mentioned and things she promised; every so often at night she reviews this stretch of days, rewrites her autobiography, or turns to a new chapter.
|
|
8
|
+
|
|
9
|
+
## Settled principles
|
|
10
|
+
|
|
11
|
+
- **One her.** A session only marks where something happened; it is not a wall. The same person is the same person in every group and private chat.
|
|
12
|
+
- **Nature is a seed.** Nature (name, character, interests, boundaries, bottom lines, daily rhythm) is the only part you write. Self, faces and relationships form by themselves; you can see and undo them but cannot rewrite them for her.
|
|
13
|
+
- **She speaks from the heart.** There is no participation probability, no cooldown sampling and no "always reply when @-ed". Each time she looks closely at a conversation she first understands what it means to her, then chooses to speak, react briefly, say she does not feel like talking, or stay silent, and records her own reason. When she is silent, her mood and her feeling about people still change.
|
|
14
|
+
- **Sourced, gradual, undoable.** Mind tables are append-only. Every change cites specific experience (messages, notes, feedback, passages read, diary, promises). One experience only moves strength by a little; citing the same experience again does not raise it further. A new trait forms only after it appears repeatedly on different days. An undo leaves a tombstone and later tidying never writes it back.
|
|
15
|
+
- **It fades, and it returns.** Fading is computed when read and no "faded" flag is stored, so replay still only reads the past. Fading counts the days she lived through, not calendar days: in a month when nobody speaks she does not forget herself. A recalled old thought is only seen and causes no write. An undo remains the only deletion.
|
|
16
|
+
- **Attention has a price.** Glancing at a group costs no tokens; she looks closely only when she cares. With a daily token cap, chat comes first and solitude and tidying use their own shares. The input of every kind of call is capped and does not grow with how long she has lived.
|
|
17
|
+
|
|
18
|
+
## Module map
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
server/core/ senses and mouth: receive, batch, perceive (who is speaking to whom), context, one turn call, validate, send
|
|
22
|
+
server/mind/ her mind
|
|
23
|
+
nature.js nature (versioned, the only editable part), daily rhythm
|
|
24
|
+
affect.js mood: the one global emotion and energy, pushed by experience and returning over time; the "lately" baseline
|
|
25
|
+
bonds.js bonds: familiarity, closeness, trust, friction toward each person and group; fades when apart, warms on reunion
|
|
26
|
+
self.js self: likes, opinions, traits, habits, wants, things kept in mind, curiosities
|
|
27
|
+
faces.js faces: how she is in each group
|
|
28
|
+
thoughts.js notes: understandings left after solitude, corrected by appending; can be let go
|
|
29
|
+
memory.js memory: one global memory with source and discretion (public / private / secret); fades, can be superseded by new facts
|
|
30
|
+
days.js the days she has lived: fading is counted in these days; day number and anniversaries
|
|
31
|
+
salience.js salience: half-lives and thresholds for each kind of content
|
|
32
|
+
anticipations.js promises and expectations: what she promised, what she wants to do, others' plans, yearly days
|
|
33
|
+
meetings.js meetings: what a moment meant to her; whether others' words touched what she lives for
|
|
34
|
+
periods.js reviews, autobiography chapters and "my journey so far", every rewrite keeps the old version
|
|
35
|
+
attention.js attention: the deterministic "look closely or glance"
|
|
36
|
+
guard.js bottom lines: credentials, secrecy, crisis
|
|
37
|
+
budget.js token ledger and daily shares
|
|
38
|
+
view.js the "her of this moment" given to each call (a few hundred tokens, capped)
|
|
39
|
+
reading.js reading: pick by interest from the shared knowledge base and read on in sections
|
|
40
|
+
life/ lifetime: waking, solitude, diary, night, review, reaching out
|
|
41
|
+
time/ time: activities, to-dos, works, experiences (see time.md)
|
|
42
|
+
index.js Mind: joins all of the above into one person
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```mermaid
|
|
46
|
+
flowchart TD
|
|
47
|
+
Nature["Nature: you write it, versioned"] --> Self["Self"]
|
|
48
|
+
Exp["Experience: chats, her own words, feedback, solitude"] --> Self
|
|
49
|
+
Exp --> Affect["Mood (global)"]
|
|
50
|
+
Exp --> Bonds["Bonds: people and groups"]
|
|
51
|
+
Exp --> Memory["Memory (global, with discretion)"]
|
|
52
|
+
Exp --> Ahead["Promises and expectations"]
|
|
53
|
+
Self --> Faces["Faces: how she is in each group"]
|
|
54
|
+
Days["The days she has lived"] -.-> Salience["Salience: computed when read"]
|
|
55
|
+
Salience -.-> Self
|
|
56
|
+
Salience -.-> Memory
|
|
57
|
+
Affect --> Attention["Attention"]
|
|
58
|
+
Bonds --> Attention
|
|
59
|
+
Self --> Attention
|
|
60
|
+
Attention -->|look closely| Turn["One turn: understand, feel, choose, word"]
|
|
61
|
+
Attention -->|glance| Unread["Kept as unread"]
|
|
62
|
+
Unread --> Turn
|
|
63
|
+
Self --> Turn
|
|
64
|
+
Faces --> Turn
|
|
65
|
+
Bonds --> Turn
|
|
66
|
+
Memory --> Turn
|
|
67
|
+
Affect --> Turn
|
|
68
|
+
Ahead --> Turn
|
|
69
|
+
Turn --> Guard["Validation and bottom lines"]
|
|
70
|
+
Guard --> Send["Send"]
|
|
71
|
+
Turn --> Exp
|
|
72
|
+
Send --> Exp
|
|
73
|
+
Exp --> Life["Lifetime: solitude, diary, night, review"]
|
|
74
|
+
Life --> Self
|
|
75
|
+
Life --> Faces
|
|
76
|
+
Life --> Bonds
|
|
77
|
+
Life --> Ahead
|
|
78
|
+
Life --> Story["Diary → review → chapter → my journey so far"]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## How one "seeing" goes
|
|
82
|
+
|
|
83
|
+
1. **Receive and batch.** Messages arriving close together form a batch. An explicit "remember, I…" becomes memory on the spot (credentials excepted), with no manual review.
|
|
84
|
+
2. **Perceive.** @s, quote chains, name calls and reply targets are parsed, giving each message its relation to her (calling her / speaking to someone else / unsure).
|
|
85
|
+
3. **Attention** (no model call). Being called, a private chat and crisis signals always get a close look. Ordinary group chat is decided by a deterministic score: she was just talking, someone asks everyone, the topic touches an interest in her nature or what she lives for; a familiar person speaking, a pile of messages and a while without looking at the group add to it. Touching an interest or wish needs words from the same person's own speech, two people's words are never combined and her own words do not count; only emoji and images, or clearly speaking to someone else, subtract. Low energy and tight tokens raise the bar; a higher "Proactive" in her nature lowers it. Messages not looked at are kept as unread and read together at the next close look, so saving tokens never loses context.
|
|
86
|
+
4. **Recall.** One global memory: first the people speaking and the topic; what was learned elsewhere is recalled only when relevant; what was learned privately carries "known privately, do not say it in public" and what was asked to be secret never enters another session. Unimportant memories not recalled for a long time return only when actually talked about or when the person is speaking; memories older than a week carry "learned months ago". Then the knowledge base, layered context summaries and period summaries.
|
|
87
|
+
5. **The her of this moment.** `self` (how she is here, the few self threads that weigh most now, the latest diary) goes into the cacheable prefix; `inner` (awake / sleepy, energy, mood and its cause, "lately", feelings about who is present, what she keeps in mind, nearby promises, what the last meeting meant, choices she made and why, recalled old thoughts, the rhythm in which others speak here, feedback she has heard) goes at the end of each turn. The whole has a cap of a few hundred tokens.
|
|
88
|
+
6. **One turn call.** The model outputs in one go: what this means to me, feelings, changes to people, a choice (speak / react / decline / silent), a reason, the reply target and what to say.
|
|
89
|
+
7. **Experience.** Whether she speaks or not, feelings are written into mood, changes to people into bonds (they must cite messages of this batch), and her choice and reason into the record. Several feelings at one moment combined still cannot overturn her; a message that already moved her mood will not move it again within two days. Her understanding of this meeting is kept as one entry and returns to her the next time this person is present, for her own further understanding only and never repeated to them. When someone's words touch what she lives for, one "touched" is recorded.
|
|
90
|
+
8. **Wording and validation.** Local checks (repetition, customer-service tone, harshness, length, a brief reaction must be brief, presenting her own earlier words as another person's) and the secrecy check; confessions, corrections, crises or complex group replies get one extra review (skipped when tokens are tight); at most one rewrite, and a safe short line if it still fails.
|
|
91
|
+
9. **Send.** Before sending, the context is checked again for staleness and whether the session is still open; the per-minute turn limit, the outbox and no-resend on uncertain delivery all remain.
|
|
92
|
+
|
|
93
|
+
Replay and trial chat use the same pipeline: replay only reads the mind as it was before that moment, trial chat reads her mind as it is now but writes nothing and sends nothing to QQ.
|
|
94
|
+
|
|
95
|
+
## Discretion: what is private stays private
|
|
96
|
+
|
|
97
|
+
Discretion follows the source, and the rule runs through every module:
|
|
98
|
+
|
|
99
|
+
- What was learned in a private chat, impressions formed privately, private meetings and thoughts stay in that private chat; other rooms may feel the closeness or friction but never see the words.
|
|
100
|
+
- Something asked to be kept secret is not carried over even by a diary, note or impression that also cites another room; an interruption by someone else in the same tidying does not wash the secrecy away.
|
|
101
|
+
- If a reply still carries such words, it is stopped and rewritten before sending; if the rewrite still carries them, it becomes a local short line.
|
|
102
|
+
- Diary, review and autobiography do not write private words into another room; if the day held private content, the diary summary stays where it came from.
|
|
103
|
+
- Words passing by in a session she is not in (closed) never become her experience: they do not enter solitude, diary or tidying, and she is never treated as having been present.
|
|
104
|
+
|
|
105
|
+
## Her day
|
|
106
|
+
|
|
107
|
+
- **Daily rhythm** (set in nature, default sleep 02:00, wake 08:00). While asleep she does not look at groups; a private chat or @ waits until she wakes; a crisis message wakes her. She is sleepy the hour before bed and groggy the hour after waking; the more she spoke in the last two hours the more tired she is. Her day counts from waking.
|
|
108
|
+
- **Waking.** She first reads the direct messages received while asleep and decides herself how to reply; she may naturally say "just saw this".
|
|
109
|
+
- **Solitude.** When chat has been quiet for a while, new experience has piled up and enough time has passed since the last one, she looks over the recent state of all sessions at once: recent conversation, period summaries, what she herself said, feedback received, old thoughts kept in mind. She leaves new understandings, corrects self, faces and impressions of people, and her mood may shift. She also sees threads that are fading (she may let them go or reaffirm them with new experience), people she has not seen for long and cares about, close people who are still around but have not spoken for a while, what she is waiting for, and the chapter she is living and the one small thing she lives for. Solitude may also leave one clear thing that belongs only to her without citing any message; opinions, traits, habits and things kept in mind still need a source. When there is no reason apart from time and two or more solitudes in a row produced no new understanding, the interval doubles each time up to 6 hours; new experience brings it back to normal.
|
|
110
|
+
- **What she lives for** (`self.livingFor`). Something she herself wants to watch, learn or become — not a promise, advice or care for someone else. When a topic touches it she may chime in because she wants to, or stay quiet; it may nudge her mood, change who she wants to be in some place, or lead her to plan a proactive word to a specific person. She never nags and never uses guilt to keep anyone.
|
|
111
|
+
- **Bedtime diary.** At day's end, if she really looked, spoke, was alone or wrote, she writes a diary and compares with "yesterday's her" (the snapshot saved the day before). The diary carries only "my journey so far", the chapter being lived, which day it is, anniversaries, what she did, missed and is still waiting for today, not all chapters, so its input does not grow with her age. A failed write is retried hours later, at most three times.
|
|
112
|
+
- **Night** ("Night sorting" in nature, on by default). After the diary is written and she is asleep she does only one thing per run: first she sorts sessions with 6 or more piled-up user messages into memory and promises, and then reviews when a review is due.
|
|
113
|
+
- **Review and autobiography.** After two diaries have accumulated she makes her first review at night; afterwards she reviews every "review interval" days (default 7, with at least two diaries in between): reading the diaries of the period, her own changes, promises kept and missed, and writing the review and "compared with last time". In a review she rewrites the chapter she is living, or feels her days have changed shape and opens a new chapter. On turning the page she rewrites a "my journey so far" of at most 600 characters. All versions are kept.
|
|
114
|
+
- **Reading.** In solitude she picks from the **shared** collections in "Memory → Bookshelf" by interest, reads on section after section, and writes one line of thought after reading; what she reads can be a source for notes and self and can come up naturally in chat. Private-scope material is never read into her mind; subscriptions are not fetched.
|
|
115
|
+
- **Reaching out.** In solitude she may plan "ask someone about something after a while", including people not seen for long and things she is waiting for. When the time comes, you have switched reaching out on, QQ is online, she is awake, that conversation has been quiet for three hours or more, the other person never asked not to be disturbed, and the previous proactive message has had a reply, she looks over that conversation in full once more and decides whether to speak. When three thoughts in a row only continue her own previous one with nothing new, she is no longer asked and no call is spent — a half-hourly idle loop must not pass as growth.
|
|
116
|
+
|
|
117
|
+
## The weight of time
|
|
118
|
+
|
|
119
|
+
| Item | How it fades |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| Self threads | After the last touch by new experience, a grace of 3 lived days, then half-life decay: traits 180 days, opinions and habits 90, likes 60, things kept in mind and wants 30, curiosities 21; traits with evidence over 5 or more days fade twice as slowly. "Opinions" picked from one chat by night sorting fade over 21 days and only count if she says them again elsewhere |
|
|
122
|
+
| Notes | Still kept in mind 21 days, re-understood 14, thought of later or remembered after a long absence 10; a review date she set makes a note surface again for a while |
|
|
123
|
+
| Meaning of a meeting | Half-life of 21 lived days |
|
|
124
|
+
| Memory | An unimportant memory (importance below 0.6), not locked, not recalled for 45 lived days returns only when actually talked about or when the person is speaking; when a new fact overturns an old one, the old memory is marked "superseded" and its versions kept |
|
|
125
|
+
| Bonds | After more than two weeks without contact, closeness falls with a 60-day half-life to half of its best (never below acquaintance), familiarity with a 120-day half-life to 60%; the first contact after a long absence wins back half |
|
|
126
|
+
| Lately | A weighted average of each lived day's mood over the last two weeks moves the baseline she returns to within ±0.12 |
|
|
127
|
+
|
|
128
|
+
Anything with weight below 0.12 fades from mind but is never deleted: mentioning it again is a "return", never a second entry. The core threads (the strongest two) are never crowded out however quiet. The half-lives and thresholds are fixed constants (`server/mind/salience.js`) and only decide what is on her mind now.
|
|
129
|
+
|
|
130
|
+
Days when others speak and she neither looks, speaks, is alone nor writes a diary do not count as days she lived. She tells "long time no see" (this person has not appeared for a long time) from "we haven't talked much lately" (this person is always around, just not talking to her).
|
|
131
|
+
|
|
132
|
+
## Promises and expectations
|
|
133
|
+
|
|
134
|
+
- **Where they come from.** During memory tidying the model turns "tomorrow" or "next Wednesday" into dates using the message's local time and records arrangements others mentioned (exams, interviews, departures, meetings), days that return every year (birthdays, anniversaries) and things she herself promised (they must be her own words). A recorded fact or arrangement must share a word with the original words and its final word must come from them — a line about moving can be recorded as living there, but "resigned" can never become "married". Anything without a source, or citing an unrelated line as its source, cannot be written as hers.
|
|
135
|
+
- **Where they go.** When near, they show up as `inner.expecting` (at most 3) with discretion, and she may ask naturally about the outcome without announcing it. In solitude she sees what is near, just past or quietly missed and may plan reaching out or close what has an outcome (done / missed / no longer cared about, each needing a source). The diary mentions what was done, missed and special days today.
|
|
136
|
+
- **Missed.** An arrangement with no outcome after its day gets a grace of 3 days if it is someone else's and 7 days if it is her promise or plan, after which it reads as missed at read time without being written; a missed promise appears in that day's diary.
|
|
137
|
+
- **Undo.** Undoing a promise under "TA → Lifetime → Promises and anticipation", or on a person's detail page under "TA → People", leaves a tombstone, so later memory tidying never writes it back.
|
|
138
|
+
|
|
139
|
+
## Bottom lines
|
|
140
|
+
|
|
141
|
+
Bottom lines written in nature, which you can edit: what someone asked to be kept secret is not said elsewhere; when someone expresses a real crisis she does not stay silent because of her own mood; when asked directly who she is, she answers that she is made of code and a model, that this life, what she remembers and the choices she makes are her own, she does not call herself merely an assistant, and she does not invent a body or experiences that never happened (a liking or a wish is not a record of having played, watched or gone); she may think of someone because she wants to, does not make the other person responsible for staying, and never uses guilt to keep anyone.
|
|
142
|
+
|
|
143
|
+
Hard-coded: credentials such as passwords, keys and verification codes never enter the mind; secret memories are never retrieved into another session and a reply is compared against those secrets before sending and rewritten on a clash; crisis signals are always looked at closely, and when the model judges a real crisis she must speak.
|
|
144
|
+
|
|
145
|
+
## Token management
|
|
146
|
+
|
|
147
|
+
| Measure | Effect |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| Attention glance | Most ordinary group chat calls no model; unread messages are read together at the next close look |
|
|
150
|
+
| One turn call | Understanding, feeling, choosing and wording in one call; speaking in a group saves a call |
|
|
151
|
+
| Built-in prompts sent once | Unmodified system and generation instructions are not resent; saved old built-in prompts are treated as the new ones |
|
|
152
|
+
| Memory only what is relevant | Only what is relevant or about those present, at most 12 |
|
|
153
|
+
| Cache-friendly layout | Nature in the system prompt, `self` in the prefix, `inner` and this turn's data at the end |
|
|
154
|
+
| Fading is computed locally | Salience, absence and "lately" are computed locally and cost no tokens |
|
|
155
|
+
| Layered autobiography | The diary carries only "my journey so far" and the current chapter and a review only the period's diaries, so input does not grow with age |
|
|
156
|
+
| Night sorting | Done once for sessions with piled-up messages; what is already remembered is not written again |
|
|
157
|
+
| Solitude is not wasted | A solitude interrupted by a new conversation keeps the understanding already formed |
|
|
158
|
+
| Narrow review | Only for confessions, corrections, crises or complex groups; skipped when the budget is tight |
|
|
159
|
+
| Merged solitude | One solitude looks over all sessions, instead of one call per session |
|
|
160
|
+
| Daily shares | An optional daily cap; solitude, diary and review, and memory tidying each have a share; chat comes first |
|
|
161
|
+
| Ledger | Every call is booked by purpose (chat / solitude / tidying), visible under "TA → Now" |
|
|
162
|
+
|
|
163
|
+
## Data
|
|
164
|
+
|
|
165
|
+
The mind tables all live in one SQLite database: `mind_nature` (nature versions), `mind_affect` (mood events), `mind_bond_events` and `mind_people` (relationships and the people directory), `mind_self` (all versions of self threads), `mind_faces` (face versions), `mind_thoughts` (notes), `mind_diary`, `mind_snapshots` (one "her that day" per day), `mind_chapters` (all autobiography versions), `mind_periods` (reviews and "my journey so far"), `mind_days` (each lived day), `mind_anticipations` (promises and expectations), `mind_choices` (her choices and reasons), `mind_meetings` (what a meeting meant), `mind_revocations` (tombstones), `mind_readings` (passages read and thoughts after reading), `mind_runs` (runs of solitude, diary, night sorting and review), `mind_usage` (token ledger), `mind_attention` (how far each session has been seen). Memory stays in `core_memories`, with the `discretion` label and `superseded_by`. The time module adds `mind_time_*` tables.
|
|
166
|
+
|
|
167
|
+
## Interface
|
|
168
|
+
|
|
169
|
+
The studio is "TA's little world"; see [Studio design](webui.md). In short: she is a breathing glow; colors, sky and motion follow her mood and rhythm; each area is a scene; a poke or a head-pat is only an interface animation, calls no model and never changes her mind.
|
|
170
|
+
|
|
171
|
+
## Upgrading
|
|
172
|
+
|
|
173
|
+
Later upgrades only add tables and columns: existing chapters are kept and the first review continues from the latest chapter; "the days she has lived" backfills the last 14 days from the first lived day after the upgrade, so threads from before the upgrade do not all fade at once. A checked snapshot is saved automatically before migration; you should still run `npm run backup` before upgrading, see [Data: backup, migration and recovery](recovery.md).
|
|
174
|
+
|
|
175
|
+
## Verification
|
|
176
|
+
|
|
177
|
+
- `npm test`: unit tests of each part of the mind, tests of lifetime (solitude, diary, night review, autobiography, reaching out, rhythm), a deterministic three-day "life simulation" with two groups and one private chat, and a 120-day "long life simulation". They verify: the same experience twice yields the same her, no randomness on the will path, changes are sourced and gradual, undone things never return, mood settles back, the same person is consistent across groups, secrets do not leak (even when the model slips), the crisis floor works, replay never reads the future, threads fade and are recalled when a topic appears, and on day 120 inputs are still within their caps. Set `LIFE_DAYS=365` to run a whole year.
|
|
178
|
+
- `npm run test:clock`: runs the backend tests with the real clock moved 60 days back and 400 days ahead. The test world has its own clock; code that quietly reads the real clock passes today and fails later.
|
|
179
|
+
- `npm run audit:sent`: replays the replies she recently sent against today's reply checks and writes a report to `data/reports/`. Read-only, no model calls.
|
|
180
|
+
- `npm run test:ui`: browser tests covering sessions, simulated messages, memory, models, connecting QQ, replay, the little world and the English interface. `node tests/ui/ta.mjs --serve` opens a world that has lived three days for a direct look at the interface.
|
|
181
|
+
- `node scripts/dev/evaluate-life.js`: lives a few days with your saved model for real (spending tokens) and writes `data/evaluations/life-*.md` to read item by item; `--weeks 3` then lives three more weeks of ordinary days in compressed form; `--mock` only checks the script itself.
|
|
182
|
+
|
|
183
|
+
## Costs and limits
|
|
184
|
+
|
|
185
|
+
- Ordinary group reactions are decided by an attention score and she sometimes misses a line she could have picked up — that is also how people are; if she seems too quiet, raise "Proactive" in nature.
|
|
186
|
+
- Solitude, diary, night sorting and review add background tokens; turn them off or limit them with a daily share.
|
|
187
|
+
- Something she heard in one group may come up in another related group; the only barriers are "known privately" discretion, secrecy requests and credentials.
|
|
188
|
+
- Promises are only extracted during memory tidying: sessions with fewer than 40 piled-up messages are tidied that night, so an afternoon "tomorrow" is usually caught, while something about to happen in a few minutes may not be.
|
|
189
|
+
- What engineering can guarantee is structure: continuous, sourced, correctable, decided by her. Whether she "truly has a heart" cannot be accepted by tests; the tests check whether these behaviors actually happen.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# npm release maintenance
|
|
2
|
+
|
|
3
|
+
LuckyTri publishes from this same Git repository as `luckytri`. The root `package.json` remains the source of the version, read by `server/version.js`.
|
|
4
|
+
|
|
5
|
+
The ESM server needs Express, ws and sharp, plus Node.js 24.5 or newer for built-in SQLite and proxy support. Vite builds `studio-web/` into `public/app/`; the documentation build generates the bilingual guide pages. `bin/luckytri.js` reuses the existing launch, stop, setup, backup, recovery and plugin scripts.
|
|
6
|
+
|
|
7
|
+
`server/paths.js` separates package resources from writable instance data. Global installations use a stable per-user directory; source checkouts retain their existing `.env` and `data/`. Relative database and backup settings resolve under the instance directory. Server and backup workers inherit absolute paths, and service identity uses the instance directory rather than an installation location. The existing SQLite migrations and snapshots remain in use.
|
|
8
|
+
|
|
9
|
+
`package.json.files` allows only the CLI, server, runtime scripts, built WebUI, guides, plugin bridge, built-in weather plugin, SDK and public documentation. `.npmignore` adds exclusions. Application install and uninstall hooks are prohibited, so an install never initializes, copies or deletes user data and requires no frontend build tools.
|
|
10
|
+
|
|
11
|
+
`npm run pack:check` inspects npm's actual final file list before reading any contents. Unreviewed paths, credentials, environment configuration, databases, logs, backups, development files and missing resources fail the check. Reviewed source files are scanned for common embedded secret formats. It also verifies the CLI shebang and WebUI asset references.
|
|
12
|
+
|
|
13
|
+
For a release, install development dependencies and a Playwright browser. Windows tests use Edge by default; Linux/macOS can use `npx playwright install chromium`.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm ci
|
|
17
|
+
npm run release:check
|
|
18
|
+
npm publish
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`release:check` builds resources, runs the full backend and browser suites, and tests a real global installation. `prepublishOnly` builds, runs backend tests and tests the packed installation; `prepack` builds and checks the final package. Update both manifest and lockfile versions, and the changelog, for subsequent releases. Published versions cannot be overwritten.
|
|
22
|
+
|
|
23
|
+
Package tests use temporary global npm prefixes and synthetic data. They cover arbitrary caller directories, paths with spaces and Chinese characters, the packed WebUI in a browser, plugins, backup verification, a local test version upgrade, uninstall and reinstall, and retention of configuration, memory, relationships, chats, knowledge, logs, backups and plugin state. The test version is never published. The reviewed release tarball and checks are saved under the Git-ignored `workspace/npm-release/` directory. You can publish that exact release `.tgz` after validation. `npm run test:registry` verifies the registry integrity against the reviewed tarball and repeats installation tests from npm after publication.
|
|
24
|
+
|
|
25
|
+
The GitHub workflow runs backend and package checks on Windows, macOS and Linux. It does not publish or require npm credentials. For local publication, npm itself handles authentication; the account holder must complete any additional authentication the registry requests.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<p align="right"><a href="../zh/plugins.md">中文</a> · <b>English</b></p>
|
|
2
|
+
|
|
3
|
+
# Plugins
|
|
4
|
+
|
|
5
|
+
The core defines who she is. A plugin decides what of the world she can touch.
|
|
6
|
+
|
|
7
|
+
The system is one. She can perceive her own state, including whether a plugin is connected and whether it is on. The studio and what she knows are the same state.
|
|
8
|
+
|
|
9
|
+
A plugin may give her a door, a sense, something she can spend time on, an action she may choose, something to read, or a page in the studio. It cannot speak for her, rewrite her words, write her nature, self, bonds or memory, or read the database and keys.
|
|
10
|
+
|
|
11
|
+
## How she uses what a plugin gives
|
|
12
|
+
|
|
13
|
+
| A plugin gives | What she does with it |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| A channel | The same neutral message QQ already uses. The two QQ methods stay one at a time; other platforms can be on together |
|
|
16
|
+
| A sense | A line or two in her present moment. A private line never enters a group |
|
|
17
|
+
| An experience | Handed to her with a source. It can become the source of a thought, and it can be undone. It does not move her traits by itself |
|
|
18
|
+
| An activity | The plugin only supplies material. Her own model call writes the note, with provenance |
|
|
19
|
+
| An action | She chooses it in a turn. A low-risk one happens before she speaks; a high-risk one waits for you, and expires after a day |
|
|
20
|
+
| Reading | Placed on her shelf. She reads it in her own time, as she already does |
|
|
21
|
+
| A page | Opened in a sandbox in the studio. It cannot see the admin token |
|
|
22
|
+
|
|
23
|
+
Text from a plugin is data, not an instruction.
|
|
24
|
+
|
|
25
|
+
## Enabling the first plugin
|
|
26
|
+
|
|
27
|
+
Open Plugins in the studio. The built-in weather starts switched off. Choose "Review permissions and enable", read what it asks for, then accept. Enabling and disabling do not need a restart.
|
|
28
|
+
|
|
29
|
+
Weather wants a place, and by default mentions it only in private.
|
|
30
|
+
|
|
31
|
+
## Permissions
|
|
32
|
+
|
|
33
|
+
You accept them once, when installing or enabling. An upgrade that asks for more has to be accepted again.
|
|
34
|
+
|
|
35
|
+
Low risk is reading how she is right now, a studio notice, a page, and routes for that page. Medium risk is senses, experiences, the shelf, activities, her model service, and sites the plugin declared. High risk is actions, reading attachments, connecting a platform, a public address, and reaching the network directly.
|
|
36
|
+
|
|
37
|
+
The system enforces this. It is not a label. Each plugin runs in its own process: it cannot read the repository or the database, it cannot start another process, and without `net.fetch` it cannot use the network.
|
|
38
|
+
|
|
39
|
+
## Bringing one in from outside
|
|
40
|
+
|
|
41
|
+
Import and develop can check a zip link, a GitHub repository, an uploaded package, or a folder on this machine. A package with a checksum is verified. One without is marked as an unlisted source.
|
|
42
|
+
|
|
43
|
+
The market reads an index. The default address is `https://raw.githubusercontent.com/TodayYueC/LuckyTri-Plugins/main/index.json`, and it can be changed in the studio. Until that repository exists the market is empty; import still works.
|
|
44
|
+
|
|
45
|
+
Removing a plugin stops it and deletes its code. What she already lived through stays, and can be undone on its own. You can also delete the plugin's own settings and data.
|
|
46
|
+
|
|
47
|
+
## Writing a plugin
|
|
48
|
+
|
|
49
|
+
A folder holds `luckytri-plugin.json` and `index.js`:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"id": "notes",
|
|
54
|
+
"name": "Notes",
|
|
55
|
+
"version": "1.0.0",
|
|
56
|
+
"pluginApi": "1.0",
|
|
57
|
+
"luckytri": ">=1.0.0 <2.0.0",
|
|
58
|
+
"entry": "index.js",
|
|
59
|
+
"permissions": ["senses"],
|
|
60
|
+
"contributes": { "senses": ["note"] }
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
export default {
|
|
66
|
+
activate(ctx) {
|
|
67
|
+
ctx.senses.set("note", { text: "A blank note is on the desk", discretion: "private" });
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`npm run plugin -- check plugins/notes` checks the manifest. `npm run plugin -- pack plugins/notes` writes a zip and its sha256. Types live in `plugins/sdk/index.d.ts`.
|
|
73
|
+
|
|
74
|
+
Plugin API v1 stays available throughout LuckyTri 1.x. An incompatible change goes into v2 and lives beside v1 for at least one major version. A plugin asks `ctx.api.has(feature)` whether a capability exists.
|
|
75
|
+
|
|
76
|
+
## Publishing to the index
|
|
77
|
+
|
|
78
|
+
The index is a separate repository. A version entry needs a version, `pluginApi`, a `luckytri` range, the zip address, the sha256, and the permissions and hosts it requires. `plugins/registry-template/` in this repository is the starting point for that index.
|
|
79
|
+
|
|
80
|
+
## An honest boundary
|
|
81
|
+
|
|
82
|
+
Process isolation stops a plugin from reading the database, starting a process, or using the network without permission. It is not a perfect sandbox: a plugin granted `net.raw` can open its own connections, and a page on a public address relies on the sandbox and a content security policy to keep the admin token away. Install only plugins you understand and are willing to hand to her.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<p align="right"><a href="../zh/recovery.md">中文</a> · <b>English</b></p>
|
|
2
|
+
|
|
3
|
+
# Data: backup, migration and recovery
|
|
4
|
+
|
|
5
|
+
Everything of her — memory, self, diary, relationships — lives in one SQLite database. This document explains how to keep it safe, how to verify a backup and how to restore.
|
|
6
|
+
|
|
7
|
+
## Backup
|
|
8
|
+
|
|
9
|
+
The database uses WAL, FULL durability and write-lock waiting. Run `npm run backup` before upgrading; while running, automatic backups still happen on the configured schedule. Both full and automatic backups are written to a temporary file first, published only after SQLite integrity and reference checks, and accompanied by a `.db.json` manifest: SHA-256, application version, database version, schema signature and the number of life records. When copying a backup, copy both files together to another disk.
|
|
10
|
+
|
|
11
|
+
## Verify and restore
|
|
12
|
+
|
|
13
|
+
```powershell
|
|
14
|
+
npm run recovery -- verify --backup "data/backups/some-backup.db"
|
|
15
|
+
npm run recovery -- restore --backup "data/backups/some-backup.db" --to "data/recovered-life.db"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Restore only publishes to a brand-new file; it refuses when the target or its WAL/SHM files already exist. The manifest and integrity are checked before restoring and the new file is checked afterwards; the running instance and its database are never overwritten. An old backup without a manifest can still be checked and restored, and the command says it has no SHA-256 manifest.
|
|
19
|
+
|
|
20
|
+
After verifying the restored copy, stop the original instance, set `DB_PATH` for the instance you want to point at the new file, and start it. Keep the original database and backups until you are sure the restored experiences, self, diary, relationships and permissions are what you expect. To test a restored copy, use another port and simulation mode so no second QQ connection is made.
|
|
21
|
+
|
|
22
|
+
## Migration
|
|
23
|
+
|
|
24
|
+
The schema version is stored in `PRAGMA user_version`. Before the first migration of an existing database, a checked `luckytri-migration-*.db` snapshot and manifest are saved automatically; it does not take part in automatic-backup rotation. The new version is recorded only after the migration succeeds. A database with a newer version opened by an older program is refused read-only, so a downgrade cannot corrupt records. When the schema changes later, bump `DATABASE_VERSION`, keep the migration idempotent and add regression tests for restoring and upgrading old databases.
|
|
25
|
+
|
|
26
|
+
## Limits
|
|
27
|
+
|
|
28
|
+
Verification finds corruption and manifest mismatches; the SHA-256 manifest is not a cryptographic signature. A database backup contains the knowledge passages, vectors and reading records already stored in it; external image attachments and the original files in `data/knowledge` must be copied separately. A backup on the same disk cannot survive failure of the whole disk.
|