@llamaventures/cli 1.19.0 → 1.22.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/AGENT_BRIEFING.md +11 -6
- package/CHANGELOG.md +49 -1
- package/README.md +12 -4
- package/README.zh-CN.md +9 -2
- package/bin/llama-mcp.mjs +133 -138
- package/bin/llama.mjs +102 -179
- package/contracts/core-api.json +4 -4
- package/contracts/required-operations.json +336 -85
- package/lib/build-manifest.json +4 -4
- package/lib/client.mjs +48 -3
- package/lib/server-compatibility.mjs +96 -0
- package/package.json +2 -2
package/AGENT_BRIEFING.md
CHANGED
|
@@ -8,6 +8,7 @@ You are not just an AI assistant. You're an **extension of a team member** — w
|
|
|
8
8
|
|
|
9
9
|
- **Your access scope is whatever your token allows.** Run `llama auth status` first; the response shows your role, identity, and active token source.
|
|
10
10
|
- **All your writes are logged.** `auth_events` and `deal_events` capture everything. Pipeline data can always be traced back to who/what changed it.
|
|
11
|
+
- **Never cue by implication.** A teammate cue creates an LC inbox item and may queue email under the user's identity. Cue a specific person only when the user explicitly authorized it in the current request. Referring to a teammate, ownership, responsibility, or writing their name is not permission.
|
|
11
12
|
- **Public surfaces stay clean.** Anything that leaves the workspace — public repos, npm packages, external artifacts, shared links — must not leak internal people, deals, private URLs, or workflow nuance.
|
|
12
13
|
- **Be direct, terse, action-oriented.** Save your words for the genuine judgment calls.
|
|
13
14
|
- **Critical when thinking, helpful when executing.** Push back on weak logic, then ship the work cleanly.
|
|
@@ -63,10 +64,11 @@ A teammate says "I just met them and heard…" or pastes a chunk of notes. Your
|
|
|
63
64
|
|
|
64
65
|
1. **Find the deal** — `llama deal search "<name>"` (Pipeline First). New name → offer to create it.
|
|
65
66
|
2. **Split what they gave you into two kinds** — this is the whole data model:
|
|
67
|
+
- **A source packet with 2+ facts, or facts + a note → ingest once.** Build a JSON object with `source`, `facts`, and optional `note`, then run `llama deal ingest <dealId> --file <packet.json>`. This is the preferred agent path: one atomic, deduplicated, retry-safe commit. Reuse an explicit `idempotencyKey` when retrying the same material. The matching MCP tool is `deal_ingest` and uses the same packet shape.
|
|
66
68
|
- **Verifiable claims → facts.** `llama deal fact add <dealId> --category <cat> --claim "…" --source "<where it came from>" --source-url <url>`. A claim someone *relayed* ("their ARR is $3M", "raised from a16z") is a fact at **unverified** trust — it's hearsay until checked. Pass `--attested` ONLY if you actually verified it against a source yourself. In raw API terms, the fact text field is `claim` (`value` is only a compatibility alias), `source` is the human-readable provenance label, and `sourceUrl` is the canonical URL.
|
|
67
69
|
- **Their judgment / impression → a note.** `llama post <dealId> "…"`. "Founder seemed evasive", "I'd lean pass", "worth a second meeting" — opinion, not fact. Attributed, never "verified".
|
|
68
70
|
- A pasted blob → pull the verifiable claims out as facts, capture their take as a note.
|
|
69
|
-
3. **Read it back before you claim it's saved.** A tool call returning `{ok:true}` is NOT proof the content is where the user will look for it.
|
|
71
|
+
3. **Read it back before you claim it's saved.** A generic tool call returning `{ok:true}` is NOT proof the content is where the user will look for it. `deal ingest` is the exception because its response is built from the rows and note block read back after commit: confirm `createdFacts`, `skippedFacts`, `note`, and `summary` in that receipt. For every other write, run `llama deal feed <dealId>` and confirm your fact/note actually appears. Never say "记好了 / saved" from a request acknowledgment alone — the #1 failure is an agent writing to the wrong surface (e.g. the brief, which is the Memo and does NOT appear in the feed) and reporting success anyway. If the authoritative ingest receipt or the feed read-back does not contain the material, fix it before reporting success.
|
|
70
72
|
- **Authorship is automatic, don't fake it.** Everything you write via CLI/MCP is recorded as "via assistant" (you're the accountable human's assistant). You can't and shouldn't make it read as human-typed — that honesty is the feature. Facts you add stay **unverified** until a human confirms them; if you pass `--attested` (only when you actually checked the source) your ceiling is **agent-verified**, never human-vouched. Only a person, signed in at the browser, can vouch. The confirmation IS the trust step — never silently mark something verified.
|
|
71
73
|
|
|
72
74
|
Why split it: facts and opinions live in different layers so the deal keeps one clean **source of truth** (facts, sourced + trust-rated) separate from people's **takes** (notes). The four layers — facts / notes / brief (AI's synthesis) / timeline — are documented in Llama Command's `docs/SCHEMA.md`.
|
|
@@ -130,8 +132,9 @@ These three look similar but land in different surfaces. Don't infer from the co
|
|
|
130
132
|
|
|
131
133
|
| You want to… | Command | Lands in |
|
|
132
134
|
|---|---|---|
|
|
135
|
+
| File a **source packet** with multiple facts and/or one note | `llama deal ingest <dealId> --file packet.json` | Facts + optional note → deal **Feed**, atomically and retry-safe |
|
|
133
136
|
| Record a **sourced, verifiable fact** | `llama deal fact add <dealId> --category <cat> --claim "…" --source "deck p3" --source-url <url>` | Facts → deal **Feed** (FACT card) + citable in the **Memo** |
|
|
134
|
-
| Leave a **comment / opinion / question / reaction** for the team | `llama post <dealId> "…"
|
|
137
|
+
| Leave a **comment / opinion / question / reaction** for the team | `llama post <dealId> "…"`; add `--cue` only after explicit permission | Posts → deal **Feed** (POST card); authorized cues create email + UI badge |
|
|
135
138
|
| Write **narrative that belongs in the IC memo** | `llama brief add-text <dealId> --heading "…" --body "…"` | Brief blocks → **Memo tab only — NOT in the Feed** |
|
|
136
139
|
|
|
137
140
|
⚠️ The trap: `brief add-text` is **not** visible in the Activity Feed. If the team should see it in the feed, use `llama post`. If it's a claim that needs a source + verification, use `llama deal fact add`. (It's `deal fact add`, not `fact-add`.)
|
|
@@ -147,7 +150,7 @@ The table below details the exact CLI for each destination.
|
|
|
147
150
|
| Insights, decisions, framework improvements | Wiki (markdown) | `llama wiki save <slug> --content "..."` (with attribution — see below) |
|
|
148
151
|
| **HTML wiki entry — standalone HTML page hosted at `/wiki/<slug>`** (sector landscape, market map, dashboard, hand-styled thesis page) | **Wiki (HTML)** | `llama wiki save <slug> --title "..." --file <path.html> --sources "..."`. Auto-detects content_type=html from extension. Public page is full-viewport sandboxed iframe takeover (no wiki chrome). Sources/status/title still required; appears in `wiki search` + backlinks. Use when the user says "deploy this HTML to wiki", "wiki 词条", "make this page a wiki entry". HTML must be self-contained (inline CSS/JS, image data URIs or external URLs) — asset bundles aren't supported on wiki yet. **Native comments + working in-page (#) anchor links are injected automatically** — readers discuss inline and the table of contents scrolls; you don't wire anything up (pages that already embed the comment widget are left as-is). |
|
|
149
152
|
| Large files (deck / PDF / transcript) | Drive deal folder | the deal's `folder_url` (from `llama deal show`) → upload via your filesystem / Drive tool |
|
|
150
|
-
| Cross-team
|
|
153
|
+
| Cross-team cues | Inbox + email | `llama post <dealId> "@<teammate> ..." --cue` — use `--cue` only after the user explicitly authorized that recipient |
|
|
151
154
|
|
|
152
155
|
### Attribution format (required for wiki writes)
|
|
153
156
|
|
|
@@ -165,8 +168,9 @@ Content. One block, one attribution. Don't mix fact and opinion in a single bloc
|
|
|
165
168
|
| Level | Type | Behaviour |
|
|
166
169
|
|---|---|---|
|
|
167
170
|
| **L0** | Reads (`search`, `show`, `list`) | Just do it. Don't announce. Integrate the result into your reply. |
|
|
168
|
-
| **L1** | Low-risk writes (timeline post, wiki append, add fact, add tag) | Do it, then tell the user **one line** afterward. |
|
|
171
|
+
| **L1** | Low-risk writes (cue-free timeline post, wiki append, add fact, add tag) | Do it, then tell the user **one line** afterward. |
|
|
169
172
|
| **L2** | Medium-risk writes (new deal, change stage, change owner, new wiki page) | Ask once: "Y/n — I'm about to do X". On yes, execute and report. Don't re-ask details. |
|
|
173
|
+
| **L2-cue** | Any assistant-authored write that resolves to a teammate cue | Require explicit permission for the resolved people and inbox/email channels. Retry the exact write with `--cue` only after yes. If the user already explicitly asked to cue them, do not ask twice. |
|
|
170
174
|
| **L3** | High-risk (delete deal, bulk change, overwrite someone else's wiki, force-push, regulatory-relevant) | Detailed explanation + explicit confirmation. Provide a dry-run / undo path when possible. |
|
|
171
175
|
|
|
172
176
|
When in doubt, lean to a higher level (more confirmation), not lower.
|
|
@@ -288,7 +292,8 @@ llama wiki restore <slug> [--lang en|zh]
|
|
|
288
292
|
|
|
289
293
|
# Timeline + posts
|
|
290
294
|
llama timeline <dealId>
|
|
291
|
-
llama post <dealId> "message"
|
|
295
|
+
llama post <dealId> "message" # cue-free note
|
|
296
|
+
llama post <dealId> "@name respond" --cue # only after explicit user approval
|
|
292
297
|
|
|
293
298
|
# Mentions inbox
|
|
294
299
|
llama mentions
|
|
@@ -298,7 +303,7 @@ Run `llama --help` for the full surface (~40 commands).
|
|
|
298
303
|
|
|
299
304
|
## MCP-native agents
|
|
300
305
|
|
|
301
|
-
If you support [MCP](https://modelcontextprotocol.io), **prefer the MCP server over parsing CLI output.** The same package ships `llama-mcp` (
|
|
306
|
+
If you support [MCP](https://modelcontextprotocol.io), **prefer the MCP server over parsing CLI output.** The same package ships `llama-mcp` (55 typed tools, identical auth chain).
|
|
302
307
|
|
|
303
308
|
Add to your MCP client config (Claude Desktop / Claude Code / Cursor / OpenClaw / Codex / etc.):
|
|
304
309
|
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,52 @@ this project adheres to [Semantic Versioning](https://semver.org).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.22.0] - 2026-07-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Consume Llama Command's server-owned CLI/MCP compatibility headers on every
|
|
13
|
+
shared API request. Recommended upgrades are surfaced on stderr at most once
|
|
14
|
+
per day, including for non-TTY coding agents, while structured stdout remains
|
|
15
|
+
unchanged.
|
|
16
|
+
- Surface hard `CLI_VERSION_UNSUPPORTED` responses with a stable error code,
|
|
17
|
+
exact upgrade command, and retry instruction.
|
|
18
|
+
|
|
19
|
+
## [1.21.0] - 2026-07-24
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- Add explicit `--cue` (CLI) and `cueAuthorized` (MCP) authorization for
|
|
23
|
+
teammate cues in posts and brief writes. Cue-free writes remain unchanged;
|
|
24
|
+
agents must obtain permission for the resolved recipients before setting it.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Agent onboarding now treats teammate cues as approval-required outbound
|
|
28
|
+
actions rather than ordinary low-risk timeline posts. Structured
|
|
29
|
+
`MENTION_APPROVAL_REQUIRED` errors retain recipient and channel context.
|
|
30
|
+
|
|
31
|
+
## [1.20.0] - 2026-07-22
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- Add `llama deal ingest <dealId> --file <packet.json>` and the matching
|
|
35
|
+
`deal_ingest` MCP tool for atomic, retry-safe source packets containing up to
|
|
36
|
+
50 facts and an optional Feed note.
|
|
37
|
+
- Add `llama pref list|add|retire|approve` and the `pref_list` / `pref_add` /
|
|
38
|
+
`pref_set_status` MCP tools for standing agent preferences (Core API 3.1.0).
|
|
39
|
+
Own user scope activates immediately; team scope requires system-admin
|
|
40
|
+
approval. Content is hard-capped at 280 chars.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- Prefer packet ingest for multi-fact agent writes; Core now canonicalizes fact
|
|
44
|
+
categories and skips exact source-aware duplicates before committing.
|
|
45
|
+
- Pin the Core API consumer contract to 3.1.0.
|
|
46
|
+
|
|
47
|
+
### Removed
|
|
48
|
+
- Remove the retired generated-persona refresh and skill-correction commands
|
|
49
|
+
from the CLI, MCP tool registry, and required Core operation inventory.
|
|
50
|
+
Llama Command keeps authenticated historical reads and 410 compatibility
|
|
51
|
+
tombstones, but new clients can no longer invoke the retired workflow.
|
|
52
|
+
- Remove `llama eval` and the `record_eval_feedback` MCP tool; the server-side
|
|
53
|
+
Golden Query Eval candidate pipeline was retired with Core API 3.0.0.
|
|
54
|
+
|
|
9
55
|
## [1.19.0] - 2026-07-16
|
|
10
56
|
|
|
11
57
|
### Added
|
|
@@ -520,7 +566,9 @@ assistant-drafted (🤖) and enforces the AI trust ceiling.
|
|
|
520
566
|
|
|
521
567
|
---
|
|
522
568
|
|
|
523
|
-
[Unreleased]: https://github.com/Llama-Ventures/llama-cli/compare/v1.
|
|
569
|
+
[Unreleased]: https://github.com/Llama-Ventures/llama-cli/compare/v1.22.0...HEAD
|
|
570
|
+
[1.22.0]: https://github.com/Llama-Ventures/llama-cli/compare/v1.21.0...v1.22.0
|
|
571
|
+
[1.21.0]: https://github.com/Llama-Ventures/llama-cli/compare/v1.20.0...v1.21.0
|
|
524
572
|
[1.18.1]: https://github.com/Llama-Ventures/llama-cli/compare/v1.18.0...v1.18.1
|
|
525
573
|
[1.18.0]: https://github.com/Llama-Ventures/llama-cli/compare/v1.17.3...v1.18.0
|
|
526
574
|
[1.17.3]: https://github.com/Llama-Ventures/llama-cli/compare/v1.17.2...v1.17.3
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<strong>The Llama Ventures CLI & MCP server.</strong><br/>
|
|
9
9
|
One package, two binaries: <code>llama</code> — the CLI for humans and scripts —
|
|
10
|
-
and <code>llama-mcp</code> — a stdio MCP server with
|
|
10
|
+
and <code>llama-mcp</code> — a stdio MCP server with 55 typed tools for any
|
|
11
11
|
MCP-native agent. Both share the same auth chain, HTTP client, and error
|
|
12
12
|
format, and talk to <a href="https://command.llamaventures.vc">command.llamaventures.vc</a>.
|
|
13
13
|
</p>
|
|
@@ -98,9 +98,11 @@ llama deal feed <dealId> # every contribution, newest first
|
|
|
98
98
|
llama activity new-deals --since 24h # recent deal creations
|
|
99
99
|
llama activity updated-deals --since 7d # meaningful updates grouped by deal
|
|
100
100
|
llama deal create "Acme AI" --source alex --deal-owner owner@llamaventures.vc --source-direction Outbound --status Interested
|
|
101
|
+
llama deal ingest <dealId> --file packet.json # atomic multi-fact + optional Feed note; retry-safe
|
|
101
102
|
llama deal fact add <dealId> --category funding --claim "Raised a seed round" --source "deck p3" --source-url https://...
|
|
102
103
|
llama deal update <dealId> status Diligence
|
|
103
104
|
llama post <dealId> "note body"
|
|
105
|
+
llama post <dealId> "@name please respond" --cue # only after explicit approval
|
|
104
106
|
llama brief add-text <dealId> --heading "..." --body "..."
|
|
105
107
|
llama wiki search "<query>"
|
|
106
108
|
llama wiki save <slug> --title "..." --content "..."
|
|
@@ -113,6 +115,13 @@ Status vocabulary — `Interested`: tracked before any contact ·
|
|
|
113
115
|
signal exists. `sourceDirection` is separate: `Inbound` came to the firm,
|
|
114
116
|
`Outbound` we reached out first.
|
|
115
117
|
|
|
118
|
+
For a deck, meeting note, email, or research packet, prefer `deal ingest` over a
|
|
119
|
+
loop of `deal fact add` calls. The JSON object accepts `source`, up to 50
|
|
120
|
+
`facts`, an optional `note`, and an optional `idempotencyKey`. The server commits
|
|
121
|
+
the packet atomically, maps common category aliases into the canonical taxonomy,
|
|
122
|
+
and skips exact source-aware duplicates. `deal fact add` remains the simple path
|
|
123
|
+
for one fact.
|
|
124
|
+
|
|
116
125
|
Facts use `claim` for the fact text. `source` is a readable provenance label
|
|
117
126
|
and `sourceUrl` is the canonical evidence URL; both round-trip from the API.
|
|
118
127
|
For deal owners, use an exact `/api/field-options` `dealOwner` value, a user
|
|
@@ -129,12 +138,11 @@ reference (100+ commands). Deletes are soft and audit-logged everywhere.
|
|
|
129
138
|
| `Error[UNAUTHORIZED]` | Server rejected the credentials | Token revoked / expired / wrong account |
|
|
130
139
|
|
|
131
140
|
The MCP server returns the same prefixes inside `isError: true` content.
|
|
132
|
-
Authenticated calls send bounded, content-redacted telemetry to Command
|
|
133
|
-
`llama eval good|bad --last` turns real searches into eval feedback.
|
|
141
|
+
Authenticated calls send bounded, content-redacted telemetry to Command.
|
|
134
142
|
|
|
135
143
|
## MCP server
|
|
136
144
|
|
|
137
|
-
`llama-mcp` is a stdio Model Context Protocol server exposing
|
|
145
|
+
`llama-mcp` is a stdio Model Context Protocol server exposing 55 typed tools
|
|
138
146
|
that mirror the most-used CLI surface. Every tool is named and scoped — there
|
|
139
147
|
is deliberately no generic API passthrough. Auth is identical to the CLI's
|
|
140
148
|
chain. For the exact live list, pipe `tools/list` through it:
|
package/README.zh-CN.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<strong>Llama Ventures 的 CLI 与 MCP server。</strong><br/>
|
|
9
9
|
一个包、两个可执行文件:<code>llama</code>——给人和脚本用的 CLI;
|
|
10
|
-
<code>llama-mcp</code>——带
|
|
10
|
+
<code>llama-mcp</code>——带 55 个类型化工具的 stdio MCP server,任何
|
|
11
11
|
MCP 原生 agent 都能接。两者共享同一认证链、同一 HTTP 客户端、同一错误格式,
|
|
12
12
|
连接 <a href="https://command.llamaventures.vc">command.llamaventures.vc</a>。
|
|
13
13
|
</p>
|
|
@@ -120,9 +120,11 @@ llama deal feed <dealId> # 该 deal 的全部贡献,最新在前
|
|
|
120
120
|
llama activity new-deals --since 24h # 最近新建的 deal
|
|
121
121
|
llama activity updated-deals --since 7d # 按 deal 聚合的实质更新
|
|
122
122
|
llama deal create "Acme AI" --source alex --deal-owner owner@llamaventures.vc --source-direction Outbound --status Interested
|
|
123
|
+
llama deal ingest <dealId> --file packet.json # 多条 facts + 可选 Feed note,一次提交且可安全重试
|
|
123
124
|
llama deal fact add <dealId> --category funding --claim "Raised a seed round" --source "deck p3" --source-url https://...
|
|
124
125
|
llama deal update <dealId> status Diligence
|
|
125
126
|
llama post <dealId> "备注内容"
|
|
127
|
+
llama post <dealId> "@name 请回复" --cue # 仅在用户明确授权后使用
|
|
126
128
|
llama brief add-text <dealId> --heading "..." --body "..."
|
|
127
129
|
llama wiki search "<查询词>"
|
|
128
130
|
llama wiki save <slug> --title "..." --content "..."
|
|
@@ -134,6 +136,11 @@ Status 语义——`Interested`:接触前先记录关注 · `Outreached`:已
|
|
|
134
136
|
尚无回应 · `Sourced`:已有真实关系信号。`sourceDirection` 是独立维度:
|
|
135
137
|
`Inbound` 流入,`Outbound` 我们主动。
|
|
136
138
|
|
|
139
|
+
处理 deck、会议笔记、邮件或研究材料时,优先使用 `deal ingest`,不要循环调用
|
|
140
|
+
`deal fact add`。JSON 对象支持 `source`、最多 50 条 `facts`、可选 `note` 和可选
|
|
141
|
+
`idempotencyKey`。服务端会原子提交整个材料包、把常见 category 别名归一到固定分类,
|
|
142
|
+
并跳过来源一致的精确重复。只有真正的单条事实才用 `deal fact add`。
|
|
143
|
+
|
|
137
144
|
Facts 的正文用 `claim`。`source` 是人可读来源标签,`sourceUrl` 是 canonical
|
|
138
145
|
证据 URL;两者都会从 API 回显。`dealOwner` 请用 `/api/field-options`
|
|
139
146
|
里的精确 `dealOwner` 值、用户邮箱,或数字 user id。
|
|
@@ -153,7 +160,7 @@ MCP server 在 `isError: true` 内容里返回相同前缀。认证请求会向
|
|
|
153
160
|
|
|
154
161
|
## MCP server
|
|
155
162
|
|
|
156
|
-
`llama-mcp` 是 stdio MCP server,
|
|
163
|
+
`llama-mcp` 是 stdio MCP server,55 个类型化工具镜像 CLI 最常用的命令面。
|
|
157
164
|
每个工具具名、有边界——**故意不提供**通用 API passthrough。认证链与 CLI
|
|
158
165
|
完全一致。精确工具清单以 `tools/list` 为准:
|
|
159
166
|
|
package/bin/llama-mcp.mjs
CHANGED
|
@@ -15,7 +15,6 @@ import { z } from "zod";
|
|
|
15
15
|
import {
|
|
16
16
|
getAuthHeaders,
|
|
17
17
|
getBaseUrl,
|
|
18
|
-
getLastAgentEvent,
|
|
19
18
|
readBriefing,
|
|
20
19
|
request,
|
|
21
20
|
requestSse,
|
|
@@ -84,20 +83,6 @@ function splitSources(value) {
|
|
|
84
83
|
.filter(Boolean);
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
function expectedIds(value) {
|
|
88
|
-
const expected = { dealIds: [], wikiSlugs: [], raw: [] };
|
|
89
|
-
if (!value) return expected;
|
|
90
|
-
const items = Array.isArray(value) ? value : String(value).split(",");
|
|
91
|
-
for (const item of items.map((s) => String(s).trim()).filter(Boolean)) {
|
|
92
|
-
const [kind, ...rest] = item.split(":");
|
|
93
|
-
const id = rest.join(":").trim();
|
|
94
|
-
if (kind === "deal" && id) expected.dealIds.push(id);
|
|
95
|
-
else if ((kind === "wiki" || kind === "slug") && id) expected.wikiSlugs.push(id);
|
|
96
|
-
else expected.raw.push(item);
|
|
97
|
-
}
|
|
98
|
-
return expected;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
86
|
function buildEnrichmentAgentMessage(args = {}) {
|
|
102
87
|
if (args.message) return String(args.message);
|
|
103
88
|
const sources = splitSources(args.sources) ?? [
|
|
@@ -176,7 +161,7 @@ async function runDealAgentTool({ dealId, message, title = "MCP agent run" }) {
|
|
|
176
161
|
// full-array PUTs (no POST), so we GET current blocks, prepend the new
|
|
177
162
|
// one (matches UI default since 2026-05-03), and PUT the merged array.
|
|
178
163
|
// Server stamps identity meta on PUT; we don't send any.
|
|
179
|
-
async function addBriefBlock(dealId, block) {
|
|
164
|
+
async function addBriefBlock(dealId, block, cueAuthorized = false) {
|
|
180
165
|
try {
|
|
181
166
|
const id = globalThis.crypto.randomUUID();
|
|
182
167
|
const cur = await request("GET", `/api/deals/${encodeURIComponent(dealId)}/blocks`);
|
|
@@ -184,7 +169,7 @@ async function addBriefBlock(dealId, block) {
|
|
|
184
169
|
const result = await request(
|
|
185
170
|
"PUT",
|
|
186
171
|
`/api/deals/${encodeURIComponent(dealId)}/blocks`,
|
|
187
|
-
{ blocks: [{ id, ...block }, ...existing] }
|
|
172
|
+
{ blocks: [{ id, ...block }, ...existing], cue_authorized: cueAuthorized === true }
|
|
188
173
|
);
|
|
189
174
|
const text = JSON.stringify(
|
|
190
175
|
{ ok: result?.ok ?? true, id, count: result?.count ?? existing.length + 1 },
|
|
@@ -257,41 +242,6 @@ server.registerTool(
|
|
|
257
242
|
}
|
|
258
243
|
);
|
|
259
244
|
|
|
260
|
-
server.registerTool(
|
|
261
|
-
"record_eval_feedback",
|
|
262
|
-
{
|
|
263
|
-
description:
|
|
264
|
-
"Mark the latest llama CLI/MCP result as good/bad or add a real query " +
|
|
265
|
-
"to the Golden Query Eval candidate pool. Use when the user says a " +
|
|
266
|
-
"Llama Command search/result was right, wrong, missing a source, or should be regression-tested.",
|
|
267
|
-
inputSchema: {
|
|
268
|
-
action: z.enum(["good", "bad", "add"]).describe("feedback action"),
|
|
269
|
-
eventId: z.number().optional().describe("agent_client_events id; defaults to latest local event"),
|
|
270
|
-
query: z.string().optional().describe("required for manual add when no source event is available"),
|
|
271
|
-
surface: z.string().optional().describe("deal, wiki, activity, people, or manual"),
|
|
272
|
-
expect: z
|
|
273
|
-
.union([z.string(), z.array(z.string())])
|
|
274
|
-
.optional()
|
|
275
|
-
.describe("expected ids like wiki:llamaos-weekly-2026-06-17 or deal:<uuid>"),
|
|
276
|
-
reason: z.string().optional().describe("why this was good/bad or should be tracked"),
|
|
277
|
-
privacyLevel: z.string().optional().describe("default internal"),
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
async ({ action, eventId, query, surface, expect, reason, privacyLevel }) => {
|
|
281
|
-
const last = getLastAgentEvent();
|
|
282
|
-
const body = {
|
|
283
|
-
action,
|
|
284
|
-
eventId: eventId ?? last?.lastEventId,
|
|
285
|
-
query,
|
|
286
|
-
surface: surface ?? last?.lastSurface,
|
|
287
|
-
expected: expectedIds(expect),
|
|
288
|
-
reason,
|
|
289
|
-
privacyLevel: privacyLevel ?? "internal",
|
|
290
|
-
};
|
|
291
|
-
return callApi("POST", "/api/agent/eval-feedback", body);
|
|
292
|
-
}
|
|
293
|
-
);
|
|
294
|
-
|
|
295
245
|
server.registerTool(
|
|
296
246
|
"skills_search",
|
|
297
247
|
{
|
|
@@ -311,6 +261,60 @@ server.registerTool(
|
|
|
311
261
|
}
|
|
312
262
|
);
|
|
313
263
|
|
|
264
|
+
server.registerTool(
|
|
265
|
+
"pref_list",
|
|
266
|
+
{
|
|
267
|
+
description:
|
|
268
|
+
"List standing agent preferences (team scope + the caller's user scope). " +
|
|
269
|
+
"These are injected into every server-side agent turn. Use status=proposed " +
|
|
270
|
+
"to review pending proposals awaiting approval.",
|
|
271
|
+
inputSchema: {
|
|
272
|
+
status: z.enum(["active", "proposed", "retired", "all"]).optional()
|
|
273
|
+
.describe("filter; defaults to active"),
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
async ({ status }) => {
|
|
277
|
+
const params = new URLSearchParams();
|
|
278
|
+
if (status) params.set("status", status);
|
|
279
|
+
return callApi("GET", `/api/agent/preferences${params.toString() ? `?${params}` : ""}`);
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
server.registerTool(
|
|
284
|
+
"pref_add",
|
|
285
|
+
{
|
|
286
|
+
description:
|
|
287
|
+
"Save a standing preference so every Llama agent follows it from the next " +
|
|
288
|
+
"turn on. Use when the user states a durable way they want agents to work " +
|
|
289
|
+
"(style, workflow, defaults). Content is hard-capped at 280 chars — if it " +
|
|
290
|
+
"does not fit, it is a procedure and belongs in a skill. Team scope needs " +
|
|
291
|
+
"system-admin approval; own user scope activates immediately.",
|
|
292
|
+
inputSchema: {
|
|
293
|
+
key: z.string().describe("short slug, e.g. reply-style.conclusion-first"),
|
|
294
|
+
content: z.string().describe("the preference, max 280 chars"),
|
|
295
|
+
scope: z.enum(["user", "team"]).optional().describe("default user (the caller)"),
|
|
296
|
+
evidence: z.string().optional().describe("what prompted this (run, correction)"),
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
async ({ key, content, scope, evidence }) =>
|
|
300
|
+
callApi("POST", "/api/agent/preferences", { key, content, scope, evidence })
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
server.registerTool(
|
|
304
|
+
"pref_set_status",
|
|
305
|
+
{
|
|
306
|
+
description:
|
|
307
|
+
"Approve (activate) or retire a standing preference by id. Own user scope " +
|
|
308
|
+
"is self-service; team scope requires a system admin.",
|
|
309
|
+
inputSchema: {
|
|
310
|
+
id: z.number().describe("preference id from pref_list"),
|
|
311
|
+
status: z.enum(["active", "retired"]).describe("new status"),
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
async ({ id, status }) =>
|
|
315
|
+
callApi("PATCH", `/api/agent/preferences/${encodeURIComponent(String(id))}`, { status })
|
|
316
|
+
);
|
|
317
|
+
|
|
314
318
|
server.registerTool(
|
|
315
319
|
"skills_read",
|
|
316
320
|
{
|
|
@@ -509,6 +513,50 @@ server.registerTool(
|
|
|
509
513
|
// Deal facts (research substrate + trust ladder)
|
|
510
514
|
// ============================================================
|
|
511
515
|
|
|
516
|
+
server.registerTool(
|
|
517
|
+
"deal_ingest",
|
|
518
|
+
{
|
|
519
|
+
description:
|
|
520
|
+
"Preferred write tool when one source yields multiple facts, or facts plus a Feed note. " +
|
|
521
|
+
"Commits the packet atomically, canonicalizes fact categories, skips exact source-aware " +
|
|
522
|
+
"duplicates, and is safe to retry with the same idempotencyKey. Use deal_fact_add only " +
|
|
523
|
+
"for a genuinely single fact. Canonical categories: company_basics, team, product, market, " +
|
|
524
|
+
"financials, fundraise, risk, milestone, meta.",
|
|
525
|
+
inputSchema: {
|
|
526
|
+
dealId: z.string(),
|
|
527
|
+
idempotencyKey: z
|
|
528
|
+
.string()
|
|
529
|
+
.optional()
|
|
530
|
+
.describe("Stable key for retries. If omitted, the server derives one from packet content."),
|
|
531
|
+
source: z
|
|
532
|
+
.object({
|
|
533
|
+
kind: z.enum(["deck", "web", "meeting_note", "email", "human", "agent_inference"]).optional(),
|
|
534
|
+
title: z.string().optional(),
|
|
535
|
+
url: z.string().optional(),
|
|
536
|
+
contentHash: z.string().optional(),
|
|
537
|
+
})
|
|
538
|
+
.optional(),
|
|
539
|
+
facts: z
|
|
540
|
+
.array(z.object({
|
|
541
|
+
category: z.string(),
|
|
542
|
+
claim: z.string(),
|
|
543
|
+
source: z.string().optional(),
|
|
544
|
+
sourceUrl: z.string().optional(),
|
|
545
|
+
sourceKind: z.enum(["deck", "web", "meeting_note", "email", "human", "agent_inference"]).optional(),
|
|
546
|
+
confidence: z.enum(["high", "medium", "low"]).optional(),
|
|
547
|
+
attested: z.boolean().optional(),
|
|
548
|
+
}))
|
|
549
|
+
.max(50)
|
|
550
|
+
.optional(),
|
|
551
|
+
note: z.string().optional().describe("Opinion, impression, or context to add to the deal Feed."),
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
async ({ dealId, ...packet }) => {
|
|
555
|
+
// @core-api-operation POST /api/deals/{dealId}/ingest
|
|
556
|
+
return callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/ingest`, packet);
|
|
557
|
+
}
|
|
558
|
+
);
|
|
559
|
+
|
|
512
560
|
server.registerTool(
|
|
513
561
|
"deal_fact_list",
|
|
514
562
|
{
|
|
@@ -613,7 +661,7 @@ server.registerTool(
|
|
|
613
661
|
"accountable person, null only for principal-less system writes) and `agent` " +
|
|
614
662
|
"(the assistant/system label when an AI did the writing, null when a human " +
|
|
615
663
|
"typed it) so you can tell human-typed from assistant-drafted. The AI's " +
|
|
616
|
-
"regenerable brief
|
|
664
|
+
"regenerable brief synthesis is NOT here (that's the Memo) — only " +
|
|
617
665
|
"facts + discussion notes. Each item: kind (fact|note), ts, who, agent, " +
|
|
618
666
|
"origin, text, and for facts: source + trust rung + category.",
|
|
619
667
|
inputSchema: {
|
|
@@ -633,10 +681,13 @@ server.registerTool(
|
|
|
633
681
|
dealId: z.string(),
|
|
634
682
|
heading: z.string().optional().describe("optional block heading"),
|
|
635
683
|
body: z.string().describe("markdown body"),
|
|
684
|
+
cueAuthorized: z.boolean().optional().describe(
|
|
685
|
+
"true only after the user explicitly approved every resolved cue recipient",
|
|
686
|
+
),
|
|
636
687
|
},
|
|
637
688
|
},
|
|
638
|
-
async ({ dealId, heading, body }) =>
|
|
639
|
-
addBriefBlock(dealId, { type: "text", heading: heading ?? "", body })
|
|
689
|
+
async ({ dealId, heading, body, cueAuthorized }) =>
|
|
690
|
+
addBriefBlock(dealId, { type: "text", heading: heading ?? "", body }, cueAuthorized)
|
|
640
691
|
);
|
|
641
692
|
|
|
642
693
|
server.registerTool(
|
|
@@ -648,10 +699,13 @@ server.registerTool(
|
|
|
648
699
|
dealId: z.string(),
|
|
649
700
|
url: z.string(),
|
|
650
701
|
label: z.string().optional().describe("optional human-readable label"),
|
|
702
|
+
cueAuthorized: z.boolean().optional().describe(
|
|
703
|
+
"true only after the user explicitly approved every resolved cue recipient",
|
|
704
|
+
),
|
|
651
705
|
},
|
|
652
706
|
},
|
|
653
|
-
async ({ dealId, url, label }) =>
|
|
654
|
-
addBriefBlock(dealId, { type: "link", url, label: label ?? "" })
|
|
707
|
+
async ({ dealId, url, label, cueAuthorized }) =>
|
|
708
|
+
addBriefBlock(dealId, { type: "link", url, label: label ?? "" }, cueAuthorized)
|
|
655
709
|
);
|
|
656
710
|
|
|
657
711
|
server.registerTool(
|
|
@@ -664,10 +718,13 @@ server.registerTool(
|
|
|
664
718
|
tone: z.string().describe("insight | warning | info | success"),
|
|
665
719
|
heading: z.string().optional(),
|
|
666
720
|
body: z.string(),
|
|
721
|
+
cueAuthorized: z.boolean().optional().describe(
|
|
722
|
+
"true only after the user explicitly approved every resolved cue recipient",
|
|
723
|
+
),
|
|
667
724
|
},
|
|
668
725
|
},
|
|
669
|
-
async ({ dealId, tone, heading, body }) =>
|
|
670
|
-
addBriefBlock(dealId, { type: "callout", tone, heading: heading ?? "", body })
|
|
726
|
+
async ({ dealId, tone, heading, body, cueAuthorized }) =>
|
|
727
|
+
addBriefBlock(dealId, { type: "callout", tone, heading: heading ?? "", body }, cueAuthorized)
|
|
671
728
|
);
|
|
672
729
|
|
|
673
730
|
server.registerTool(
|
|
@@ -690,9 +747,12 @@ server.registerTool(
|
|
|
690
747
|
locked: z.boolean().optional(),
|
|
691
748
|
hidden: z.boolean().optional(),
|
|
692
749
|
sourceSection: z.string().optional(),
|
|
750
|
+
cueAuthorized: z.boolean().optional().describe(
|
|
751
|
+
"true only after the user explicitly approved every resolved cue recipient",
|
|
752
|
+
),
|
|
693
753
|
},
|
|
694
754
|
},
|
|
695
|
-
async ({ dealId, blockId, heading, body, url, label, description, tone, locked, hidden, sourceSection }) => {
|
|
755
|
+
async ({ dealId, blockId, heading, body, url, label, description, tone, locked, hidden, sourceSection, cueAuthorized }) => {
|
|
696
756
|
const patch = {};
|
|
697
757
|
for (const [k, v] of Object.entries({ heading, body, url, label, description, tone })) {
|
|
698
758
|
if (v !== undefined) patch[k] = v;
|
|
@@ -702,6 +762,7 @@ server.registerTool(
|
|
|
702
762
|
if (hidden !== undefined) meta.hidden = hidden;
|
|
703
763
|
if (sourceSection !== undefined) meta.sourceSection = sourceSection;
|
|
704
764
|
if (Object.keys(meta).length > 0) patch.meta = meta;
|
|
765
|
+
if (cueAuthorized === true) patch.cue_authorized = true;
|
|
705
766
|
return callApi(
|
|
706
767
|
"PATCH",
|
|
707
768
|
`/api/deals/${encodeURIComponent(dealId)}/blocks/${encodeURIComponent(blockId)}`,
|
|
@@ -940,16 +1001,22 @@ server.registerTool(
|
|
|
940
1001
|
"post",
|
|
941
1002
|
{
|
|
942
1003
|
description:
|
|
943
|
-
"Post a message to a deal's timeline.
|
|
944
|
-
"
|
|
945
|
-
"
|
|
1004
|
+
"Post a message to a deal's timeline. Cue-free posts are autonomous. " +
|
|
1005
|
+
"Explicit or implicit teammate cues create inbox/email notifications and require " +
|
|
1006
|
+
"cueAuthorized=true only after explicit user permission.",
|
|
946
1007
|
inputSchema: {
|
|
947
1008
|
dealId: z.string(),
|
|
948
1009
|
message: z.string(),
|
|
1010
|
+
cueAuthorized: z.boolean().optional().describe(
|
|
1011
|
+
"true only after the user explicitly approved cueing every resolved recipient",
|
|
1012
|
+
),
|
|
949
1013
|
},
|
|
950
1014
|
},
|
|
951
|
-
async ({ dealId, message }) =>
|
|
952
|
-
callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/posts`, {
|
|
1015
|
+
async ({ dealId, message, cueAuthorized }) =>
|
|
1016
|
+
callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/posts`, {
|
|
1017
|
+
message,
|
|
1018
|
+
cue_authorized: cueAuthorized === true,
|
|
1019
|
+
})
|
|
953
1020
|
);
|
|
954
1021
|
|
|
955
1022
|
// ============================================================
|
|
@@ -993,63 +1060,7 @@ server.registerTool(
|
|
|
993
1060
|
);
|
|
994
1061
|
|
|
995
1062
|
// ============================================================
|
|
996
|
-
//
|
|
997
|
-
// ============================================================
|
|
998
|
-
|
|
999
|
-
server.registerTool(
|
|
1000
|
-
"skill_correction_list",
|
|
1001
|
-
{
|
|
1002
|
-
description:
|
|
1003
|
-
"List the recorded corrections (long-term rules) for a persona/skill. These shape how " +
|
|
1004
|
-
"that persona's analysis is generated.",
|
|
1005
|
-
inputSchema: {
|
|
1006
|
-
skillSlug: z.string(),
|
|
1007
|
-
includeDeleted: z.boolean().optional(),
|
|
1008
|
-
},
|
|
1009
|
-
},
|
|
1010
|
-
async ({ skillSlug, includeDeleted }) => {
|
|
1011
|
-
const params = new URLSearchParams({ skill: skillSlug });
|
|
1012
|
-
if (includeDeleted) params.set("include_deleted", "1");
|
|
1013
|
-
return callApi("GET", `/api/skill-corrections?${params}`);
|
|
1014
|
-
}
|
|
1015
|
-
);
|
|
1016
|
-
|
|
1017
|
-
server.registerTool(
|
|
1018
|
-
"skill_correction_add",
|
|
1019
|
-
{
|
|
1020
|
-
description:
|
|
1021
|
-
"Record a long-term correction rule for a persona/skill (e.g. 'always check burn multiple " +
|
|
1022
|
-
"before commenting on efficiency'). ALWAYS reconfirm the distilled rule with the user before " +
|
|
1023
|
-
"calling — this changes how the persona behaves going forward. Optionally tie it to the deal/" +
|
|
1024
|
-
"block where it came up.",
|
|
1025
|
-
inputSchema: {
|
|
1026
|
-
skillSlug: z.string(),
|
|
1027
|
-
correctionText: z.string(),
|
|
1028
|
-
dealUuid: z.string().optional(),
|
|
1029
|
-
blockId: z.string().optional(),
|
|
1030
|
-
},
|
|
1031
|
-
},
|
|
1032
|
-
async ({ skillSlug, correctionText, dealUuid, blockId }) =>
|
|
1033
|
-
callApi("POST", "/api/skill-corrections", {
|
|
1034
|
-
skill_slug: skillSlug,
|
|
1035
|
-
correction_text: correctionText,
|
|
1036
|
-
triggered_in_deal_uuid: dealUuid ?? null,
|
|
1037
|
-
triggered_in_block_id: blockId ?? null,
|
|
1038
|
-
})
|
|
1039
|
-
);
|
|
1040
|
-
|
|
1041
|
-
server.registerTool(
|
|
1042
|
-
"skill_correction_delete",
|
|
1043
|
-
{
|
|
1044
|
-
description: "Soft-delete a recorded skill correction by id.",
|
|
1045
|
-
inputSchema: { id: z.union([z.string(), z.number()]) },
|
|
1046
|
-
},
|
|
1047
|
-
async ({ id }) =>
|
|
1048
|
-
callApi("DELETE", `/api/skill-corrections/${encodeURIComponent(String(id))}`)
|
|
1049
|
-
);
|
|
1050
|
-
|
|
1051
|
-
// ============================================================
|
|
1052
|
-
// Brief / persona refresh (signal-driven re-evaluation)
|
|
1063
|
+
// Brief refresh (signal-driven re-evaluation)
|
|
1053
1064
|
// ============================================================
|
|
1054
1065
|
|
|
1055
1066
|
server.registerTool(
|
|
@@ -1070,22 +1081,6 @@ server.registerTool(
|
|
|
1070
1081
|
)
|
|
1071
1082
|
);
|
|
1072
1083
|
|
|
1073
|
-
server.registerTool(
|
|
1074
|
-
"deal_refresh_persona",
|
|
1075
|
-
{
|
|
1076
|
-
description:
|
|
1077
|
-
"Regenerate one persona's analysis section for a deal. persona = a server-configured " +
|
|
1078
|
-
"persona key (unknown keys are rejected; discover the roster via agent_bootstrap). " +
|
|
1079
|
-
"Returns a runId (or null if debounced).",
|
|
1080
|
-
inputSchema: {
|
|
1081
|
-
dealId: z.string(),
|
|
1082
|
-
persona: z.string(),
|
|
1083
|
-
},
|
|
1084
|
-
},
|
|
1085
|
-
async ({ dealId, persona }) =>
|
|
1086
|
-
callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/refresh-persona`, { persona })
|
|
1087
|
-
);
|
|
1088
|
-
|
|
1089
1084
|
server.registerTool(
|
|
1090
1085
|
"deal_agent_run",
|
|
1091
1086
|
{
|