@oratis/lisa 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -5
- package/README.zh-CN.md +34 -5
- package/dist/advisor/detectors.d.ts +25 -0
- package/dist/advisor/detectors.d.ts.map +1 -0
- package/dist/advisor/detectors.js +161 -0
- package/dist/advisor/detectors.js.map +1 -0
- package/dist/advisor/engine.d.ts +41 -0
- package/dist/advisor/engine.d.ts.map +1 -0
- package/dist/advisor/engine.js +156 -0
- package/dist/advisor/engine.js.map +1 -0
- package/dist/advisor/types.d.ts +68 -0
- package/dist/advisor/types.d.ts.map +1 -0
- package/dist/advisor/types.js +19 -0
- package/dist/advisor/types.js.map +1 -0
- 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/integrations/claude-code/observer.d.ts +19 -0
- package/dist/integrations/claude-code/observer.d.ts.map +1 -0
- package/dist/integrations/claude-code/observer.js +59 -0
- package/dist/integrations/claude-code/observer.js.map +1 -0
- package/dist/integrations/claude-code/parser.d.ts +2 -0
- package/dist/integrations/claude-code/parser.d.ts.map +1 -1
- package/dist/integrations/claude-code/parser.js +153 -0
- package/dist/integrations/claude-code/parser.js.map +1 -1
- package/dist/integrations/claude-code/watcher.d.ts +9 -0
- package/dist/integrations/claude-code/watcher.d.ts.map +1 -1
- package/dist/integrations/claude-code/watcher.js +25 -4
- package/dist/integrations/claude-code/watcher.js.map +1 -1
- package/dist/integrations/codex/observer.d.ts +48 -0
- package/dist/integrations/codex/observer.d.ts.map +1 -0
- package/dist/integrations/codex/observer.js +213 -0
- package/dist/integrations/codex/observer.js.map +1 -0
- package/dist/integrations/current-hub.d.ts +13 -0
- package/dist/integrations/current-hub.d.ts.map +1 -0
- package/dist/integrations/current-hub.js +17 -0
- package/dist/integrations/current-hub.js.map +1 -0
- package/dist/integrations/hub.d.ts +45 -0
- package/dist/integrations/hub.d.ts.map +1 -0
- package/dist/integrations/hub.js +110 -0
- package/dist/integrations/hub.js.map +1 -0
- package/dist/integrations/registry.d.ts +15 -0
- package/dist/integrations/registry.d.ts.map +1 -0
- package/dist/integrations/registry.js +38 -0
- package/dist/integrations/registry.js.map +1 -0
- package/dist/integrations/types.d.ts +90 -0
- package/dist/integrations/types.d.ts.map +1 -0
- package/dist/integrations/types.js +12 -0
- package/dist/integrations/types.js.map +1 -0
- 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/advise_now.d.ts +11 -0
- package/dist/tools/advise_now.d.ts.map +1 -0
- package/dist/tools/advise_now.js +34 -0
- package/dist/tools/advise_now.js.map +1 -0
- package/dist/tools/dispatch_agent.d.ts +38 -0
- package/dist/tools/dispatch_agent.d.ts.map +1 -0
- package/dist/tools/dispatch_agent.js +145 -0
- package/dist/tools/dispatch_agent.js.map +1 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.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 +69 -5
- package/dist/web/island.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +79 -16
- package/dist/web/server.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 之后:
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advisor detectors — pure functions: an AdvisorInput snapshot in, candidate
|
|
3
|
+
* Suggestions out. No I/O, no state mutation, fully testable. The engine
|
|
4
|
+
* (engine.ts) is what scores + throttles + dedupes them.
|
|
5
|
+
*
|
|
6
|
+
* Every detector works from STRUCTURAL signals only (state, cwd, the Tier-2
|
|
7
|
+
* SessionActivity) — never conversation content.
|
|
8
|
+
*/
|
|
9
|
+
import type { AdvisorInput, Suggestion } from "./types.js";
|
|
10
|
+
export declare const STUCK_MS: number;
|
|
11
|
+
export declare const COST_SPIKE_TOKENS = 1500000;
|
|
12
|
+
/** Pending permission → urgent, actionable. */
|
|
13
|
+
export declare function detectPermission(input: AdvisorInput): Suggestion[];
|
|
14
|
+
export declare function detectStuck(input: AdvisorInput): Suggestion[];
|
|
15
|
+
/** Two+ active sessions sharing a cwd (or an overlapping touched file). */
|
|
16
|
+
export declare function detectConflict(input: AdvisorInput): Suggestion[];
|
|
17
|
+
/** Combined token usage across active sessions above the spike threshold. */
|
|
18
|
+
export declare function detectCostSpike(input: AdvisorInput): Suggestion[];
|
|
19
|
+
/** A session that finished a turn and is awaiting you (end_turn / done). */
|
|
20
|
+
export declare function detectReady(input: AdvisorInput): Suggestion[];
|
|
21
|
+
/** Nothing running but standing chores exist. */
|
|
22
|
+
export declare function detectIdleCapacity(input: AdvisorInput): Suggestion[];
|
|
23
|
+
/** Run every detector and concatenate their candidates. */
|
|
24
|
+
export declare function runAllDetectors(input: AdvisorInput): Suggestion[];
|
|
25
|
+
//# sourceMappingURL=detectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectors.d.ts","sourceRoot":"","sources":["../../src/advisor/detectors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAA+B,MAAM,YAAY,CAAC;AAGxF,eAAO,MAAM,QAAQ,QAAc,CAAC;AACpC,eAAO,MAAM,iBAAiB,UAAY,CAAC;AAoB3C,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,EAAE,CAkBlE;AAoBD,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,EAAE,CAyC7D;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,EAAE,CA2BhE;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,EAAE,CA6BjE;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,EAAE,CAqB7D;AAED,iDAAiD;AACjD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,EAAE,CAiBpE;AAED,2DAA2D;AAC3D,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,EAAE,CASjE"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advisor detectors — pure functions: an AdvisorInput snapshot in, candidate
|
|
3
|
+
* Suggestions out. No I/O, no state mutation, fully testable. The engine
|
|
4
|
+
* (engine.ts) is what scores + throttles + dedupes them.
|
|
5
|
+
*
|
|
6
|
+
* Every detector works from STRUCTURAL signals only (state, cwd, the Tier-2
|
|
7
|
+
* SessionActivity) — never conversation content.
|
|
8
|
+
*/
|
|
9
|
+
// Thresholds (ms). Tuned to "a human would want this flagged".
|
|
10
|
+
export const STUCK_MS = 10 * 60_000; // waiting/error with no change for 10 min
|
|
11
|
+
export const COST_SPIKE_TOKENS = 1_500_000; // combined tokens across active sessions
|
|
12
|
+
function mk(id, category, urgency, text, conditionHash, now, action) {
|
|
13
|
+
return { id, category, urgency, text, conditionHash, ts: now, action, score: 0 };
|
|
14
|
+
}
|
|
15
|
+
function basename(p) {
|
|
16
|
+
if (!p)
|
|
17
|
+
return "";
|
|
18
|
+
const parts = p.split("/");
|
|
19
|
+
return parts[parts.length - 1] || p;
|
|
20
|
+
}
|
|
21
|
+
/** Pending permission → urgent, actionable. */
|
|
22
|
+
export function detectPermission(input) {
|
|
23
|
+
const out = [];
|
|
24
|
+
for (const s of input.sessions) {
|
|
25
|
+
const pend = s.activity?.pendingPermission;
|
|
26
|
+
if (!pend)
|
|
27
|
+
continue;
|
|
28
|
+
out.push(mk(`permission:${s.agent}:${s.sessionId}`, "stuck", "urgent", `${s.project} wants to run ${pend} — approve?`, `permission:${pend}`, input.now, { label: "Approve", kind: "approve", arg: s.sessionId }));
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* "Stuck" = a session that looks like it needs attention but isn't making
|
|
34
|
+
* progress. The trap (and the v1 noise bug) is that a `waiting` session is
|
|
35
|
+
* USUALLY fine — Claude finished its turn (end_turn) and is just idle waiting
|
|
36
|
+
* for the user. That's not stuck, it's normal, and flagging every >10min idle
|
|
37
|
+
* session buries the real signal.
|
|
38
|
+
*
|
|
39
|
+
* So a session counts as stuck only when:
|
|
40
|
+
* - state is `error` (something actually broke), OR
|
|
41
|
+
* - state is `waiting` with a reason that means "blocked / not cleanly
|
|
42
|
+
* finished" — i.e. NOT end_turn/stop_sequence/max_tokens (clean stops,
|
|
43
|
+
* surfaced quietly by detectReady instead).
|
|
44
|
+
* AND it's been quiet for ≥ STUCK_MS. If more than COLLAPSE_THRESHOLD qualify,
|
|
45
|
+
* emit ONE rolled-up line so the digest stays scannable.
|
|
46
|
+
*/
|
|
47
|
+
const CLEAN_STOP_REASONS = new Set(["end_turn", "stop_sequence", "max_tokens"]);
|
|
48
|
+
const COLLAPSE_THRESHOLD = 3;
|
|
49
|
+
export function detectStuck(input) {
|
|
50
|
+
const stuck = input.sessions.filter((s) => {
|
|
51
|
+
if (s.activity?.pendingPermission)
|
|
52
|
+
return false; // detectPermission owns these
|
|
53
|
+
if (input.now - s.lastMtime < STUCK_MS)
|
|
54
|
+
return false;
|
|
55
|
+
if (s.state === "error")
|
|
56
|
+
return true;
|
|
57
|
+
if (s.state === "waiting")
|
|
58
|
+
return !CLEAN_STOP_REASONS.has(s.stateReason);
|
|
59
|
+
return false;
|
|
60
|
+
});
|
|
61
|
+
if (stuck.length === 0)
|
|
62
|
+
return [];
|
|
63
|
+
if (stuck.length > COLLAPSE_THRESHOLD) {
|
|
64
|
+
const errs = stuck.filter((s) => s.state === "error").length;
|
|
65
|
+
const detail = errs > 0 ? ` (${errs} errored)` : "";
|
|
66
|
+
return [
|
|
67
|
+
mk(`stuck-many:${stuck.length}`, "stuck", "notice", `${stuck.length} agent sessions look stuck or stalled${detail} — want the list?`, `stuck-many:${stuck.length}:${errs}`, input.now, { label: "List", kind: "look", arg: "stuck" }),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
return stuck.map((s) => {
|
|
71
|
+
const mins = Math.round((input.now - s.lastMtime) / 60_000);
|
|
72
|
+
const cmd = s.activity?.lastCommandName;
|
|
73
|
+
const why = s.state === "error" ? "errored" : "has stalled";
|
|
74
|
+
const on = cmd ? ` on \`${cmd}\`` : "";
|
|
75
|
+
return mk(`stuck:${s.agent}:${s.sessionId}`, "stuck", "notice", `${s.project} ${why}${on} (idle ${mins}m) — want me to look, or cancel it?`, `stuck:${s.state}:${s.stateReason}:${cmd ?? ""}`, input.now, { label: "Look", kind: "look", arg: s.sessionId });
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** Two+ active sessions sharing a cwd (or an overlapping touched file). */
|
|
79
|
+
export function detectConflict(input) {
|
|
80
|
+
const out = [];
|
|
81
|
+
const byCwd = new Map();
|
|
82
|
+
for (const s of input.sessions) {
|
|
83
|
+
if (!s.cwd)
|
|
84
|
+
continue;
|
|
85
|
+
if (s.state !== "working" && s.state !== "waiting")
|
|
86
|
+
continue;
|
|
87
|
+
const arr = byCwd.get(s.cwd) ?? [];
|
|
88
|
+
arr.push(s);
|
|
89
|
+
byCwd.set(s.cwd, arr);
|
|
90
|
+
}
|
|
91
|
+
for (const [cwd, group] of byCwd) {
|
|
92
|
+
if (group.length < 2)
|
|
93
|
+
continue;
|
|
94
|
+
const agents = [...new Set(group.map((g) => g.agent))];
|
|
95
|
+
const label = basename(cwd);
|
|
96
|
+
out.push(mk(`conflict:${cwd}`, "conflict", "notice", `${group.length} agents (${agents.join(", ")}) are both working in ${label} — high risk of clobbering. Serialize?`, `conflict:${group.map((g) => g.sessionId).sort().join(",")}`, input.now, { label: "Serialize", kind: "serialize", arg: cwd }));
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
/** Combined token usage across active sessions above the spike threshold. */
|
|
101
|
+
export function detectCostSpike(input) {
|
|
102
|
+
let total = 0;
|
|
103
|
+
let topSession = null;
|
|
104
|
+
let topTokens = 0;
|
|
105
|
+
for (const s of input.sessions) {
|
|
106
|
+
const t = s.activity?.tokens;
|
|
107
|
+
if (!t)
|
|
108
|
+
continue;
|
|
109
|
+
const sum = (t.input ?? 0) + (t.output ?? 0);
|
|
110
|
+
total += sum;
|
|
111
|
+
if (sum > topTokens) {
|
|
112
|
+
topTokens = sum;
|
|
113
|
+
topSession = s;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (total < COST_SPIKE_TOKENS)
|
|
117
|
+
return [];
|
|
118
|
+
const millions = (total / 1_000_000).toFixed(1);
|
|
119
|
+
const pct = total > 0 ? Math.round((topTokens / total) * 100) : 0;
|
|
120
|
+
const who = topSession ? ` — ${topSession.project} is ${pct}% of it` : "";
|
|
121
|
+
return [
|
|
122
|
+
mk(`cost:${Math.round(total / 100_000)}`, // bucketed so it re-fires per ~100k step
|
|
123
|
+
"cost_spike", "notice", `Active agents are at ${millions}M tokens${who}.`, `cost:${Math.round(total / 500_000)}`, input.now, topSession ? { label: "Look", kind: "look", arg: topSession.sessionId } : undefined),
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
/** A session that finished a turn and is awaiting you (end_turn / done). */
|
|
127
|
+
export function detectReady(input) {
|
|
128
|
+
const out = [];
|
|
129
|
+
for (const s of input.sessions) {
|
|
130
|
+
const ready = s.state === "done" ||
|
|
131
|
+
(s.state === "waiting" && s.stateReason === "end_turn");
|
|
132
|
+
if (!ready)
|
|
133
|
+
continue;
|
|
134
|
+
if (s.activity?.lastError)
|
|
135
|
+
continue; // erroring isn't "ready"
|
|
136
|
+
out.push(mk(`ready:${s.agent}:${s.sessionId}`, "ready", "info", `${s.project} finished and is waiting for you.`, `ready:${s.lastMtime}`, input.now, { label: "Open", kind: "open", arg: s.cwd ?? s.sessionId }));
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
/** Nothing running but standing chores exist. */
|
|
141
|
+
export function detectIdleCapacity(input) {
|
|
142
|
+
const active = input.sessions.some((s) => s.state === "working" || s.state === "waiting");
|
|
143
|
+
const pending = input.pendingDesireCount ?? 0;
|
|
144
|
+
if (active || pending <= 0)
|
|
145
|
+
return [];
|
|
146
|
+
return [
|
|
147
|
+
mk("idle-capacity", "idle", "info", `Nothing's running and you have ${pending} standing ${pending === 1 ? "task" : "tasks"} — want me to dispatch them?`, `idle:${pending}`, input.now, { label: "Dispatch", kind: "dispatch", arg: String(pending) }),
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
/** Run every detector and concatenate their candidates. */
|
|
151
|
+
export function runAllDetectors(input) {
|
|
152
|
+
return [
|
|
153
|
+
...detectPermission(input),
|
|
154
|
+
...detectStuck(input),
|
|
155
|
+
...detectConflict(input),
|
|
156
|
+
...detectCostSpike(input),
|
|
157
|
+
...detectReady(input),
|
|
158
|
+
...detectIdleCapacity(input),
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=detectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectors.js","sourceRoot":"","sources":["../../src/advisor/detectors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advisor engine — the anti-annoyance brain.
|
|
3
|
+
*
|
|
4
|
+
* Pure decision core: given this run's candidate suggestions + the persisted
|
|
5
|
+
* advisor state + the clock, decide which clear the relevance bar AND the
|
|
6
|
+
* throttle AND dedup, and which get suppressed (journaled, not shown). The
|
|
7
|
+
* caller does the I/O (load/save state, surface the survivors).
|
|
8
|
+
*
|
|
9
|
+
* See docs/ORCHESTRATOR_PLAN.md §5b.3 for the anti-annoyance contract.
|
|
10
|
+
*/
|
|
11
|
+
import { type AdvisorDecision, type AdvisorInput, type AdvisorState, type Suggestion, type SuggestionCategory } from "./types.js";
|
|
12
|
+
export declare const ADVISOR_STATE_PATH: string;
|
|
13
|
+
/** Relevance score = urgency × actionability × dismissal-decay. */
|
|
14
|
+
export declare function scoreSuggestion(s: Suggestion, state: AdvisorState): number;
|
|
15
|
+
/**
|
|
16
|
+
* Pure decision. Returns the surface/suppress split AND the next state to
|
|
17
|
+
* persist. Does not touch disk.
|
|
18
|
+
*/
|
|
19
|
+
export declare function decide(candidates: Suggestion[], state: AdvisorState, now: number): {
|
|
20
|
+
decision: AdvisorDecision;
|
|
21
|
+
nextState: AdvisorState;
|
|
22
|
+
};
|
|
23
|
+
/** Record a user dismissal so the category fades over time. */
|
|
24
|
+
export declare function applyDismissal(state: AdvisorState, id: string, category: SuggestionCategory): AdvisorState;
|
|
25
|
+
export declare function loadAdvisorState(p?: string): Promise<AdvisorState>;
|
|
26
|
+
export declare function saveAdvisorState(state: AdvisorState, p?: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Full advise cycle: detect → decide → persist. Returns the suggestions to
|
|
29
|
+
* surface. Uses the soul lock so a heartbeat advise can't race a manual one.
|
|
30
|
+
*/
|
|
31
|
+
export declare function advise(input: AdvisorInput, statePath?: string): Promise<AdvisorDecision>;
|
|
32
|
+
/**
|
|
33
|
+
* On-demand ("pull") advice: run the detectors against the current snapshot
|
|
34
|
+
* and return everything above the bar, sorted — WITHOUT the throttle/dedup
|
|
35
|
+
* (the user explicitly asked, so don't suppress) and WITHOUT mutating state.
|
|
36
|
+
* Used by the advise_now tool.
|
|
37
|
+
*/
|
|
38
|
+
export declare function adviseNow(input: AdvisorInput, state?: AdvisorState): Suggestion[];
|
|
39
|
+
/** Render surfaced suggestions into one digest card (markdown-ish text). */
|
|
40
|
+
export declare function formatDigest(suggestions: Suggestion[]): string;
|
|
41
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/advisor/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACxB,MAAM,YAAY,CAAC;AAQpB,eAAO,MAAM,kBAAkB,QAA6C,CAAC;AAE7E,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAO1E;AAUD;;;GAGG;AACH,wBAAgB,MAAM,CACpB,UAAU,EAAE,UAAU,EAAE,EACxB,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,MAAM,GACV;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,YAAY,CAAA;CAAE,CAgDxD;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,EACnB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,kBAAkB,GAC3B,YAAY,CASd;AAID,wBAAsB,gBAAgB,CACpC,CAAC,GAAE,MAA2B,GAC7B,OAAO,CAAC,YAAY,CAAC,CAQvB;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,YAAY,EACnB,CAAC,GAAE,MAA2B,GAC7B,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;GAGG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,YAAY,EACnB,SAAS,GAAE,MAA2B,GACrC,OAAO,CAAC,eAAe,CAAC,CAQ1B;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,GAAE,YAAkC,GAAG,UAAU,EAAE,CAMtG;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,CAQ9D"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advisor engine — the anti-annoyance brain.
|
|
3
|
+
*
|
|
4
|
+
* Pure decision core: given this run's candidate suggestions + the persisted
|
|
5
|
+
* advisor state + the clock, decide which clear the relevance bar AND the
|
|
6
|
+
* throttle AND dedup, and which get suppressed (journaled, not shown). The
|
|
7
|
+
* caller does the I/O (load/save state, surface the survivors).
|
|
8
|
+
*
|
|
9
|
+
* See docs/ORCHESTRATOR_PLAN.md §5b.3 for the anti-annoyance contract.
|
|
10
|
+
*/
|
|
11
|
+
import fsp from "node:fs/promises";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { LISA_HOME } from "../paths.js";
|
|
14
|
+
import { atomicWrite } from "../fs-utils.js";
|
|
15
|
+
import { withFileLock } from "../soul/lock.js";
|
|
16
|
+
import { runAllDetectors } from "./detectors.js";
|
|
17
|
+
import { emptyAdvisorState, } from "./types.js";
|
|
18
|
+
// Tunables.
|
|
19
|
+
const URGENCY_WEIGHT = { info: 1, notice: 2, urgent: 4 };
|
|
20
|
+
const MIN_SCORE = 1.5; // relevance bar: below this → journal, not user
|
|
21
|
+
const DIGEST_THROTTLE_MS = 3 * 60 * 60_000; // ≤1 non-urgent digest / 3h
|
|
22
|
+
const RE_ARM_MS = 24 * 60 * 60_000; // a standing condition can re-surface daily
|
|
23
|
+
export const ADVISOR_STATE_PATH = path.join(LISA_HOME, "advisor-state.json");
|
|
24
|
+
/** Relevance score = urgency × actionability × dismissal-decay. */
|
|
25
|
+
export function scoreSuggestion(s, state) {
|
|
26
|
+
const urgency = URGENCY_WEIGHT[s.urgency];
|
|
27
|
+
const actionability = s.action ? 1 : 0.5;
|
|
28
|
+
const catDismissals = state.categoryDismissals[s.category] ?? 0;
|
|
29
|
+
// Each dismissal of this category halves its pull (learns to shut up).
|
|
30
|
+
const decay = 1 / (1 + catDismissals * 1.0);
|
|
31
|
+
return urgency * actionability * decay;
|
|
32
|
+
}
|
|
33
|
+
/** Has this exact condition been surfaced recently (→ suppress as dup)? */
|
|
34
|
+
function isDuplicate(s, state, now) {
|
|
35
|
+
const prev = state.surfaced[s.id];
|
|
36
|
+
if (!prev)
|
|
37
|
+
return false;
|
|
38
|
+
if (prev.conditionHash !== s.conditionHash)
|
|
39
|
+
return false; // condition changed → fresh
|
|
40
|
+
return now - prev.ts < RE_ARM_MS;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Pure decision. Returns the surface/suppress split AND the next state to
|
|
44
|
+
* persist. Does not touch disk.
|
|
45
|
+
*/
|
|
46
|
+
export function decide(candidates, state, now) {
|
|
47
|
+
const surface = [];
|
|
48
|
+
const suppressed = [];
|
|
49
|
+
// Score everything first.
|
|
50
|
+
for (const c of candidates)
|
|
51
|
+
c.score = scoreSuggestion(c, state);
|
|
52
|
+
const withinThrottle = now - state.lastDigestAt < DIGEST_THROTTLE_MS;
|
|
53
|
+
let surfacedAnyNonUrgent = false;
|
|
54
|
+
for (const c of candidates) {
|
|
55
|
+
// Dedup: same condition seen recently → suppress.
|
|
56
|
+
if (isDuplicate(c, state, now)) {
|
|
57
|
+
suppressed.push(c);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (c.urgency === "urgent") {
|
|
61
|
+
// Urgent bypasses the bar + throttle — but still deduped above.
|
|
62
|
+
surface.push(c);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
// Non-urgent must clear the relevance bar…
|
|
66
|
+
if (c.score < MIN_SCORE) {
|
|
67
|
+
suppressed.push(c);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// …and the digest throttle.
|
|
71
|
+
if (withinThrottle) {
|
|
72
|
+
suppressed.push(c);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
surface.push(c);
|
|
76
|
+
surfacedAnyNonUrgent = true;
|
|
77
|
+
}
|
|
78
|
+
// Build next state: record what we surfaced + advance the digest clock.
|
|
79
|
+
const nextState = {
|
|
80
|
+
...state,
|
|
81
|
+
surfaced: { ...state.surfaced },
|
|
82
|
+
};
|
|
83
|
+
for (const s of surface) {
|
|
84
|
+
nextState.surfaced[s.id] = { ts: now, conditionHash: s.conditionHash };
|
|
85
|
+
}
|
|
86
|
+
if (surfacedAnyNonUrgent)
|
|
87
|
+
nextState.lastDigestAt = now;
|
|
88
|
+
// Sort surfaced by score desc so the digest leads with what matters.
|
|
89
|
+
surface.sort((a, b) => b.score - a.score);
|
|
90
|
+
return { decision: { surface, suppressed }, nextState };
|
|
91
|
+
}
|
|
92
|
+
/** Record a user dismissal so the category fades over time. */
|
|
93
|
+
export function applyDismissal(state, id, category) {
|
|
94
|
+
return {
|
|
95
|
+
...state,
|
|
96
|
+
dismissals: { ...state.dismissals, [id]: (state.dismissals[id] ?? 0) + 1 },
|
|
97
|
+
categoryDismissals: {
|
|
98
|
+
...state.categoryDismissals,
|
|
99
|
+
[category]: (state.categoryDismissals[category] ?? 0) + 1,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// ── I/O ─────────────────────────────────────────────────────────────────
|
|
104
|
+
export async function loadAdvisorState(p = ADVISOR_STATE_PATH) {
|
|
105
|
+
try {
|
|
106
|
+
const raw = await fsp.readFile(p, "utf8");
|
|
107
|
+
const parsed = JSON.parse(raw);
|
|
108
|
+
return { ...emptyAdvisorState(), ...parsed };
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return emptyAdvisorState();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export async function saveAdvisorState(state, p = ADVISOR_STATE_PATH) {
|
|
115
|
+
await atomicWrite(p, JSON.stringify(state, null, 2));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Full advise cycle: detect → decide → persist. Returns the suggestions to
|
|
119
|
+
* surface. Uses the soul lock so a heartbeat advise can't race a manual one.
|
|
120
|
+
*/
|
|
121
|
+
export async function advise(input, statePath = ADVISOR_STATE_PATH) {
|
|
122
|
+
return withFileLock(statePath + ".lock", async () => {
|
|
123
|
+
const state = await loadAdvisorState(statePath);
|
|
124
|
+
const candidates = runAllDetectors(input);
|
|
125
|
+
const { decision, nextState } = decide(candidates, state, input.now);
|
|
126
|
+
await saveAdvisorState(nextState, statePath);
|
|
127
|
+
return decision;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* On-demand ("pull") advice: run the detectors against the current snapshot
|
|
132
|
+
* and return everything above the bar, sorted — WITHOUT the throttle/dedup
|
|
133
|
+
* (the user explicitly asked, so don't suppress) and WITHOUT mutating state.
|
|
134
|
+
* Used by the advise_now tool.
|
|
135
|
+
*/
|
|
136
|
+
export function adviseNow(input, state = emptyAdvisorState()) {
|
|
137
|
+
const cands = runAllDetectors(input);
|
|
138
|
+
for (const c of cands)
|
|
139
|
+
c.score = scoreSuggestion(c, state);
|
|
140
|
+
return cands
|
|
141
|
+
.filter((c) => c.urgency === "urgent" || c.score >= MIN_SCORE)
|
|
142
|
+
.sort((a, b) => b.score - a.score);
|
|
143
|
+
}
|
|
144
|
+
/** Render surfaced suggestions into one digest card (markdown-ish text). */
|
|
145
|
+
export function formatDigest(suggestions) {
|
|
146
|
+
if (suggestions.length === 0)
|
|
147
|
+
return "";
|
|
148
|
+
if (suggestions.length === 1)
|
|
149
|
+
return suggestions[0].text;
|
|
150
|
+
const lines = suggestions.map((s) => {
|
|
151
|
+
const mark = s.urgency === "urgent" ? "⚠ " : "• ";
|
|
152
|
+
return mark + s.text;
|
|
153
|
+
});
|
|
154
|
+
return "A few things across your agents:\n" + lines.join("\n");
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/advisor/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advisor (L5 ADVISE) — types.
|
|
3
|
+
*
|
|
4
|
+
* The advisor turns cross-agent observation into periodic, proactive
|
|
5
|
+
* suggestions. Its one hard problem is not being annoying, so everything is
|
|
6
|
+
* organized around the relevance bar + throttle + dedup (see engine.ts).
|
|
7
|
+
*
|
|
8
|
+
* See docs/ORCHESTRATOR_PLAN.md §5b.
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentSession } from "../integrations/types.js";
|
|
11
|
+
export type SuggestionCategory = "stuck" | "conflict" | "repeated_failure" | "cost_spike" | "ready" | "idle";
|
|
12
|
+
/** How loudly a suggestion wants to be surfaced. */
|
|
13
|
+
export type Urgency = "info" | "notice" | "urgent";
|
|
14
|
+
/** A concrete action LISA can offer to take. A suggestion without one is
|
|
15
|
+
* considered non-actionable and won't clear the relevance bar. */
|
|
16
|
+
export interface SuggestedAction {
|
|
17
|
+
label: string;
|
|
18
|
+
kind: "approve" | "cancel" | "open" | "serialize" | "look" | "dispatch";
|
|
19
|
+
/** Opaque arg the action handler needs (sessionId, cwd, …). */
|
|
20
|
+
arg?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface Suggestion {
|
|
23
|
+
/** Stable dedup key — same underlying condition → same id. */
|
|
24
|
+
id: string;
|
|
25
|
+
category: SuggestionCategory;
|
|
26
|
+
urgency: Urgency;
|
|
27
|
+
/** One-line, user-facing. Structural — no conversation content. */
|
|
28
|
+
text: string;
|
|
29
|
+
action?: SuggestedAction;
|
|
30
|
+
/** Computed relevance score (urgency × novelty × actionability). */
|
|
31
|
+
score: number;
|
|
32
|
+
/** A hash of the underlying condition; re-surface only when it changes. */
|
|
33
|
+
conditionHash: string;
|
|
34
|
+
/** When this was generated (epoch ms). */
|
|
35
|
+
ts: number;
|
|
36
|
+
}
|
|
37
|
+
/** Persisted advisor memory (~/.lisa/advisor-state.json). */
|
|
38
|
+
export interface AdvisorState {
|
|
39
|
+
/** id → last time we surfaced it + the condition hash then. */
|
|
40
|
+
surfaced: Record<string, {
|
|
41
|
+
ts: number;
|
|
42
|
+
conditionHash: string;
|
|
43
|
+
}>;
|
|
44
|
+
/** id → number of times the user dismissed it (down-weights category). */
|
|
45
|
+
dismissals: Record<string, number>;
|
|
46
|
+
/** category → dismissal count, for learning what to stop saying. */
|
|
47
|
+
categoryDismissals: Partial<Record<SuggestionCategory, number>>;
|
|
48
|
+
/** last time ANY non-urgent digest was surfaced (throttle). */
|
|
49
|
+
lastDigestAt: number;
|
|
50
|
+
/** rolling memory of (command → error count) for repeated-failure detection. */
|
|
51
|
+
errorCommandCounts: Record<string, number>;
|
|
52
|
+
}
|
|
53
|
+
export declare function emptyAdvisorState(): AdvisorState;
|
|
54
|
+
/** Input snapshot for the detectors. */
|
|
55
|
+
export interface AdvisorInput {
|
|
56
|
+
sessions: AgentSession[];
|
|
57
|
+
now: number;
|
|
58
|
+
/** Count of actionable desires with nothing running (drives "idle"). */
|
|
59
|
+
pendingDesireCount?: number;
|
|
60
|
+
}
|
|
61
|
+
/** What the engine decided to do with this run's candidates. */
|
|
62
|
+
export interface AdvisorDecision {
|
|
63
|
+
/** Cleared the bar + throttle/dedup → surface to the user. */
|
|
64
|
+
surface: Suggestion[];
|
|
65
|
+
/** Generated but suppressed (logged to journal, not shown). */
|
|
66
|
+
suppressed: Suggestion[];
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/advisor/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,MAAM,kBAAkB,GAC1B,OAAO,GACP,UAAU,GACV,kBAAkB,GAClB,YAAY,GACZ,OAAO,GACP,MAAM,CAAC;AAEX,oDAAoD;AACpD,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEnD;mEACmE;AACnE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,UAAU,CAAC;IACxE,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChE,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,oEAAoE;IACpE,kBAAkB,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,CAAC;IAChE,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED,wBAAgB,iBAAiB,IAAI,YAAY,CAQhD;AAED,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,+DAA+D;IAC/D,UAAU,EAAE,UAAU,EAAE,CAAC;CAC1B"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advisor (L5 ADVISE) — types.
|
|
3
|
+
*
|
|
4
|
+
* The advisor turns cross-agent observation into periodic, proactive
|
|
5
|
+
* suggestions. Its one hard problem is not being annoying, so everything is
|
|
6
|
+
* organized around the relevance bar + throttle + dedup (see engine.ts).
|
|
7
|
+
*
|
|
8
|
+
* See docs/ORCHESTRATOR_PLAN.md §5b.
|
|
9
|
+
*/
|
|
10
|
+
export function emptyAdvisorState() {
|
|
11
|
+
return {
|
|
12
|
+
surfaced: {},
|
|
13
|
+
dismissals: {},
|
|
14
|
+
categoryDismissals: {},
|
|
15
|
+
lastDigestAt: 0,
|
|
16
|
+
errorCommandCounts: {},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/advisor/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAsDH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,EAAE;QACd,kBAAkB,EAAE,EAAE;QACtB,YAAY,EAAE,CAAC;QACf,kBAAkB,EAAE,EAAE;KACvB,CAAC;AACJ,CAAC"}
|