@llamaventures/cli 2.0.1 → 2.0.3
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 +37 -0
- package/CHANGELOG.md +33 -0
- package/README.md +10 -2
- package/README.zh-CN.md +6 -0
- package/bin/llama-mcp.mjs +74 -36
- package/bin/llama.mjs +49 -6
- package/lib/build-manifest.json +2 -2
- package/lib/client.mjs +30 -1
- package/lib/deal-actions.mjs +26 -58
- package/package.json +1 -1
package/AGENT_BRIEFING.md
CHANGED
|
@@ -4,6 +4,28 @@ This package is the authenticated tool boundary for Llama Command. For Deal
|
|
|
4
4
|
work, the interface is intentionally small: search, read, create, and write.
|
|
5
5
|
Do not bypass it with SQL, Google APIs, or retired HTTP routes.
|
|
6
6
|
|
|
7
|
+
## Mandatory live bootstrap
|
|
8
|
+
|
|
9
|
+
Before any Llama workflow, run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
llama agent bootstrap
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The authenticated response contains the current private Llama Brain with
|
|
16
|
+
exactly two components: the approved Investment Framework V3 Markdown (how to
|
|
17
|
+
think) and the Llama Command operating skill (how to work). Beside Brain it
|
|
18
|
+
contains the separate current Live Deal Page field contract (what every visible
|
|
19
|
+
slot means and how to verify a good write). The public CLI intentionally bundles
|
|
20
|
+
none of this private content; it only fetches it after authentication.
|
|
21
|
+
|
|
22
|
+
CLI is the Agent's tool, not Brain. Do not substitute a private skill wrapper
|
|
23
|
+
for either canonical Brain component.
|
|
24
|
+
|
|
25
|
+
Do not assume the Page is only `description` plus `notes`, and do not infer its
|
|
26
|
+
schema from a previous session. If bootstrap fails, repair authentication or
|
|
27
|
+
upgrade the CLI before writing; do not guess the fields.
|
|
28
|
+
|
|
7
29
|
## The Deal model
|
|
8
30
|
|
|
9
31
|
Each company has five business resources:
|
|
@@ -17,6 +39,21 @@ Each company has five business resources:
|
|
|
17
39
|
Archive, trash, status, founder, opinion, fact, memo section, and artifact kind
|
|
18
40
|
are data or labels inside these resources. They are not separate tools.
|
|
19
41
|
|
|
42
|
+
Information and Page are deliberately different. Information is traceable
|
|
43
|
+
working memory; it never becomes Page automatically. Page is the bounded,
|
|
44
|
+
human-visible current projection, so patch the exact semantic fields described
|
|
45
|
+
by the live contract instead of hiding structured analysis in `notes`.
|
|
46
|
+
|
|
47
|
+
Every Page content field is Agent-writable. A field's `author` or
|
|
48
|
+
`semanticOwner` describes attribution, not permission. Preserve a real human
|
|
49
|
+
judgment with its author; otherwise label the value as an Agent proposal / 待人确认.
|
|
50
|
+
|
|
51
|
+
`page.patch` uses JSON Merge Patch: objects merge, `null` deletes, and arrays
|
|
52
|
+
replace the entire array. Before changing one `whatTheyDo`, `peopleSteps`,
|
|
53
|
+
`foundersJson`, or `industryMaps` item, read the current Page, preserve every
|
|
54
|
+
sibling item, and submit the complete updated array. The live field contract
|
|
55
|
+
includes the exact payload path and write mode for all 61 semantic fields.
|
|
56
|
+
|
|
20
57
|
## Exactly four Deal actions
|
|
21
58
|
|
|
22
59
|
```bash
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.3] - 2026-08-31
|
|
4
|
+
|
|
5
|
+
- Show Core validation codes and field paths instead of collapsing malformed
|
|
6
|
+
Deal writes into `Invalid Occam command`.
|
|
7
|
+
- Publish `write_deal` as four operation-specific MCP command shapes while
|
|
8
|
+
retaining one mutation tool.
|
|
9
|
+
- Add copyable write examples to `llama help deal` and omit the duplicate
|
|
10
|
+
rendered briefing from structured bootstrap output.
|
|
11
|
+
- Let Core's private Page contract classify prose and enums instead of keeping
|
|
12
|
+
a second, drifting public-CLI field whitelist.
|
|
13
|
+
- Return a compact `page.patch` revision receipt with exact read-back fields
|
|
14
|
+
instead of echoing the full Live Deal Page after every write.
|
|
15
|
+
- Turn retired top-level commands such as `llama brief`, `fact`, `post`,
|
|
16
|
+
`timeline`, `html`, `memo`, and `pipeline` into self-correcting errors that
|
|
17
|
+
name the CLI 2 replacement and live contract entrypoint.
|
|
18
|
+
|
|
19
|
+
## [2.0.2] - 2026-08-31
|
|
20
|
+
|
|
21
|
+
- Correct the authenticated runtime model: Brain has exactly two private
|
|
22
|
+
components, Investment Framework V3 and the Llama Command operating skill.
|
|
23
|
+
- Load the complete Live Deal Page field prompts beside Brain as a separate
|
|
24
|
+
current CLI/MCP tool contract.
|
|
25
|
+
- Keep all Brain and Page prompt bytes server-owned and out of the public npm
|
|
26
|
+
artifact.
|
|
27
|
+
|
|
3
28
|
## [2.0.1] - 2026-08-30
|
|
4
29
|
|
|
5
30
|
- Ship the four-action Occam Deal surface from the audited cleanup commit.
|
|
@@ -30,6 +55,14 @@ this project adheres to [Semantic Versioning](https://semver.org).
|
|
|
30
55
|
|
|
31
56
|
## [Unreleased]
|
|
32
57
|
|
|
58
|
+
### Changed
|
|
59
|
+
- Make the authenticated live bootstrap mandatory before Deal work and clarify
|
|
60
|
+
that it loads the server-owned two-part private Brain plus the separate Live
|
|
61
|
+
Deal Page field contract. CLI remains only the tool/transport layer.
|
|
62
|
+
- Teach CLI and MCP Agents that Information never updates Page automatically,
|
|
63
|
+
every Page content field is Agent-writable, and field authorship controls
|
|
64
|
+
attribution rather than permission.
|
|
65
|
+
|
|
33
66
|
## [1.26.0] - 2026-08-11
|
|
34
67
|
|
|
35
68
|
### Added
|
package/README.md
CHANGED
|
@@ -90,8 +90,9 @@ When Page prose is human-visible, write one bilingual field value as
|
|
|
90
90
|
`{"en":"natural English","zh":"自然中文"}`. The Web language switch selects
|
|
91
91
|
the reader's version from the same Page revision. Keep language-neutral names,
|
|
92
92
|
enums, URLs, numbers, dates, and source IDs scalar. Information and raw Input
|
|
93
|
-
stay in their original language with provenance.
|
|
94
|
-
|
|
93
|
+
stay in their original language with provenance. Core rejects new scalar Page
|
|
94
|
+
prose according to its private Page contract; the public CLI catches malformed
|
|
95
|
+
half-localized pairs without maintaining a second field whitelist. Historical
|
|
95
96
|
scalar prose remains readable.
|
|
96
97
|
|
|
97
98
|
Raw user input example:
|
|
@@ -158,6 +159,13 @@ llama skills search "<task>"
|
|
|
158
159
|
llama skills show <slug>
|
|
159
160
|
```
|
|
160
161
|
|
|
162
|
+
Agents should run `llama agent bootstrap` before Deal work. The command loads
|
|
163
|
+
the authenticated, server-owned private Brain: Investment Framework V3 for
|
|
164
|
+
thinking plus the Llama Command operating skill for working. It also loads the
|
|
165
|
+
separate current Live Deal Page field contract for writing. None of that private
|
|
166
|
+
content is shipped in this public package; CLI is only the tool and authenticated
|
|
167
|
+
transport.
|
|
168
|
+
|
|
161
169
|
The live server briefing is authoritative. The bundled
|
|
162
170
|
`AGENT_BRIEFING.md` is an offline fallback with the same four-action contract.
|
|
163
171
|
|
package/README.zh-CN.md
CHANGED
|
@@ -151,6 +151,12 @@ llama skills search "<任务>"
|
|
|
151
151
|
llama skills show <slug>
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
+
Agent 在处理 Deal 前应先运行 `llama agent bootstrap`。该命令会从鉴权后的
|
|
155
|
+
Llama Command 服务端加载两部分私有 Brain:负责投资思考的 Investment
|
|
156
|
+
Framework V3,以及负责如何在 Command 工作的 Llama Command operating skill;
|
|
157
|
+
同时加载独立的、实时的 Live Deal Page 字段契约。所有私有内容都不会进入公开
|
|
158
|
+
npm 包;CLI 只是 Agent 的工具和鉴权运输层。
|
|
159
|
+
|
|
154
160
|
服务端实时 briefing 是权威合同;包内 `AGENT_BRIEFING.md` 是相同四动作
|
|
155
161
|
合同的离线兜底。
|
|
156
162
|
|
package/bin/llama-mcp.mjs
CHANGED
|
@@ -5,10 +5,12 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import {
|
|
8
|
+
formatErrorForDisplay,
|
|
8
9
|
getAuthHeaders,
|
|
9
10
|
readBriefing,
|
|
10
11
|
request,
|
|
11
12
|
setClientRuntime,
|
|
13
|
+
structuredBootstrapManifest,
|
|
12
14
|
} from "../lib/client.mjs";
|
|
13
15
|
import {
|
|
14
16
|
clearExternalSession,
|
|
@@ -20,6 +22,7 @@ import {
|
|
|
20
22
|
import {
|
|
21
23
|
buildDealReadPath,
|
|
22
24
|
buildDealSearchPath,
|
|
25
|
+
compactDealWriteResult,
|
|
23
26
|
prepareDealCommand,
|
|
24
27
|
} from "../lib/deal-actions.mjs";
|
|
25
28
|
|
|
@@ -36,12 +39,12 @@ function textResult(text, isError = false) {
|
|
|
36
39
|
};
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
async function callApi(method, path, body) {
|
|
42
|
+
async function callApi(method, path, body, transform = (value) => value) {
|
|
40
43
|
try {
|
|
41
|
-
const result = await request(method, path, body);
|
|
44
|
+
const result = transform(await request(method, path, body));
|
|
42
45
|
return textResult(typeof result === "string" ? result : JSON.stringify(result, null, 2));
|
|
43
46
|
} catch (error) {
|
|
44
|
-
return textResult(
|
|
47
|
+
return textResult(formatErrorForDisplay(error), true);
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
|
|
@@ -51,6 +54,54 @@ const originSchema = z.object({
|
|
|
51
54
|
originatingChatRecordId: z.string().uuid().optional(),
|
|
52
55
|
});
|
|
53
56
|
|
|
57
|
+
const writeDealCommandSchema = z.discriminatedUnion("operation", [
|
|
58
|
+
z.object({
|
|
59
|
+
operation: z.literal("input.submit"),
|
|
60
|
+
dealId: z.string().uuid(),
|
|
61
|
+
format: z.string().min(1).max(120).optional(),
|
|
62
|
+
content: z.any(),
|
|
63
|
+
source: z.record(z.string(), z.any()).optional(),
|
|
64
|
+
origin: originSchema,
|
|
65
|
+
idempotencyKey: z.string().min(1).max(240).optional(),
|
|
66
|
+
}).strict(),
|
|
67
|
+
z.object({
|
|
68
|
+
operation: z.literal("information.put"),
|
|
69
|
+
dealId: z.string().uuid(),
|
|
70
|
+
informationId: z.string().uuid().optional(),
|
|
71
|
+
type: z.string().min(1).max(120),
|
|
72
|
+
labels: z.array(z.string()).optional(),
|
|
73
|
+
subject: z.record(z.string(), z.any()).optional(),
|
|
74
|
+
value: z.any(),
|
|
75
|
+
expectedVersion: z.number().int().positive().optional(),
|
|
76
|
+
origin: originSchema,
|
|
77
|
+
idempotencyKey: z.string().min(1).max(240).optional(),
|
|
78
|
+
}).strict(),
|
|
79
|
+
z.object({
|
|
80
|
+
operation: z.literal("page.patch"),
|
|
81
|
+
dealId: z.string().uuid(),
|
|
82
|
+
patch: z.record(z.string(), z.any()),
|
|
83
|
+
expectedRevision: z.number().int().nonnegative().optional(),
|
|
84
|
+
origin: originSchema,
|
|
85
|
+
idempotencyKey: z.string().min(1).max(240).optional(),
|
|
86
|
+
}).strict(),
|
|
87
|
+
z.object({
|
|
88
|
+
operation: z.literal("artifact.put"),
|
|
89
|
+
dealId: z.string().uuid(),
|
|
90
|
+
artifactId: z.string().uuid().optional(),
|
|
91
|
+
kind: z.string().min(1).max(120),
|
|
92
|
+
title: z.string().min(1).max(500),
|
|
93
|
+
mimeType: z.string().min(1).max(240),
|
|
94
|
+
contentBase64: z.string().min(1).optional(),
|
|
95
|
+
storageKey: z.string().min(1).optional(),
|
|
96
|
+
storageUrl: z.string().url().optional(),
|
|
97
|
+
byteSize: z.number().int().nonnegative().optional(),
|
|
98
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
|
|
99
|
+
metadata: z.record(z.string(), z.any()).optional(),
|
|
100
|
+
origin: originSchema,
|
|
101
|
+
idempotencyKey: z.string().min(1).max(240).optional(),
|
|
102
|
+
}).strict(),
|
|
103
|
+
]);
|
|
104
|
+
|
|
54
105
|
// Deal is deliberately a closed four-tool surface. Do not add split resource
|
|
55
106
|
// tools here: one more tool is one more branch every agent must reason about.
|
|
56
107
|
server.registerTool(
|
|
@@ -70,7 +121,7 @@ server.registerTool(
|
|
|
70
121
|
server.registerTool(
|
|
71
122
|
"read_deal",
|
|
72
123
|
{
|
|
73
|
-
description: "Read one Deal. Live Page is
|
|
124
|
+
description: "Read one Deal. Live Page is current human-visible state; Information is memory and never updates Page automatically. Page field semantics come from agent_bootstrap, not every Deal read.",
|
|
74
125
|
inputSchema: {
|
|
75
126
|
dealId: z.string().min(1),
|
|
76
127
|
detail: z.enum(["overview", "memory", "files", "conversation", "history", "all"]).optional(),
|
|
@@ -99,36 +150,18 @@ server.registerTool(
|
|
|
99
150
|
server.registerTool(
|
|
100
151
|
"write_deal",
|
|
101
152
|
{
|
|
102
|
-
description: "The only Deal mutation tool: input.submit, information.put, page.patch, or artifact.put.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
expectedRevision: z.number().int().nonnegative().optional(),
|
|
108
|
-
informationId: z.string().uuid().optional(),
|
|
109
|
-
type: z.string().min(1).max(120).optional(),
|
|
110
|
-
labels: z.array(z.string()).optional(),
|
|
111
|
-
subject: z.record(z.string(), z.any()).optional(),
|
|
112
|
-
value: z.any().optional(),
|
|
113
|
-
expectedVersion: z.number().int().positive().optional(),
|
|
114
|
-
format: z.string().min(1).max(120).optional(),
|
|
115
|
-
content: z.any().optional(),
|
|
116
|
-
source: z.record(z.string(), z.any()).optional(),
|
|
117
|
-
artifactId: z.string().uuid().optional(),
|
|
118
|
-
kind: z.string().min(1).max(120).optional(),
|
|
119
|
-
title: z.string().min(1).max(500).optional(),
|
|
120
|
-
mimeType: z.string().min(1).max(240).optional(),
|
|
121
|
-
contentBase64: z.string().min(1).optional(),
|
|
122
|
-
storageKey: z.string().min(1).optional(),
|
|
123
|
-
storageUrl: z.string().url().optional(),
|
|
124
|
-
byteSize: z.number().int().nonnegative().optional(),
|
|
125
|
-
sha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
|
|
126
|
-
metadata: z.record(z.string(), z.any()).optional(),
|
|
127
|
-
origin: originSchema,
|
|
128
|
-
idempotencyKey: z.string().min(1).max(240).optional(),
|
|
129
|
-
},
|
|
153
|
+
description: "The only Deal mutation tool. Pass one command: input.submit, information.put, page.patch, or artifact.put. All Page content fields are Agent-writable; author controls attribution, not permission. Use the bootstrap field contract and bilingual Page prose. page.patch is JSON Merge Patch: arrays replace whole arrays, so read-modify-write and preserve sibling slots.",
|
|
154
|
+
// MCP SDK 1.30 only publishes object schemas at the tool root. Nesting one
|
|
155
|
+
// command preserves the real discriminated union instead of flattening four
|
|
156
|
+
// incompatible operations into false affordances.
|
|
157
|
+
inputSchema: { command: writeDealCommandSchema },
|
|
130
158
|
},
|
|
131
|
-
async (
|
|
159
|
+
async ({ command }) => callApi(
|
|
160
|
+
"POST",
|
|
161
|
+
"/api/occam/deals/commands",
|
|
162
|
+
prepareDealCommand("write", command),
|
|
163
|
+
(result) => compactDealWriteResult(command, result),
|
|
164
|
+
),
|
|
132
165
|
);
|
|
133
166
|
|
|
134
167
|
server.registerTool(
|
|
@@ -152,13 +185,18 @@ server.registerTool(
|
|
|
152
185
|
server.registerTool(
|
|
153
186
|
"agent_bootstrap",
|
|
154
187
|
{
|
|
155
|
-
description: "
|
|
188
|
+
description: "Mandatory before Llama work: fetch the authenticated two-part private Brain (Investment Framework V3 + Llama Command operating skill), the separate current Live Deal Page field contract, runtime contract, and visible Llama OS skills.",
|
|
156
189
|
inputSchema: { limit: z.number().int().min(1).max(100).optional() },
|
|
157
190
|
},
|
|
158
191
|
async ({ limit } = {}) => {
|
|
159
192
|
const params = new URLSearchParams({ clientVersion: PKG_VERSION });
|
|
160
193
|
if (limit) params.set("limit", String(limit));
|
|
161
|
-
|
|
194
|
+
try {
|
|
195
|
+
const result = await request("GET", `/api/agent/manifest?${params}`);
|
|
196
|
+
return textResult(JSON.stringify(structuredBootstrapManifest(result), null, 2));
|
|
197
|
+
} catch (error) {
|
|
198
|
+
return textResult(formatErrorForDisplay(error), true);
|
|
199
|
+
}
|
|
162
200
|
},
|
|
163
201
|
);
|
|
164
202
|
|
|
@@ -361,7 +399,7 @@ server.registerTool(
|
|
|
361
399
|
server.registerPrompt(
|
|
362
400
|
"agent_briefing",
|
|
363
401
|
{
|
|
364
|
-
description: "
|
|
402
|
+
description: "Mandatory before Llama work: fetch the authenticated two-part private Brain, the separate current Live Deal Page field contract, and runtime contract; bundled text is only an offline fallback.",
|
|
365
403
|
},
|
|
366
404
|
async () => {
|
|
367
405
|
const headers = await getAuthHeaders();
|
package/bin/llama.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { createRequire } from "node:module";
|
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
5
|
import readline from "node:readline";
|
|
6
6
|
import {
|
|
7
|
+
formatErrorForDisplay,
|
|
7
8
|
getAuthHeaders,
|
|
8
9
|
getBaseUrl,
|
|
9
10
|
getToken,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
readCanonicalToken,
|
|
13
14
|
readLegacyConfig,
|
|
14
15
|
request,
|
|
16
|
+
structuredBootstrapManifest,
|
|
15
17
|
tryGcloudIdentityToken,
|
|
16
18
|
writeCanonicalToken,
|
|
17
19
|
writeLegacyConfig,
|
|
@@ -33,6 +35,7 @@ import {
|
|
|
33
35
|
DEAL_ACTIONS,
|
|
34
36
|
buildDealReadPath,
|
|
35
37
|
buildDealSearchPath,
|
|
38
|
+
compactDealWriteResult,
|
|
36
39
|
prepareDealCommand,
|
|
37
40
|
readJsonInput,
|
|
38
41
|
} from "../lib/deal-actions.mjs";
|
|
@@ -69,11 +72,27 @@ Exactly four actions:
|
|
|
69
72
|
llama deal create --json <file|->
|
|
70
73
|
llama deal write --json <file|->
|
|
71
74
|
|
|
75
|
+
Before any Page write:
|
|
76
|
+
llama agent bootstrap
|
|
77
|
+
|
|
78
|
+
The authenticated bootstrap contains the two-part private Llama Brain:
|
|
79
|
+
Investment Framework V3 plus the Llama Command operating skill. It also
|
|
80
|
+
contains the separate current 61-field Page contract. CLI is the tool, not
|
|
81
|
+
Brain. Information never updates Page automatically. page.patch is JSON
|
|
82
|
+
Merge Patch: objects merge, null deletes, and arrays replace whole arrays, so
|
|
83
|
+
read-modify-write complete Page arrays and preserve sibling slots.
|
|
84
|
+
|
|
72
85
|
create JSON:
|
|
73
86
|
{"companyName":"Acme","page":{},"information":[],"origin":{"kind":"user","originalUserUtterance":"..."}}
|
|
74
87
|
|
|
75
|
-
write
|
|
76
|
-
input.submit
|
|
88
|
+
write JSON (CLI derives idempotencyKey when omitted):
|
|
89
|
+
input.submit: {"operation":"input.submit","dealId":"<uuid>","content":"<original input>","format":"text","source":{},"origin":{"kind":"user","originalUserUtterance":"..."}}
|
|
90
|
+
information.put:{"operation":"information.put","dealId":"<uuid>","type":"founder_claim","labels":["founder-stated"],"subject":{"topic":"traction"},"value":{"content":"...","source":{"kind":"first_meeting_transcript"}},"origin":{"kind":"user","originalUserUtterance":"..."}}
|
|
91
|
+
page.patch: {"operation":"page.patch","dealId":"<uuid>","patch":{"description":{"en":"...","zh":"..."}},"origin":{"kind":"agent"}}
|
|
92
|
+
artifact.put: {"operation":"artifact.put","dealId":"<uuid>","kind":"deck","title":"deck.pdf","mimeType":"application/pdf","storageKey":"<drive-file-id>","storageUrl":"https://drive.google.com/...","byteSize":123,"sha256":"<64 hex>","origin":{"kind":"user","originalUserUtterance":"..."}}
|
|
93
|
+
|
|
94
|
+
For Information, origin records who caused the write and preserves user words;
|
|
95
|
+
value.source records where the evidence came from.
|
|
77
96
|
|
|
78
97
|
Core owns Chat Records, append-only Deal Events, Drive provisioning, audit,
|
|
79
98
|
and idempotency. User-originated work must preserve exact wording in
|
|
@@ -117,6 +136,28 @@ function assertDealAction(area, action) {
|
|
|
117
136
|
}
|
|
118
137
|
}
|
|
119
138
|
|
|
139
|
+
const RETIRED_TOP_LEVEL_COMMANDS = Object.freeze({
|
|
140
|
+
brief: "llama deal write --json (operation: page.patch)",
|
|
141
|
+
fact: "llama deal write --json (operation: information.put)",
|
|
142
|
+
post: "llama deal write --json (operation: input.submit)",
|
|
143
|
+
timeline: "llama deal read <dealId> --detail history",
|
|
144
|
+
html: "llama deal write --json (operation: artifact.put)",
|
|
145
|
+
memo: "llama deal write --json (operation: artifact.put)",
|
|
146
|
+
pipeline: "llama deal search|read|create|write",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
function unknownCommandError(area, action) {
|
|
150
|
+
const replacement = RETIRED_TOP_LEVEL_COMMANDS[area];
|
|
151
|
+
if (replacement) {
|
|
152
|
+
return new Error(
|
|
153
|
+
`\`llama ${area}\` was retired in CLI 2.0.\n` +
|
|
154
|
+
` → Use: ${replacement}\n` +
|
|
155
|
+
" → Contract: llama agent bootstrap",
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return new Error(`Unknown command: llama ${[area, action].filter(Boolean).join(" ")}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
120
161
|
function onboardingNoAuth() {
|
|
121
162
|
return `Llama team onboarding requires credentials.
|
|
122
163
|
|
|
@@ -431,7 +472,7 @@ async function main() {
|
|
|
431
472
|
const params = new URLSearchParams({ clientVersion: PKG_VERSION });
|
|
432
473
|
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
433
474
|
const manifest = await request("GET", `/api/agent/manifest?${params}`);
|
|
434
|
-
if (flags.json) print(manifest);
|
|
475
|
+
if (flags.json) print(structuredBootstrapManifest(manifest));
|
|
435
476
|
else process.stdout.write(`${manifest.briefing || JSON.stringify(manifest, null, 2)}\n`);
|
|
436
477
|
return;
|
|
437
478
|
}
|
|
@@ -529,7 +570,9 @@ async function main() {
|
|
|
529
570
|
if (area === "deal" && ["create", "write"].includes(action)) {
|
|
530
571
|
const { flags } = parseFlags(rest, ["json"]);
|
|
531
572
|
const input = await readJsonInput(flags.json);
|
|
532
|
-
|
|
573
|
+
const command = prepareDealCommand(action, input);
|
|
574
|
+
const response = await request("POST", "/api/occam/deals/commands", command);
|
|
575
|
+
print(compactDealWriteResult(command, response));
|
|
533
576
|
return;
|
|
534
577
|
}
|
|
535
578
|
|
|
@@ -555,12 +598,12 @@ async function main() {
|
|
|
555
598
|
return;
|
|
556
599
|
}
|
|
557
600
|
|
|
558
|
-
throw
|
|
601
|
+
throw unknownCommandError(area, action);
|
|
559
602
|
}
|
|
560
603
|
|
|
561
604
|
main()
|
|
562
605
|
.then(() => maybeNudgeUpdate())
|
|
563
606
|
.catch((error) => {
|
|
564
|
-
console.error(
|
|
607
|
+
console.error(formatErrorForDisplay(error));
|
|
565
608
|
process.exit(1);
|
|
566
609
|
});
|
package/lib/build-manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "llama.cli-build.v1",
|
|
3
3
|
"packageName": "@llamaventures/cli",
|
|
4
|
-
"packageVersion": "2.0.
|
|
5
|
-
"sourceSha": "
|
|
4
|
+
"packageVersion": "2.0.3",
|
|
5
|
+
"sourceSha": "dc1618ab65b6cb2e73b107e7d56315ce2076744e",
|
|
6
6
|
"sourceKind": "github",
|
|
7
7
|
"sourceDirty": false,
|
|
8
8
|
"coreApiContract": {
|
package/lib/client.mjs
CHANGED
|
@@ -534,7 +534,21 @@ function apiResponseError(data, status) {
|
|
|
534
534
|
return error;
|
|
535
535
|
}
|
|
536
536
|
if (data?.code !== "MENTION_APPROVAL_REQUIRED") {
|
|
537
|
-
const
|
|
537
|
+
const issues = Array.isArray(data?.issues)
|
|
538
|
+
? data.issues.map((issue) => {
|
|
539
|
+
const path = Array.isArray(issue?.path) && issue.path.length
|
|
540
|
+
? issue.path.reduce(
|
|
541
|
+
(result, part) => typeof part === "number"
|
|
542
|
+
? `${result}[${part}]`
|
|
543
|
+
: result ? `${result}.${part}` : String(part),
|
|
544
|
+
"",
|
|
545
|
+
)
|
|
546
|
+
: "$";
|
|
547
|
+
const detail = issue?.message || issue?.code || "Invalid value";
|
|
548
|
+
return `- ${path}: ${detail}`;
|
|
549
|
+
})
|
|
550
|
+
: [];
|
|
551
|
+
const error = new Error([message, ...issues].join("\n"));
|
|
538
552
|
if (typeof data?.code === "string") error.code = data.code;
|
|
539
553
|
error.details = data;
|
|
540
554
|
error.status = status;
|
|
@@ -563,6 +577,21 @@ function apiResponseError(data, status) {
|
|
|
563
577
|
return error;
|
|
564
578
|
}
|
|
565
579
|
|
|
580
|
+
export function formatErrorForDisplay(error) {
|
|
581
|
+
const message = error?.message ?? String(error);
|
|
582
|
+
if (/^Error(?:\[[^\]]+\])?:/u.test(message)) return message;
|
|
583
|
+
const code = typeof error?.code === "string" ? `[${error.code}]` : "";
|
|
584
|
+
return `Error${code}: ${message}`;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
export function structuredBootstrapManifest(manifest) {
|
|
588
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
589
|
+
return manifest;
|
|
590
|
+
}
|
|
591
|
+
const { briefing: _renderedDuplicate, ...structured } = manifest;
|
|
592
|
+
return structured;
|
|
593
|
+
}
|
|
594
|
+
|
|
566
595
|
async function requestWithRetry(method, endpoint, body, opts, allowRetry) {
|
|
567
596
|
const authHeaders = await getAuthHeaders();
|
|
568
597
|
if (Object.keys(authHeaders).length === 0) throw noAuthError();
|
package/lib/deal-actions.mjs
CHANGED
|
@@ -72,62 +72,6 @@ function validateLocalizedTextPairs(value, path = "page") {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
const LANGUAGE_NEUTRAL_PAGE_KEYS = new Set([
|
|
76
|
-
"companyName", "companyKey", "state", "pipelineStatus", "stage",
|
|
77
|
-
"theirStage", "ownerUserId", "sourceUserId", "website", "dealOwner",
|
|
78
|
-
"source", "sourceDirection", "proposedAmount", "roundSize", "valuation",
|
|
79
|
-
"founders", "deckLink", "location", "foundedYear", "leadInvestor",
|
|
80
|
-
"dateAdded", "tab", "sourcePerson", "createdByName", "createdByLabel",
|
|
81
|
-
"createdVia", "id", "name", "key", "slug", "url", "href", "email",
|
|
82
|
-
"linkedin", "handle", "status", "kind", "type", "tier", "confidence",
|
|
83
|
-
"currency", "unit", "format", "mimeType", "sha256", "timestamp",
|
|
84
|
-
"date", "year",
|
|
85
|
-
]);
|
|
86
|
-
const LANGUAGE_NEUTRAL_PAGE_CONTAINERS = new Set([
|
|
87
|
-
"manualTags", "investors", "founderEmails", "labels", "tags",
|
|
88
|
-
]);
|
|
89
|
-
const OPAQUE_PAGE_CONTAINERS = new Set([
|
|
90
|
-
"migration", "extra", "fieldProvenance",
|
|
91
|
-
]);
|
|
92
|
-
|
|
93
|
-
function pathKeys(path) {
|
|
94
|
-
return path.match(/[^.[\]]+/g)?.filter((part) => !/^\d+$/u.test(part)) ?? [];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function isLanguageNeutralPagePath(path) {
|
|
98
|
-
const keys = pathKeys(path);
|
|
99
|
-
if (keys.some((key) => OPAQUE_PAGE_CONTAINERS.has(key))) return true;
|
|
100
|
-
if (keys.some((key) => LANGUAGE_NEUTRAL_PAGE_CONTAINERS.has(key))) return true;
|
|
101
|
-
const key = keys.at(-1) ?? "";
|
|
102
|
-
if (LANGUAGE_NEUTRAL_PAGE_KEYS.has(key)) return true;
|
|
103
|
-
return /(?:Id|Ids|URL|Url|URI|Uri|Link|Email|Emails|At|Date|Year|Key|Slug|Hash)$/u.test(key);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function validateAgentAuthoredPageProse(value, path = "page") {
|
|
107
|
-
if (typeof value === "string") {
|
|
108
|
-
if (!isLanguageNeutralPagePath(path)) {
|
|
109
|
-
throw new Error(
|
|
110
|
-
`${path} agent-authored prose must be one value with non-empty en and zh`,
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
if (Array.isArray(value)) {
|
|
116
|
-
value.forEach((item, index) => validateAgentAuthoredPageProse(item, `${path}[${index}]`));
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
if (!value || typeof value !== "object") return;
|
|
120
|
-
if (
|
|
121
|
-
Object.prototype.hasOwnProperty.call(value, "en") ||
|
|
122
|
-
Object.prototype.hasOwnProperty.call(value, "zh")
|
|
123
|
-
) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
for (const [key, child] of Object.entries(value)) {
|
|
127
|
-
validateAgentAuthoredPageProse(child, `${path}.${key}`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
75
|
export async function readJsonInput(path, stdin = process.stdin) {
|
|
132
76
|
if (!path || path === true) throw new Error("--json <file|-> is required");
|
|
133
77
|
const text = path === "-"
|
|
@@ -170,6 +114,32 @@ export function buildDealReadPath(dealId, detail = "overview") {
|
|
|
170
114
|
return `/api/occam/deals/${encodeURIComponent(dealId)}${expansion ? `?expand=${expansion}` : ""}`;
|
|
171
115
|
}
|
|
172
116
|
|
|
117
|
+
/**
|
|
118
|
+
* page.patch previously echoed the entire Live Deal Page after every write.
|
|
119
|
+
* Keep the authoritative revision receipt and tell the Agent exactly what to
|
|
120
|
+
* read back, without paying another full-page context cost before verification.
|
|
121
|
+
*/
|
|
122
|
+
export function compactDealWriteResult(command, response) {
|
|
123
|
+
if (command?.operation !== "page.patch" || !response?.result?.page) return response;
|
|
124
|
+
const page = response.result.page;
|
|
125
|
+
return {
|
|
126
|
+
...response,
|
|
127
|
+
result: {
|
|
128
|
+
idempotent: Boolean(response.result.idempotent),
|
|
129
|
+
page: {
|
|
130
|
+
id: page.id,
|
|
131
|
+
revision: page.revision,
|
|
132
|
+
updatedAt: page.updatedAt,
|
|
133
|
+
},
|
|
134
|
+
verify: {
|
|
135
|
+
dealId: command.dealId,
|
|
136
|
+
fields: Object.keys(command.patch ?? {}),
|
|
137
|
+
command: `llama deal read ${command.dealId}`,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
173
143
|
export function prepareDealCommand(action, input) {
|
|
174
144
|
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
175
145
|
throw new Error("Mutation JSON must be an object");
|
|
@@ -183,11 +153,9 @@ export function prepareDealCommand(action, input) {
|
|
|
183
153
|
}
|
|
184
154
|
if (action === "create") {
|
|
185
155
|
validateLocalizedTextPairs(command.page ?? {});
|
|
186
|
-
validateAgentAuthoredPageProse(command.page ?? {});
|
|
187
156
|
}
|
|
188
157
|
if (action === "write" && command.operation === "page.patch") {
|
|
189
158
|
validateLocalizedTextPairs(command.patch ?? {});
|
|
190
|
-
validateAgentAuthoredPageProse(command.patch ?? {});
|
|
191
159
|
}
|
|
192
160
|
return ensureIdempotencyKey(validateCommandOrigin(command));
|
|
193
161
|
}
|