@llamaventures/cli 1.18.0 → 1.19.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 CHANGED
@@ -45,6 +45,7 @@ Any time the user mentions a company name or founder name:
45
45
  - Use `--status Outreached` when we only contacted/logged the company and have no response or effective relationship yet.
46
46
  - Use `--status Sourced` only once there is a response, intro, meeting, or another real relationship signal.
47
47
  - Also set `--source-direction Inbound` if the deal came into the firm; set `--source-direction Outbound` if Llama found/listed/reached out first.
48
+ - If assigning an owner at create time, use `--deal-owner` with an exact `/api/field-options` value, email, or user id. Do not guess from a first name.
48
49
  4. If user gives you new facts (status / valuation / founder note) → `llama deal update` immediately, tell the user **one line** afterward.
49
50
 
50
51
  Don't:
@@ -62,7 +63,7 @@ A teammate says "I just met them and heard…" or pastes a chunk of notes. Your
62
63
 
63
64
  1. **Find the deal** — `llama deal search "<name>"` (Pipeline First). New name → offer to create it.
64
65
  2. **Split what they gave you into two kinds** — this is the whole data model:
65
- - **Verifiable claims → facts.** `llama deal fact add <dealId> --category <cat> --claim "…" --source "<where it came from>"`. 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.
66
+ - **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.
66
67
  - **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".
67
68
  - A pasted blob → pull the verifiable claims out as facts, capture their take as a note.
68
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. After filing, run `llama deal feed <dealId>` and confirm your fact/note actually appears, THEN tell the user in plain language what you recorded and where. Never say "记好了 / saved" from the return value 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 it's not in the feed, you routed it wrong — fix it.
@@ -129,7 +130,7 @@ These three look similar but land in different surfaces. Don't infer from the co
129
130
 
130
131
  | You want to… | Command | Lands in |
131
132
  |---|---|---|
132
- | Record a **sourced, verifiable fact** | `llama deal fact add <dealId> --category <cat> --claim "…" --source <url>` | Facts → deal **Feed** (FACT card) + citable in the **Memo** |
133
+ | 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** |
133
134
  | Leave a **comment / opinion / question / reaction** for the team | `llama post <dealId> "…"` (`@name` to notify) | Posts → deal **Feed** (POST card); `@mention` fires email + UI badge |
134
135
  | Write **narrative that belongs in the IC memo** | `llama brief add-text <dealId> --heading "…" --body "…"` | Brief blocks → **Memo tab only — NOT in the Feed** |
135
136
 
@@ -209,8 +210,8 @@ llama deal list [--owner ...] [--status ...]
209
210
 
210
211
  # Pipeline — write
211
212
  llama deal create "Company" --description "..." --source-direction Outbound --status Interested
212
- llama deal create "Company" --description "..." --source-direction Outbound --status Outreached
213
- llama deal create "Company" --description "..." --source-direction Inbound --status Sourced
213
+ llama deal create "Company" --description "..." --source-direction Outbound --status Outreached --deal-owner "owner@llamaventures.vc"
214
+ llama deal create "Company" --description "..." --source-direction Inbound --status Sourced --deal-owner "Exact Name"
214
215
  llama deal update <dealId> <field> <value>
215
216
  # writable: status theirStage stage notes dealOwner source sourceDirection description website
216
217
  # location founders proposedAmount roundSize valuation sector subsector
package/CHANGELOG.md CHANGED
@@ -6,6 +6,26 @@ this project adheres to [Semantic Versioning](https://semver.org).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.19.0] - 2026-07-16
10
+
11
+ ### Added
12
+ - Pin each CLI artifact to the exact versioned Llama Core OpenAPI contract,
13
+ source commit, and complete source-derived operation inventory.
14
+ - Verify the contract identity and required Core operations during tests,
15
+ packaging, clean tarball installation, and release-gate validation.
16
+
17
+ ### Changed
18
+ - Treat Core HTTP as the CLI's only backend boundary and expose contract/build
19
+ provenance through `llama version --json` for independent rollback audits.
20
+
21
+ ## [1.18.1] - 2026-07-09
22
+
23
+ ### Changed
24
+ - Clarified the Command facts write contract across CLI, MCP, and agent docs:
25
+ `claim` is canonical, `value` remains a compatibility alias, `sourceUrl`
26
+ round-trips as provenance, and categories are free text. `deal create` now
27
+ forwards `--deal-owner` using name, email, or user id.
28
+
9
29
  ## [1.18.0] - 2026-07-06
10
30
 
11
31
  ### Added
@@ -500,7 +520,8 @@ assistant-drafted (🤖) and enforces the AI trust ceiling.
500
520
 
501
521
  ---
502
522
 
503
- [Unreleased]: https://github.com/Llama-Ventures/llama-cli/compare/v1.18.0...HEAD
523
+ [Unreleased]: https://github.com/Llama-Ventures/llama-cli/compare/v1.18.1...HEAD
524
+ [1.18.1]: https://github.com/Llama-Ventures/llama-cli/compare/v1.18.0...v1.18.1
504
525
  [1.18.0]: https://github.com/Llama-Ventures/llama-cli/compare/v1.17.3...v1.18.0
505
526
  [1.17.3]: https://github.com/Llama-Ventures/llama-cli/compare/v1.17.2...v1.17.3
506
527
  [1.17.2]: https://github.com/Llama-Ventures/llama-cli/compare/v1.17.1...v1.17.2
package/README.md CHANGED
@@ -42,6 +42,7 @@
42
42
  ```bash
43
43
  npm i -g @llamaventures/cli # Node 18+; also puts `llama-mcp` on your PATH
44
44
  llama --version
45
+ llama version --json # package, source commit, and pinned Core API contract
45
46
  llama auth status # round-trips against /api/me
46
47
  ```
47
48
 
@@ -96,7 +97,8 @@ llama deal show <dealId>
96
97
  llama deal feed <dealId> # every contribution, newest first
97
98
  llama activity new-deals --since 24h # recent deal creations
98
99
  llama activity updated-deals --since 7d # meaningful updates grouped by deal
99
- llama deal create "Acme AI" --source alex --source-direction Outbound --status Interested
100
+ llama deal create "Acme AI" --source alex --deal-owner owner@llamaventures.vc --source-direction Outbound --status Interested
101
+ llama deal fact add <dealId> --category funding --claim "Raised a seed round" --source "deck p3" --source-url https://...
100
102
  llama deal update <dealId> status Diligence
101
103
  llama post <dealId> "note body"
102
104
  llama brief add-text <dealId> --heading "..." --body "..."
@@ -111,6 +113,11 @@ Status vocabulary — `Interested`: tracked before any contact ·
111
113
  signal exists. `sourceDirection` is separate: `Inbound` came to the firm,
112
114
  `Outbound` we reached out first.
113
115
 
116
+ Facts use `claim` for the fact text. `source` is a readable provenance label
117
+ and `sourceUrl` is the canonical evidence URL; both round-trip from the API.
118
+ For deal owners, use an exact `/api/field-options` `dealOwner` value, a user
119
+ email, or a numeric user id.
120
+
114
121
  Run `llama --help` for the group index, `llama help all` for the full
115
122
  reference (100+ commands). Deletes are soft and audit-logged everywhere.
116
123
 
package/README.zh-CN.md CHANGED
@@ -67,6 +67,7 @@ Pitch 完成后档案自动进我们团队的 inbox,我们会主动联系你
67
67
  ```bash
68
68
  npm i -g @llamaventures/cli # Node 18+;llama-mcp 也会一起装上 PATH
69
69
  llama --version
70
+ llama version --json # 包版本、源码 commit 与固定的 Core API 契约
70
71
  llama auth status # 会跑一次 /api/me 验证
71
72
  ```
72
73
 
@@ -118,7 +119,8 @@ llama deal show <dealId>
118
119
  llama deal feed <dealId> # 该 deal 的全部贡献,最新在前
119
120
  llama activity new-deals --since 24h # 最近新建的 deal
120
121
  llama activity updated-deals --since 7d # 按 deal 聚合的实质更新
121
- llama deal create "Acme AI" --source alex --source-direction Outbound --status Interested
122
+ llama deal create "Acme AI" --source alex --deal-owner owner@llamaventures.vc --source-direction Outbound --status Interested
123
+ llama deal fact add <dealId> --category funding --claim "Raised a seed round" --source "deck p3" --source-url https://...
122
124
  llama deal update <dealId> status Diligence
123
125
  llama post <dealId> "备注内容"
124
126
  llama brief add-text <dealId> --heading "..." --body "..."
@@ -132,6 +134,10 @@ Status 语义——`Interested`:接触前先记录关注 · `Outreached`:已
132
134
  尚无回应 · `Sourced`:已有真实关系信号。`sourceDirection` 是独立维度:
133
135
  `Inbound` 流入,`Outbound` 我们主动。
134
136
 
137
+ Facts 的正文用 `claim`。`source` 是人可读来源标签,`sourceUrl` 是 canonical
138
+ 证据 URL;两者都会从 API 回显。`dealOwner` 请用 `/api/field-options`
139
+ 里的精确 `dealOwner` 值、用户邮箱,或数字 user id。
140
+
135
141
  `llama --help` 看分组索引,`llama help all` 看全部 100+ 命令。
136
142
  所有删除默认软删除、有审计记录。
137
143
 
package/bin/llama-mcp.mjs CHANGED
@@ -471,7 +471,11 @@ server.registerTool(
471
471
  .describe(
472
472
  "Our Stage workflow position. Use Interested when we want to record/track before contact; use Outreached when we contacted/logged them and have no response/effective relationship; use Sourced only once there is a response, intro, meeting, or other real relationship signal."
473
473
  ),
474
- source: z.string().optional().describe("free-form sourced-by; recommend nominating a user"),
474
+ source: z.string().optional().describe("free-form sourced-by; exact team-member names also attach source_user_id"),
475
+ dealOwner: z
476
+ .string()
477
+ .optional()
478
+ .describe("owner override. Use an exact /api/field-options dealOwner value, a user email, or numeric user id."),
475
479
  sourceDirection: z
476
480
  .string()
477
481
  .optional()
@@ -510,7 +514,7 @@ server.registerTool(
510
514
  {
511
515
  description:
512
516
  "List a deal's recorded facts (the research substrate). Each fact carries a " +
513
- "category, a claim, a source, a confidence, and a trust rung (unverified → " +
517
+ "category, a claim, a source/sourceUrl, a confidence, and a trust rung (unverified → " +
514
518
  "agent-verified → human-vouched → endorsed) plus who/what recorded it.",
515
519
  inputSchema: {
516
520
  dealId: z.string(),
@@ -530,13 +534,15 @@ server.registerTool(
530
534
  "something unconfirmed (stored 'unverified', which is the honest default). You CANNOT " +
531
535
  "mark a fact as human-confirmed — only a person can raise it to 'human-vouched'. " +
532
536
  "`confidence` is how certain the claim is; `attested` is whether YOU take responsibility " +
533
- "for having checked it. category founders | financials | product | market | team | " +
534
- "company_basics | risk | fundraise | milestone | meta.",
537
+ "for having checked it. `source` is a human-readable provenance label; `sourceUrl` is a " +
538
+ "canonical URL that round-trips as sourceUrl/source_url. Category is free text; common " +
539
+ "values include founders | financials | product | market | team | company_basics | risk | fundraise | milestone | meta.",
535
540
  inputSchema: {
536
541
  dealId: z.string(),
537
542
  category: z.string(),
538
543
  claim: z.string(),
539
544
  source: z.string().optional().describe("where you found this (URL, 'deck p3', 'LinkedIn')"),
545
+ sourceUrl: z.string().optional().describe("canonical URL for the evidence, if separate from source"),
540
546
  confidence: z.enum(["high", "medium", "low"]).optional(),
541
547
  attested: z
542
548
  .boolean()
@@ -544,11 +550,12 @@ server.registerTool(
544
550
  .describe("true → stored 'agent-verified'; false/omitted → 'unverified'. Answer honestly."),
545
551
  },
546
552
  },
547
- async ({ dealId, category, claim, source, confidence, attested }) =>
553
+ async ({ dealId, category, claim, source, sourceUrl, confidence, attested }) =>
548
554
  callApi("POST", `/api/deals/${encodeURIComponent(dealId)}/facts`, {
549
555
  category,
550
556
  claim,
551
557
  source: source ?? "",
558
+ ...(sourceUrl ? { sourceUrl } : {}),
552
559
  confidence: confidence ?? "medium",
553
560
  attested: attested === true,
554
561
  })
package/bin/llama.mjs CHANGED
@@ -35,6 +35,7 @@ import {
35
35
  import { LLAMA_CLI_CLIENT_ID, pkceLoopbackFlow, revokeToken as revokeOAuthToken } from "../lib/oauth-flow.mjs";
36
36
  import { deleteBundle, detectBackend, readBundle, writeBundle } from "../lib/oauth-storage.mjs";
37
37
  import { maybeNudgeUpdate, getUpdateNudge } from "../lib/version-check.mjs";
38
+ import { getBuildInfo } from "../lib/build-info.mjs";
38
39
 
39
40
  const requireFromHere = createRequire(import.meta.url);
40
41
  const { version: PKG_VERSION } = requireFromHere("../package.json");
@@ -424,7 +425,8 @@ auto-detects \`gcloud auth print-identity-token\` and uses Bearer auth.
424
425
  Manually-set \`llc_\` tokens are used as a fallback.
425
426
 
426
427
  Deals:
427
- llama deal create "Company" --source <name> --source-direction Inbound|Outbound --description "..." --status Interested|Outreached|Sourced --website https://...
428
+ llama deal create "Company" --source <name> --deal-owner <name|email|userId> --source-direction Inbound|Outbound --description "..." --status Interested|Outreached|Sourced --website https://...
429
+ llama deal founders set <dealId> --json '[{"name":"Ada","email":"ada@example.com","linkedin_url":"https://linkedin.com/in/ada"}]'
428
430
  llama deal show <dealId>
429
431
  llama deal feed <dealId> # every contribution (facts + notes), human-typed or assistant-drafted, newest first
430
432
  llama deal update <dealId> <field> <value>
@@ -534,8 +536,8 @@ Deal soft-delete / restore / trash list:
534
536
  llama deal trash # list deleted deals
535
537
 
536
538
  Deal facts (AI-extracted or human-asserted, with verification):
537
- llama deal fact list <dealId> # ⚠ session-only on server today
538
- llama deal fact add <dealId> --category <cat> --claim "<text>" [--source <url>] [--confidence high|medium|low] [--attested]
539
+ llama deal fact list <dealId>
540
+ llama deal fact add <dealId> --category <cat> --claim "<text>" [--source "..."] [--source-url <url>] [--confidence high|medium|low] [--attested]
539
541
  llama deal fact verify <dealId> <factId> --status confirmed|disputed [--corrected-value "..."]
540
542
 
541
543
  Skill corrections (persona-owner pushback — read by persona-watcher):
@@ -1005,6 +1007,10 @@ async function runPitchRepl() {
1005
1007
  async function main() {
1006
1008
  const [area, action, ...rest] = process.argv.slice(2);
1007
1009
  if (area === "--version" || area === "-v" || area === "version") {
1010
+ if (action === "--json" || action === "json") {
1011
+ print(getBuildInfo());
1012
+ return;
1013
+ }
1008
1014
  // `llama version --check` — explicitly check npm for a newer release and
1009
1015
  // print the upgrade line (or "up to date"). Lets an agent surface the
1010
1016
  // nudge on demand, separate from the throttled, TTY-gated auto-nudge.
@@ -1440,10 +1446,11 @@ async function main() {
1440
1446
  if (area === "deal" && action === "create") {
1441
1447
  const { flags, positional } = parseFlags(rest);
1442
1448
  const companyName = positional.join(" ").trim();
1443
- if (!companyName) throw new Error("Usage: llama deal create \"Company\" [--source Name]");
1449
+ if (!companyName) throw new Error("Usage: llama deal create \"Company\" [--source Name] [--deal-owner name|email|userId]");
1444
1450
  const body = {
1445
1451
  companyName,
1446
1452
  source: flags.source,
1453
+ dealOwner: flags.dealOwner || flags["deal-owner"],
1447
1454
  sourceDirection: flags.sourceDirection || flags["source-direction"],
1448
1455
  description: flags.description,
1449
1456
  website: flags.website,
@@ -1483,6 +1490,26 @@ async function main() {
1483
1490
  return;
1484
1491
  }
1485
1492
 
1493
+ if (area === "deal" && action === "founders") {
1494
+ const sub = rest[0];
1495
+ const dealId = rest[1];
1496
+ const { flags } = parseFlags(rest.slice(2), ["json"]);
1497
+ if (sub !== "set" || !dealId || typeof flags.json !== "string") {
1498
+ throw new Error(
1499
+ "Usage: llama deal founders set <dealId> --json '[{\"name\":\"Ada\",\"email\":\"ada@example.com\"}]'"
1500
+ );
1501
+ }
1502
+ let founders;
1503
+ try {
1504
+ founders = JSON.parse(flags.json);
1505
+ } catch {
1506
+ throw new Error("--json must be a valid JSON array");
1507
+ }
1508
+ if (!Array.isArray(founders)) throw new Error("--json must be a JSON array");
1509
+ print(await request("PUT", `/api/deals/${encodeURIComponent(dealId)}/founders`, { founders }));
1510
+ return;
1511
+ }
1512
+
1486
1513
  // ----- deals.extra JSONB patches (system-admin only, server-gated) -----
1487
1514
  // Same endpoint as `deal update`, but `extraKey` instead of `field`.
1488
1515
  // Server patches one top-level key via jsonb_set and audits the change
@@ -1699,6 +1726,8 @@ async function main() {
1699
1726
  const linkId = rest[2];
1700
1727
  if (!linkId) throw new Error(`Usage: llama deal link ${sub} <dealId> <linkId>`);
1701
1728
  const path = `/api/deals/${encodeURIComponent(dealId)}/links/${encodeURIComponent(linkId)}`;
1729
+ // @core-api-operation DELETE /api/deals/{dealId}/links/{linkId}
1730
+ // @core-api-operation POST /api/deals/{dealId}/links/{linkId}/restore
1702
1731
  print(await request(
1703
1732
  sub === "delete" ? "DELETE" : "POST",
1704
1733
  sub === "delete" ? path : `${path}/restore`
@@ -1735,10 +1764,6 @@ async function main() {
1735
1764
  }
1736
1765
 
1737
1766
  // ----- Deal facts (AI-extracted or human-asserted, with verification) -----
1738
- // NOTE: server routes currently use session-only auth() — token-only
1739
- // callers will get 401 until /api/deals/:id/facts and
1740
- // /api/deals/:id/facts/:factId switch to authenticate(). CLI surface
1741
- // is forward-compatible.
1742
1767
  if (area === "deal" && action === "fact") {
1743
1768
  const sub = rest[0];
1744
1769
  const dealId = rest[1];
@@ -1754,10 +1779,12 @@ async function main() {
1754
1779
  }
1755
1780
 
1756
1781
  if (sub === "add") {
1757
- if (!flags.category || !flags.claim) {
1782
+ const claim = flags.claim ?? flags.value;
1783
+ const sourceUrl = flags["source-url"] ?? flags.sourceUrl;
1784
+ if (!flags.category || !claim) {
1758
1785
  throw new Error(
1759
1786
  `Usage: llama deal fact add <dealId> --category <cat> --claim "<text>" ` +
1760
- `[--source <url>] [--confidence high|medium|low] [--attested]`
1787
+ `[--source "..."] [--source-url <url>] [--confidence high|medium|low] [--attested]`
1761
1788
  );
1762
1789
  }
1763
1790
  // --attested: the caller takes responsibility that this is accurate
@@ -1765,8 +1792,9 @@ async function main() {
1765
1792
  // vouched; without it, it stays unverified. Declare honestly.
1766
1793
  print(await request("POST", `/api/deals/${encodeURIComponent(dealId)}/facts`, {
1767
1794
  category: String(flags.category),
1768
- claim: String(flags.claim),
1795
+ claim: String(claim),
1769
1796
  source: flags.source ? String(flags.source) : "",
1797
+ ...(sourceUrl ? { sourceUrl: String(sourceUrl) } : {}),
1770
1798
  confidence: flags.confidence ? String(flags.confidence) : "medium",
1771
1799
  attested: flags.attested === true,
1772
1800
  }));
@@ -1967,6 +1995,7 @@ async function main() {
1967
1995
  if (!slug) throw new Error("Usage: llama wiki read <slug> [--lang en|zh]");
1968
1996
  const lang = flags.lang === "zh" ? "zh" : "en";
1969
1997
  const path = `/api/wiki/${encodeURIComponent(slug)}?lang=${lang}`;
1998
+ // @core-api-operation GET /api/wiki/{slug}
1970
1999
  print(await request("GET", path));
1971
2000
  return;
1972
2001
  }
@@ -2298,6 +2327,9 @@ Routing — is this the right command?
2298
2327
  if (flags["errors-only"]) params.set("errors_only", "1");
2299
2328
  }
2300
2329
  const qs = params.toString() ? `?${params.toString()}` : "";
2330
+ // @core-api-operation GET /api/admin/auth-events
2331
+ // @core-api-operation GET /api/admin/deal-events
2332
+ // @core-api-operation GET /api/admin/agent-events
2301
2333
  print(await request("GET", `/api/admin/${sub}${qs}`));
2302
2334
  return;
2303
2335
  }
@@ -0,0 +1,9 @@
1
+ {
2
+ "format": "llama.core-api-contract.v1",
3
+ "name": "llama-core-api",
4
+ "apiVersion": "1.0.1",
5
+ "openapiVersion": "3.0.3",
6
+ "sha256": "95ce3639aeee15b0fed09830116838f2a11fd2e40e4f35bf80d785664ecf8d96",
7
+ "pathCount": 211,
8
+ "operationCount": 273
9
+ }
@@ -0,0 +1,91 @@
1
+ {
2
+ "format": "llama.cli-core-api-operations.v1",
3
+ "coreApiContract": "contracts/core-api.json",
4
+ "requiredOperations": [
5
+ { "method": "DELETE", "path": "/api/deals/{dealId}" },
6
+ { "method": "DELETE", "path": "/api/deals/{dealId}/blocks/{blockId}" },
7
+ { "method": "DELETE", "path": "/api/deals/{dealId}/collaborators/{userId}" },
8
+ { "method": "DELETE", "path": "/api/deals/{dealId}/documents/{slug}" },
9
+ { "method": "DELETE", "path": "/api/deals/{dealId}/documents/{slug}/html" },
10
+ { "method": "DELETE", "path": "/api/deals/{dealId}/links/{linkId}" },
11
+ { "method": "DELETE", "path": "/api/deals/{dealId}/memo" },
12
+ { "method": "DELETE", "path": "/api/skill-corrections/{id}" },
13
+ { "method": "DELETE", "path": "/api/wiki/{slug}" },
14
+ { "method": "GET", "path": "/api/admin/agent-events" },
15
+ { "method": "GET", "path": "/api/admin/auth-events" },
16
+ { "method": "GET", "path": "/api/admin/deal-events" },
17
+ { "method": "GET", "path": "/api/agent/activity" },
18
+ { "method": "GET", "path": "/api/agent/briefing" },
19
+ { "method": "GET", "path": "/api/agent/explain" },
20
+ { "method": "GET", "path": "/api/agent/manifest" },
21
+ { "method": "GET", "path": "/api/agent/skills" },
22
+ { "method": "GET", "path": "/api/agent/skills/{slug}" },
23
+ { "method": "GET", "path": "/api/deals" },
24
+ { "method": "GET", "path": "/api/deals/{dealId}" },
25
+ { "method": "GET", "path": "/api/deals/{dealId}/blocks" },
26
+ { "method": "GET", "path": "/api/deals/{dealId}/blocks/{blockId}" },
27
+ { "method": "GET", "path": "/api/deals/{dealId}/blocks/{blockId}/history" },
28
+ { "method": "GET", "path": "/api/deals/{dealId}/collaborators" },
29
+ { "method": "GET", "path": "/api/deals/{dealId}/command-center" },
30
+ { "method": "GET", "path": "/api/deals/{dealId}/documents" },
31
+ { "method": "GET", "path": "/api/deals/{dealId}/documents/{slug}/html" },
32
+ { "method": "GET", "path": "/api/deals/{dealId}/documents/{slug}/html/history" },
33
+ { "method": "GET", "path": "/api/deals/{dealId}/facts" },
34
+ { "method": "GET", "path": "/api/deals/{dealId}/feed" },
35
+ { "method": "GET", "path": "/api/deals/{dealId}/links" },
36
+ { "method": "GET", "path": "/api/deals/{dealId}/memo" },
37
+ { "method": "GET", "path": "/api/deals/{dealId}/timeline" },
38
+ { "method": "GET", "path": "/api/deals/deleted" },
39
+ { "method": "GET", "path": "/api/me" },
40
+ { "method": "GET", "path": "/api/me/nominations" },
41
+ { "method": "GET", "path": "/api/mentions" },
42
+ { "method": "GET", "path": "/api/mentions/unread-count" },
43
+ { "method": "GET", "path": "/api/oauth/authorize" },
44
+ { "method": "GET", "path": "/api/partner/approvals" },
45
+ { "method": "GET", "path": "/api/skill-corrections" },
46
+ { "method": "GET", "path": "/api/users" },
47
+ { "method": "GET", "path": "/api/wiki/{slug}" },
48
+ { "method": "GET", "path": "/api/wiki/search" },
49
+ { "method": "PATCH", "path": "/api/deals/{dealId}/blocks/{blockId}" },
50
+ { "method": "PATCH", "path": "/api/deals/{dealId}/documents/{slug}" },
51
+ { "method": "PATCH", "path": "/api/deals/{dealId}/facts/{factId}" },
52
+ { "method": "POST", "path": "/api/agent/client-events" },
53
+ { "method": "POST", "path": "/api/agent/eval-feedback" },
54
+ { "method": "POST", "path": "/api/deals/{dealId}/agent-runs/{runId}/revert" },
55
+ { "method": "POST", "path": "/api/deals/{dealId}/blocks/{blockId}/history" },
56
+ { "method": "POST", "path": "/api/deals/{dealId}/blocks/{blockId}/restore" },
57
+ { "method": "POST", "path": "/api/deals/{dealId}/collaborators" },
58
+ { "method": "POST", "path": "/api/deals/{dealId}/collaborators/{userId}/restore" },
59
+ { "method": "POST", "path": "/api/deals/{dealId}/documents" },
60
+ { "method": "POST", "path": "/api/deals/{dealId}/documents/{slug}/html/restore/{version}" },
61
+ { "method": "POST", "path": "/api/deals/{dealId}/enrich" },
62
+ { "method": "POST", "path": "/api/deals/{dealId}/facts" },
63
+ { "method": "POST", "path": "/api/deals/{dealId}/links" },
64
+ { "method": "POST", "path": "/api/deals/{dealId}/links/{linkId}/restore" },
65
+ { "method": "POST", "path": "/api/deals/{dealId}/memo" },
66
+ { "method": "POST", "path": "/api/deals/{dealId}/posts" },
67
+ { "method": "POST", "path": "/api/deals/{dealId}/propose-owner" },
68
+ { "method": "POST", "path": "/api/deals/{dealId}/refresh-brief" },
69
+ { "method": "POST", "path": "/api/deals/{dealId}/refresh-persona" },
70
+ { "method": "POST", "path": "/api/deals/{dealId}/restore" },
71
+ { "method": "POST", "path": "/api/deals/{dealId}/threads" },
72
+ { "method": "POST", "path": "/api/deals/{dealId}/threads/{threadId}" },
73
+ { "method": "POST", "path": "/api/deals/create" },
74
+ { "method": "POST", "path": "/api/deals/update" },
75
+ { "method": "POST", "path": "/api/external/chat" },
76
+ { "method": "POST", "path": "/api/external/start-session" },
77
+ { "method": "POST", "path": "/api/external/upload" },
78
+ { "method": "POST", "path": "/api/mentions/{id}/resolve" },
79
+ { "method": "POST", "path": "/api/nominations/{approvalId}" },
80
+ { "method": "POST", "path": "/api/oauth/revoke" },
81
+ { "method": "POST", "path": "/api/oauth/token" },
82
+ { "method": "POST", "path": "/api/partner/approvals" },
83
+ { "method": "POST", "path": "/api/skill-corrections" },
84
+ { "method": "POST", "path": "/api/wiki/{slug}/restore" },
85
+ { "method": "POST", "path": "/api/wiki/save" },
86
+ { "method": "PUT", "path": "/api/deals/{dealId}/blocks" },
87
+ { "method": "PUT", "path": "/api/deals/{dealId}/documents/{slug}/html" },
88
+ { "method": "PUT", "path": "/api/deals/{dealId}/founders" },
89
+ { "method": "PUT", "path": "/api/deals/{dealId}/memo" }
90
+ ]
91
+ }
@@ -0,0 +1,34 @@
1
+ import { createRequire } from "node:module";
2
+
3
+ const requireFromHere = createRequire(import.meta.url);
4
+
5
+ function readJson(relativePath) {
6
+ try {
7
+ return requireFromHere(relativePath);
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+
13
+ export function getBuildInfo() {
14
+ const built = readJson("./build-manifest.json");
15
+ if (built) return built;
16
+
17
+ const packageJson = readJson("../package.json") || {};
18
+ const contract = readJson("../contracts/core-api.json") || {};
19
+ return {
20
+ format: "llama.cli-build.v1",
21
+ packageName: packageJson.name || "@llamaventures/cli",
22
+ packageVersion: packageJson.version || "unknown",
23
+ sourceSha: "local/unknown",
24
+ sourceKind: "local",
25
+ sourceDirty: null,
26
+ coreApiContract: {
27
+ format: contract.format || "unknown",
28
+ name: contract.name || "llama-core-api",
29
+ apiVersion: contract.apiVersion || "unknown",
30
+ openapiVersion: contract.openapiVersion || "unknown",
31
+ sha256: contract.sha256 || "unknown",
32
+ },
33
+ };
34
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "format": "llama.cli-build.v1",
3
+ "packageName": "@llamaventures/cli",
4
+ "packageVersion": "1.19.0",
5
+ "sourceSha": "5f615ca16d607fc0da09887952afdf42301a43f8",
6
+ "sourceKind": "github",
7
+ "sourceDirty": false,
8
+ "coreApiContract": {
9
+ "format": "llama.core-api-contract.v1",
10
+ "name": "llama-core-api",
11
+ "apiVersion": "1.0.1",
12
+ "openapiVersion": "3.0.3",
13
+ "sha256": "95ce3639aeee15b0fed09830116838f2a11fd2e40e4f35bf80d785664ecf8d96"
14
+ }
15
+ }
package/lib/client.mjs CHANGED
@@ -14,6 +14,7 @@ import { fileURLToPath } from "url";
14
14
  import { execFile as _execFile } from "child_process";
15
15
  import { promisify } from "util";
16
16
  import { createHash, randomUUID } from "crypto";
17
+ import { getBuildInfo } from "./build-info.mjs";
17
18
 
18
19
  const execFile = promisify(_execFile);
19
20
 
@@ -207,9 +208,13 @@ function rememberAgentEvent(event) {
207
208
  }
208
209
 
209
210
  function agentClientHeaders(command) {
211
+ const build = getBuildInfo();
210
212
  return {
211
213
  "X-Llama-Client": runtimeClient,
212
214
  "X-Llama-Client-Version": getPackageVersion(),
215
+ "X-Llama-Client-Source-Sha": build.sourceSha,
216
+ "X-Llama-API-Contract-Version": build.coreApiContract.apiVersion,
217
+ "X-Llama-API-Contract-Digest": build.coreApiContract.sha256,
213
218
  "X-Llama-Agent-Client": detectAgentClient(),
214
219
  "X-Llama-Agent-Session": currentAgentSessionId(),
215
220
  "X-Llama-Command": command || "unknown",
@@ -173,6 +173,7 @@ export async function pkceLoopbackFlow({ baseUrl, scope, resource }) {
173
173
  });
174
174
 
175
175
  // Build authorize URL and open browser.
176
+ // @core-api-operation GET /api/oauth/authorize
176
177
  const authorizeUrl = new URL(`${baseUrl}/api/oauth/authorize`);
177
178
  authorizeUrl.searchParams.set("response_type", "code");
178
179
  authorizeUrl.searchParams.set("client_id", CLIENT_ID);
@@ -73,6 +73,7 @@ async function fetchLatest() {
73
73
  // The /latest convenience endpoint returns full JSON under the default
74
74
  // Accept. (The abbreviated "vnd.npm.install-v1+json" metadata type is only
75
75
  // valid on the full packument endpoint — it 406s here.)
76
+ // @core-api-ignore external npm registry version check
76
77
  const res = await fetch(REGISTRY_URL, { signal: controller.signal });
77
78
  if (!res.ok) return null;
78
79
  const data = await res.json();
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "@llamaventures/cli",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "CLI + MCP server for the Llama Ventures investment workbench (command.llamaventures.vc).",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "test": "npm run test:agent-routing",
7
+ "test": "npm run test:agent-routing && npm run test:contract",
8
8
  "test:agent-routing": "node scripts/verify-agent-routing.mjs",
9
- "verify:release": "npm test && node scripts/verify-tarball-clean.mjs && npm pack --dry-run"
9
+ "test:contract": "node --test scripts/build-manifest.test.mjs scripts/core-api-call-sites.test.mjs scripts/verify-core-api-contract.test.mjs && node scripts/verify-core-api-contract.mjs",
10
+ "verify:artifact": "node scripts/verify-release-artifact.mjs",
11
+ "verify:release": "npm test && npm run verify:artifact && node scripts/verify-tarball-clean.mjs",
12
+ "prepack": "node scripts/prepare-build-manifest.mjs",
13
+ "postpack": "node scripts/cleanup-build-manifest.mjs"
10
14
  },
11
15
  "bin": {
12
16
  "llama": "bin/llama.mjs",
@@ -14,6 +18,7 @@
14
18
  },
15
19
  "files": [
16
20
  "bin/",
21
+ "contracts/",
17
22
  "lib/",
18
23
  "AGENT_BRIEFING.md",
19
24
  "CHANGELOG.md",