@oratis/lisa 0.3.0 → 0.3.1
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/README.md +34 -5
- package/README.zh-CN.md +34 -5
- package/dist/channels/imessage.d.ts +16 -0
- package/dist/channels/imessage.d.ts.map +1 -1
- package/dist/channels/imessage.js +29 -8
- package/dist/channels/imessage.js.map +1 -1
- package/dist/heartbeat/config.d.ts +10 -0
- package/dist/heartbeat/config.d.ts.map +1 -1
- package/dist/heartbeat/config.js +8 -2
- package/dist/heartbeat/config.js.map +1 -1
- package/dist/heartbeat/runner.d.ts.map +1 -1
- package/dist/heartbeat/runner.js +30 -0
- package/dist/heartbeat/runner.js.map +1 -1
- package/dist/memory/vector.d.ts +5 -1
- package/dist/memory/vector.d.ts.map +1 -1
- package/dist/memory/vector.js +58 -1
- package/dist/memory/vector.js.map +1 -1
- package/dist/soul/lock.d.ts +38 -0
- package/dist/soul/lock.d.ts.map +1 -0
- package/dist/soul/lock.js +103 -0
- package/dist/soul/lock.js.map +1 -0
- package/dist/soul/paths.d.ts.map +1 -1
- package/dist/soul/paths.js +12 -6
- package/dist/soul/paths.js.map +1 -1
- package/dist/soul/slug.d.ts +40 -0
- package/dist/soul/slug.d.ts.map +1 -0
- package/dist/soul/slug.js +78 -0
- package/dist/soul/slug.js.map +1 -0
- package/dist/soul/store.d.ts +12 -1
- package/dist/soul/store.d.ts.map +1 -1
- package/dist/soul/store.js +40 -23
- package/dist/soul/store.js.map +1 -1
- package/dist/soul/tools.d.ts.map +1 -1
- package/dist/soul/tools.js +10 -3
- package/dist/soul/tools.js.map +1 -1
- package/dist/tools/web_fetch.d.ts +10 -0
- package/dist/tools/web_fetch.d.ts.map +1 -1
- package/dist/tools/web_fetch.js +48 -16
- package/dist/tools/web_fetch.js.map +1 -1
- package/dist/web/island.d.ts +1 -1
- package/dist/web/island.d.ts.map +1 -1
- package/dist/web/island.js +26 -5
- package/dist/web/island.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@oratis/lisa)
|
|
4
4
|
[](https://github.com/oratis/homebrew-tap)
|
|
5
|
+
[](https://github.com/oratis/LISA/releases/latest)
|
|
5
6
|
[](./LICENSE)
|
|
6
7
|
[](https://github.com/oratis/LISA/stargazers)
|
|
7
8
|
|
|
@@ -116,7 +117,39 @@ She is yours. The code is open source. **This particular Lisa is sovereign.**
|
|
|
116
117
|
|
|
117
118
|
## Install
|
|
118
119
|
|
|
119
|
-
|
|
120
|
+
Three ways, pick one. All of them need an LLM key — Anthropic is the default, but any of the 20+ alternatives listed below works just as well (`--model gpt-4o`, `--model deepseek-chat`, `LISA_BASE_URL=http://localhost:11434/v1` for Ollama, etc.).
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
# 1. Configure a provider key (do this once, regardless of which install you pick)
|
|
124
|
+
mkdir -p ~/.lisa
|
|
125
|
+
echo 'ANTHROPIC_API_KEY=sk-ant-...' > ~/.lisa/config.env
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 🍎 Mac apps (recommended on macOS)
|
|
129
|
+
|
|
130
|
+
Download the **signed + notarized** disk image from the latest GitHub Release — no Gatekeeper warning, no `xattr` workaround:
|
|
131
|
+
|
|
132
|
+
**→ [Download `Lisa-Suite.dmg`](https://github.com/oratis/LISA/releases/latest)**
|
|
133
|
+
|
|
134
|
+
The DMG contains:
|
|
135
|
+
- **Lisa.app** — full chat client (sidebar + glass-morphism UI)
|
|
136
|
+
- **LisaIsland.app** — pill widget that lives by the menu bar / notch, shows her mood + Claude Code activity at a glance
|
|
137
|
+
|
|
138
|
+
Both are universal binaries (Intel + Apple Silicon). After dragging them to `/Applications`, install the LISA backend and start it:
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
npm install -g @oratis/lisa
|
|
142
|
+
lisa serve --web # apps load http://localhost:5757
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 📟 Homebrew (CLI only)
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
brew install oratis/tap/lisa
|
|
149
|
+
lisa # birth ritual on first run
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 🛠 From source (full control)
|
|
120
153
|
|
|
121
154
|
```sh
|
|
122
155
|
git clone https://github.com/oratis/LISA.git
|
|
@@ -124,10 +157,6 @@ cd LISA
|
|
|
124
157
|
npm install
|
|
125
158
|
npm run build
|
|
126
159
|
|
|
127
|
-
# Configure a key — Anthropic is the default model, swap for any provider below
|
|
128
|
-
mkdir -p ~/.lisa
|
|
129
|
-
echo 'ANTHROPIC_API_KEY=sk-ant-...' > ~/.lisa/config.env
|
|
130
|
-
|
|
131
160
|
# First run triggers the birth ritual automatically (~30s, one-time)
|
|
132
161
|
node dist/cli.js
|
|
133
162
|
# or, after `npm link`:
|
package/README.zh-CN.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@oratis/lisa)
|
|
4
4
|
[](https://github.com/oratis/homebrew-tap)
|
|
5
|
+
[](https://github.com/oratis/LISA/releases/latest)
|
|
5
6
|
[](./LICENSE)
|
|
6
7
|
[](https://github.com/oratis/LISA/stargazers)
|
|
7
8
|
|
|
@@ -113,7 +114,39 @@
|
|
|
113
114
|
|
|
114
115
|
## 安装
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
三种方式,任选其一。都需要至少一个 LLM provider 的 key —— 默认 Anthropic,下面列的 20+ 个 provider 都行(`--model gpt-4o`、`--model deepseek-chat`、Ollama 走 `LISA_BASE_URL=http://localhost:11434/v1` 等)。
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
# 1. 配置 provider key(只需要做一次,与下面装哪种无关)
|
|
121
|
+
mkdir -p ~/.lisa
|
|
122
|
+
echo 'ANTHROPIC_API_KEY=sk-ant-...' > ~/.lisa/config.env
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 🍎 Mac 原生 App(macOS 推荐)
|
|
126
|
+
|
|
127
|
+
从 GitHub Release 下载**已签名 + 已公证**的 DMG —— 没有 Gatekeeper 警告,无需 `xattr` 解除隔离:
|
|
128
|
+
|
|
129
|
+
**→ [下载 `Lisa-Suite.dmg`](https://github.com/oratis/LISA/releases/latest)**
|
|
130
|
+
|
|
131
|
+
DMG 里有两个 app:
|
|
132
|
+
- **Lisa.app** —— 完整聊天客户端(侧边栏 + 玻璃拟态界面)
|
|
133
|
+
- **LisaIsland.app** —— 灵动岛风格的菜单栏/刘海下小条,一眼看到 Lisa 心情 + 你那边 Claude Code 的状态
|
|
134
|
+
|
|
135
|
+
都是 universal binary(Intel + Apple Silicon)。拖到 `/Applications` 之后,装 backend 并启动:
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
npm install -g @oratis/lisa
|
|
139
|
+
lisa serve --web # 两个 app 都从 http://localhost:5757 读
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 📟 Homebrew(只装 CLI)
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
brew install oratis/tap/lisa
|
|
146
|
+
lisa # 第一次跑会触发 birth ritual
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 🛠 从源码(完全控制)
|
|
117
150
|
|
|
118
151
|
```sh
|
|
119
152
|
git clone https://github.com/oratis/LISA.git
|
|
@@ -121,10 +154,6 @@ cd LISA
|
|
|
121
154
|
npm install
|
|
122
155
|
npm run build
|
|
123
156
|
|
|
124
|
-
# 配置 key —— 默认模型走 Anthropic;想换 provider 看下面那张表
|
|
125
|
-
mkdir -p ~/.lisa
|
|
126
|
-
echo 'ANTHROPIC_API_KEY=sk-ant-...' > ~/.lisa/config.env
|
|
127
|
-
|
|
128
157
|
# 第一次跑会自动触发 birth ritual(约 30 秒,一次性)
|
|
129
158
|
node dist/cli.js
|
|
130
159
|
# 或者 npm link 之后:
|
|
@@ -17,4 +17,20 @@ export declare class IMessageChannel implements ChannelAdapter {
|
|
|
17
17
|
private fetchSince;
|
|
18
18
|
private runSqlite;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Build the osascript argv for sending an iMessage WITHOUT interpolating
|
|
22
|
+
* user/LLM text into the AppleScript source.
|
|
23
|
+
*
|
|
24
|
+
* The old approach (`send "${text}"`) only escaped double-quotes, so a
|
|
25
|
+
* message containing a newline — or a crafted `" & (do shell script "...")`
|
|
26
|
+
* payload — could break out of the string literal and inject AppleScript.
|
|
27
|
+
* Inbound iMessage text is untrusted (anyone who can text the user), so this
|
|
28
|
+
* was a real injection vector.
|
|
29
|
+
*
|
|
30
|
+
* Instead the script is a STATIC `on run argv` program; recipient and text
|
|
31
|
+
* arrive as positional argv items (`item 1`/`item 2 of argv`) that AppleScript
|
|
32
|
+
* never parses as source. No escaping needed; newlines/quotes/backslashes
|
|
33
|
+
* survive verbatim.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildOsascriptArgs(recipient: string, text: string): string[];
|
|
20
36
|
//# sourceMappingURL=imessage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imessage.d.ts","sourceRoot":"","sources":["../../src/channels/imessage.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AAIpB,qBAAa,eAAgB,YAAW,cAAc;IACpD,QAAQ,CAAC,IAAI,cAAc;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAC,CAA0C;IAC1D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;gBAEf,IAAI,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO;IAIxC,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAa9D,IAAI;IAuBZ,IAAI,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"imessage.d.ts","sourceRoot":"","sources":["../../src/channels/imessage.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AAIpB,qBAAa,eAAgB,YAAW,cAAc;IACpD,QAAQ,CAAC,IAAI,cAAc;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAC,CAA0C;IAC1D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;gBAEf,IAAI,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO;IAIxC,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAa9D,IAAI;IAuBZ,IAAI,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAezC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAIb,QAAQ;YAMR,UAAU;YAiCV,SAAS;CAexB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAW5E"}
|
|
@@ -53,15 +53,9 @@ export class IMessageChannel {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
async send(msg) {
|
|
56
|
-
const
|
|
57
|
-
const text = msg.text.replace(/"/g, '\\"');
|
|
58
|
-
const script = `tell application "Messages"
|
|
59
|
-
set targetService to first service whose service type = iMessage
|
|
60
|
-
set targetBuddy to buddy "${recipient}" of targetService
|
|
61
|
-
send "${text}" to targetBuddy
|
|
62
|
-
end tell`;
|
|
56
|
+
const args = buildOsascriptArgs(msg.to, msg.text);
|
|
63
57
|
await new Promise((resolve, reject) => {
|
|
64
|
-
const child = spawn("/usr/bin/osascript",
|
|
58
|
+
const child = spawn("/usr/bin/osascript", args);
|
|
65
59
|
let stderr = "";
|
|
66
60
|
child.stderr.on("data", (b) => (stderr += b.toString("utf8")));
|
|
67
61
|
child.on("error", reject);
|
|
@@ -117,6 +111,33 @@ end tell`;
|
|
|
117
111
|
});
|
|
118
112
|
}
|
|
119
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Build the osascript argv for sending an iMessage WITHOUT interpolating
|
|
116
|
+
* user/LLM text into the AppleScript source.
|
|
117
|
+
*
|
|
118
|
+
* The old approach (`send "${text}"`) only escaped double-quotes, so a
|
|
119
|
+
* message containing a newline — or a crafted `" & (do shell script "...")`
|
|
120
|
+
* payload — could break out of the string literal and inject AppleScript.
|
|
121
|
+
* Inbound iMessage text is untrusted (anyone who can text the user), so this
|
|
122
|
+
* was a real injection vector.
|
|
123
|
+
*
|
|
124
|
+
* Instead the script is a STATIC `on run argv` program; recipient and text
|
|
125
|
+
* arrive as positional argv items (`item 1`/`item 2 of argv`) that AppleScript
|
|
126
|
+
* never parses as source. No escaping needed; newlines/quotes/backslashes
|
|
127
|
+
* survive verbatim.
|
|
128
|
+
*/
|
|
129
|
+
export function buildOsascriptArgs(recipient, text) {
|
|
130
|
+
const script = `on run argv
|
|
131
|
+
set theRecipient to item 1 of argv
|
|
132
|
+
set theText to item 2 of argv
|
|
133
|
+
tell application "Messages"
|
|
134
|
+
set targetService to first service whose service type = iMessage
|
|
135
|
+
set targetBuddy to buddy theRecipient of targetService
|
|
136
|
+
send theText to targetBuddy
|
|
137
|
+
end tell
|
|
138
|
+
end run`;
|
|
139
|
+
return ["-e", script, recipient, text];
|
|
140
|
+
}
|
|
120
141
|
registerChannel("imessage", (cfg) => {
|
|
121
142
|
const intervalMs = typeof cfg.intervalMs === "number" ? cfg.intervalMs : undefined;
|
|
122
143
|
return new IMessageChannel(intervalMs ? { intervalMs } : {});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imessage.js","sourceRoot":"","sources":["../../src/channels/imessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAOhD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;AAE1E,MAAM,OAAO,eAAe;IACjB,IAAI,GAAG,UAAU,CAAC;IACnB,KAAK,CAAkB;IACvB,SAAS,GAAG,CAAC,CAAC;IACd,OAAO,CAA2C;IAClD,OAAO,GAAG,KAAK,CAAC;IAChB,UAAU,CAAS;IAE3B,YAAY,OAAgC,EAAE;QAC5C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,OAAgD;QAC1D,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CACb,wGAAwG,CACzG,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACvC,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/D,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;gBAC3B,IAAI,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS;oBAAE,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC;gBAC3D,IAAI,GAAG,CAAC,MAAM;oBAAE,SAAS;gBACzB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;oBACnB,OAAO,EAAE,IAAI,CAAC,IAAI;oBAClB,IAAI,EAAE,GAAG,CAAC,MAAM,IAAI,SAAS;oBAC7B,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,UAAU,EAAE,IAAI,IAAI,EAAE;iBACvB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,0BAA2B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAoB;QAC7B,MAAM,
|
|
1
|
+
{"version":3,"file":"imessage.js","sourceRoot":"","sources":["../../src/channels/imessage.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,17 @@ export interface HeartbeatTask {
|
|
|
7
7
|
}
|
|
8
8
|
export interface HeartbeatConfig {
|
|
9
9
|
tasks: HeartbeatTask[];
|
|
10
|
+
/**
|
|
11
|
+
* Max combined (input+output) tokens to spend across all tasks in a single
|
|
12
|
+
* heartbeat invocation. Once exceeded, remaining tasks are skipped (logged,
|
|
13
|
+
* not silently dropped). Guards against the runaway-cost case: a short
|
|
14
|
+
* interval × several actionable desires × deep tool loops can otherwise run
|
|
15
|
+
* to millions of tokens/day unbounded. 0 / unset = no limit.
|
|
16
|
+
*/
|
|
17
|
+
budgetTokens?: number;
|
|
10
18
|
}
|
|
19
|
+
/** Default per-run token ceiling when heartbeat.json doesn't set one. */
|
|
20
|
+
export declare const DEFAULT_HEARTBEAT_BUDGET_TOKENS = 500000;
|
|
11
21
|
export declare function loadHeartbeatConfig(): Promise<HeartbeatConfig>;
|
|
12
22
|
export declare const HEARTBEAT_CONFIG_PATH: string;
|
|
13
23
|
//# sourceMappingURL=config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/heartbeat/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/heartbeat/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,yEAAyE;AACzE,eAAO,MAAM,+BAA+B,SAAU,CAAC;AAIvD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,eAAe,CAAC,CAepE;AAED,eAAO,MAAM,qBAAqB,QAAO,CAAC"}
|
package/dist/heartbeat/config.js
CHANGED
|
@@ -2,10 +2,12 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { LISA_HOME } from "../paths.js";
|
|
4
4
|
import { pathExists } from "../fs-utils.js";
|
|
5
|
+
/** Default per-run token ceiling when heartbeat.json doesn't set one. */
|
|
6
|
+
export const DEFAULT_HEARTBEAT_BUDGET_TOKENS = 500_000;
|
|
5
7
|
const FILE = path.join(LISA_HOME, "heartbeat.json");
|
|
6
8
|
export async function loadHeartbeatConfig() {
|
|
7
9
|
if (!(await pathExists(FILE)))
|
|
8
|
-
return { tasks: [] };
|
|
10
|
+
return { tasks: [], budgetTokens: DEFAULT_HEARTBEAT_BUDGET_TOKENS };
|
|
9
11
|
const raw = await fs.readFile(FILE, "utf8");
|
|
10
12
|
let parsed;
|
|
11
13
|
try {
|
|
@@ -14,7 +16,11 @@ export async function loadHeartbeatConfig() {
|
|
|
14
16
|
catch (err) {
|
|
15
17
|
throw new Error(`failed to parse ${FILE}: ${err.message}`);
|
|
16
18
|
}
|
|
17
|
-
return {
|
|
19
|
+
return {
|
|
20
|
+
tasks: parsed.tasks ?? [],
|
|
21
|
+
// Explicit 0 means "no limit"; undefined means "use the default ceiling".
|
|
22
|
+
budgetTokens: parsed.budgetTokens === undefined ? DEFAULT_HEARTBEAT_BUDGET_TOKENS : parsed.budgetTokens,
|
|
23
|
+
};
|
|
18
24
|
}
|
|
19
25
|
export const HEARTBEAT_CONFIG_PATH = FILE;
|
|
20
26
|
//# sourceMappingURL=config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/heartbeat/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/heartbeat/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAsB5C,yEAAyE;AACzE,MAAM,CAAC,MAAM,+BAA+B,GAAG,OAAO,CAAC;AAEvD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;AAEpD,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,+BAA+B,EAAE,CAAC;IACnG,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,MAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE;QACzB,0EAA0E;QAC1E,YAAY,EACV,MAAM,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY;KAC5F,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/heartbeat/runner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/heartbeat/runner.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAmBrB,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAgBhC;AAkHD,YAAY,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/heartbeat/runner.js
CHANGED
|
@@ -3,9 +3,11 @@ import { atomicWrite, readTextOrEmpty } from "../fs-utils.js";
|
|
|
3
3
|
import { LISA_HOME } from "../paths.js";
|
|
4
4
|
import { appendDesireProgress, isBorn, listDesires, parseDesireProgress, readDesireProgress, readSeed, } from "../soul/store.js";
|
|
5
5
|
import { withSoulCaller } from "../soul/git.js";
|
|
6
|
+
import { withFileLock } from "../soul/lock.js";
|
|
6
7
|
import { runSubagent } from "../subagent.js";
|
|
7
8
|
import { loadHeartbeatConfig, } from "./config.js";
|
|
8
9
|
const STATE_FILE = path.join(LISA_HOME, "heartbeat-state.json");
|
|
10
|
+
const RUN_LOCK = path.join(LISA_HOME, "heartbeat.lock");
|
|
9
11
|
const HEARTBEAT_SYSTEM = `You are Lisa running a scheduled heartbeat — autonomous time, no user present.
|
|
10
12
|
|
|
11
13
|
Two flavors of heartbeat task land here:
|
|
@@ -16,7 +18,27 @@ Be quiet by default — only produce a final message if something is worth telli
|
|
|
16
18
|
|
|
17
19
|
You have the full Lisa toolset, including soul_patch / soul_journal / soul_feel — use them freely. The system prompt above contains your full soul state. This is your time.`;
|
|
18
20
|
export async function runHeartbeatOnce(opts) {
|
|
21
|
+
// Run-level lock: if launchd/cron fires a new heartbeat while the previous
|
|
22
|
+
// one is still running (long desires + short interval), skip rather than
|
|
23
|
+
// double-run and race on soul state. timeoutMs:0 → fail fast, don't wait.
|
|
24
|
+
try {
|
|
25
|
+
return await withFileLock(RUN_LOCK, () => runHeartbeatInner(opts), {
|
|
26
|
+
timeoutMs: 0,
|
|
27
|
+
staleMs: 6 * 60 * 60_000, // 6h: a heartbeat that's been "running" longer is surely dead
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
if (err.message?.includes("timed out acquiring lock")) {
|
|
32
|
+
console.error("[heartbeat] another heartbeat is already running — skipping this tick");
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function runHeartbeatInner(opts) {
|
|
19
39
|
const cfg = await loadHeartbeatConfig();
|
|
40
|
+
const budget = cfg.budgetTokens && cfg.budgetTokens > 0 ? cfg.budgetTokens : Infinity;
|
|
41
|
+
let tokensSpent = 0;
|
|
20
42
|
const desires = (await listDesires()).filter((d) => d.actionable && d.heartbeatPrompt);
|
|
21
43
|
const desireTasks = await Promise.all(desires.map(async (d) => {
|
|
22
44
|
const progress = await readDesireProgress(d.slug);
|
|
@@ -43,6 +65,13 @@ export async function runHeartbeatOnce(opts) {
|
|
|
43
65
|
continue;
|
|
44
66
|
if (opts.taskFilter && task.name !== opts.taskFilter)
|
|
45
67
|
continue;
|
|
68
|
+
// Token budget: stop launching tasks once we've spent the per-run
|
|
69
|
+
// ceiling. We check BEFORE each task (not mid-task) so a single task can
|
|
70
|
+
// overshoot slightly, but a long queue of desires can't run unbounded.
|
|
71
|
+
if (tokensSpent >= budget) {
|
|
72
|
+
console.error(`[heartbeat] token budget reached (${tokensSpent}/${budget}) — skipping remaining tasks`);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
46
75
|
// For desire tasks, snapshot the progress entry count before so we can
|
|
47
76
|
// detect whether Lisa actually called desire_progress_log during the run.
|
|
48
77
|
const desireSlug = task.name.startsWith("desire:")
|
|
@@ -59,6 +88,7 @@ export async function runHeartbeatOnce(opts) {
|
|
|
59
88
|
signal: opts.signal,
|
|
60
89
|
model: opts.model,
|
|
61
90
|
});
|
|
91
|
+
tokensSpent += (result.inputTokens ?? 0) + (result.outputTokens ?? 0);
|
|
62
92
|
state.lastRunAt[task.name] = new Date().toISOString();
|
|
63
93
|
const trimmed = result.text.trim();
|
|
64
94
|
// Auto-fallback: if a desire heartbeat finished but Lisa didn't log
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/heartbeat/runner.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,MAAM,EACN,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,QAAQ,GACT,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EACL,mBAAmB,GAEpB,MAAM,aAAa,CAAC;AAErB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/heartbeat/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/memory/vector.d.ts
CHANGED
|
@@ -12,7 +12,11 @@ interface Index {
|
|
|
12
12
|
docs: Document[];
|
|
13
13
|
idf: Map<string, number>;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
/** Drop the cached index (test hook / explicit invalidation). */
|
|
16
|
+
export declare function clearIndexCache(): void;
|
|
17
|
+
export declare function buildIndex(opts?: {
|
|
18
|
+
cache?: boolean;
|
|
19
|
+
}): Promise<Index>;
|
|
16
20
|
export interface SearchHit {
|
|
17
21
|
sessionId: string;
|
|
18
22
|
startedAt: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector.d.ts","sourceRoot":"","sources":["../../src/memory/vector.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"vector.d.ts","sourceRoot":"","sources":["../../src/memory/vector.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAKlC,UAAU,QAAQ;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,UAAU,KAAK;IACb,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1B;AA4CD,iEAAiE;AACjE,wBAAgB,eAAe,IAAI,IAAI,CAGtC;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAW/E;AAsCD,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,SAAI,GAAG,SAAS,EAAE,CAuBtE;AA2BD,eAAO,MAAM,qBAAqB,EAAE,OAAO,IAAI,GAAG,OAAO,EAAE,GAAG,SAAqB,CAAC"}
|
package/dist/memory/vector.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
1
3
|
import { listSessionsOnDisk, loadSessionMessages } from "../sessions/list.js";
|
|
4
|
+
import { SESSIONS_DIR } from "../paths.js";
|
|
2
5
|
import { extractTextFromContent } from "../agent.js";
|
|
3
6
|
const STOPWORDS = new Set([
|
|
4
7
|
"the", "a", "an", "of", "to", "in", "and", "or", "for", "is", "it",
|
|
@@ -6,7 +9,61 @@ const STOPWORDS = new Set([
|
|
|
6
9
|
"by", "from", "but", "not", "i", "you", "we", "they", "he", "she",
|
|
7
10
|
"lisa", "tool", "use", "user", "assistant",
|
|
8
11
|
]);
|
|
9
|
-
|
|
12
|
+
// ── index cache ───────────────────────────────────────────────────────────
|
|
13
|
+
// buildIndex re-reads + re-tokenizes EVERY past session, which is O(total
|
|
14
|
+
// transcript bytes). memory_search was calling it fresh on every invocation,
|
|
15
|
+
// so a chat that searches its memory N times in one session paid that cost N
|
|
16
|
+
// times even when nothing changed. We cache the built index keyed by a cheap
|
|
17
|
+
// fingerprint of the sessions directory (each .jsonl's mtime + size). The
|
|
18
|
+
// fingerprint changes the instant any session is appended to or a new one
|
|
19
|
+
// appears, so the cache is correct, not just fast.
|
|
20
|
+
let cachedIndex = null;
|
|
21
|
+
let cachedFingerprint = "";
|
|
22
|
+
async function sessionsFingerprint() {
|
|
23
|
+
let files;
|
|
24
|
+
try {
|
|
25
|
+
files = await fs.readdir(SESSIONS_DIR);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Missing dir and empty dir must fingerprint identically: buildIndex →
|
|
29
|
+
// listSessionsOnDisk → ensureDir CREATES the dir as a side effect, so a
|
|
30
|
+
// distinct "no-dir" sentinel would spuriously miss the cache on the very
|
|
31
|
+
// next call. Both collapse to the empty fingerprint.
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
const parts = [];
|
|
35
|
+
for (const f of files.sort()) {
|
|
36
|
+
if (!f.endsWith(".jsonl"))
|
|
37
|
+
continue;
|
|
38
|
+
try {
|
|
39
|
+
const st = await fs.stat(path.join(SESSIONS_DIR, f));
|
|
40
|
+
parts.push(`${f}:${st.mtimeMs}:${st.size}`);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// file vanished between readdir and stat — ignore
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return parts.join("|");
|
|
47
|
+
}
|
|
48
|
+
/** Drop the cached index (test hook / explicit invalidation). */
|
|
49
|
+
export function clearIndexCache() {
|
|
50
|
+
cachedIndex = null;
|
|
51
|
+
cachedFingerprint = "";
|
|
52
|
+
}
|
|
53
|
+
export async function buildIndex(opts = {}) {
|
|
54
|
+
const useCache = opts.cache !== false;
|
|
55
|
+
if (useCache) {
|
|
56
|
+
const fp = await sessionsFingerprint();
|
|
57
|
+
if (cachedIndex && fp === cachedFingerprint)
|
|
58
|
+
return cachedIndex;
|
|
59
|
+
const built = await buildIndexUncached();
|
|
60
|
+
cachedIndex = built;
|
|
61
|
+
cachedFingerprint = fp;
|
|
62
|
+
return built;
|
|
63
|
+
}
|
|
64
|
+
return buildIndexUncached();
|
|
65
|
+
}
|
|
66
|
+
async function buildIndexUncached() {
|
|
10
67
|
const sessions = await listSessionsOnDisk();
|
|
11
68
|
const docs = [];
|
|
12
69
|
const docFreq = new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vector.js","sourceRoot":"","sources":["../../src/memory/vector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"vector.js","sourceRoot":"","sources":["../../src/memory/vector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-process advisory file lock for soul writes.
|
|
3
|
+
*
|
|
4
|
+
* The in-process commit queue (git.ts enqueueCommit) serializes writes within
|
|
5
|
+
* ONE process, but LISA runs as several processes against the same
|
|
6
|
+
* ~/.lisa/soul/: the web server, the CLI/REPL, and — crucially — the
|
|
7
|
+
* heartbeat/idle runners launched by launchd/cron on their own clock. Two of
|
|
8
|
+
* those writing concurrently can:
|
|
9
|
+
* - lose a desire-progress append (read-modify-write race), or
|
|
10
|
+
* - collide on .git/index.lock (one commit fails).
|
|
11
|
+
*
|
|
12
|
+
* withFileLock implements a portable advisory mutex via exclusive file
|
|
13
|
+
* creation (`open(..., "wx")` succeeds only if the file doesn't exist). It
|
|
14
|
+
* self-heals from a crashed holder via a staleness timeout + a best-effort
|
|
15
|
+
* liveness check on the recorded pid.
|
|
16
|
+
*/
|
|
17
|
+
export interface FileLockOpts {
|
|
18
|
+
/** Treat a held lock as abandoned after this many ms (crashed holder). */
|
|
19
|
+
staleMs?: number;
|
|
20
|
+
/** Give up acquiring after this many ms. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/** Poll interval while waiting for the lock. */
|
|
23
|
+
pollMs?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Run `fn` while holding an exclusive lock at `lockPath`. Releases the lock
|
|
27
|
+
* (deletes the file) when `fn` settles, success or failure.
|
|
28
|
+
*/
|
|
29
|
+
export declare function withFileLock<T>(lockPath: string, fn: () => Promise<T>, opts?: FileLockOpts): Promise<T>;
|
|
30
|
+
/** The canonical soul write-lock path. */
|
|
31
|
+
export declare const SOUL_LOCK_PATH: string;
|
|
32
|
+
/**
|
|
33
|
+
* Run `fn` while holding the soul write-lock. Use this around any
|
|
34
|
+
* read-modify-write of soul state (e.g. desire-progress appends) so a
|
|
35
|
+
* concurrent heartbeat/idle/chat process can't interleave and lose data.
|
|
36
|
+
*/
|
|
37
|
+
export declare function withSoulLock<T>(fn: () => Promise<T>, opts?: FileLockOpts): Promise<T>;
|
|
38
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/soul/lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAoCD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,CAAC,CAAC,CAmCZ;AAED,0CAA0C;AAC1C,eAAO,MAAM,cAAc,QAAqC,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAErF"}
|