@llamaventures/cli 2.0.2 → 2.1.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 +10 -1
- package/CHANGELOG.md +39 -14
- package/README.md +27 -3
- package/README.zh-CN.md +23 -1
- package/bin/llama-mcp.mjs +100 -31
- package/bin/llama.mjs +87 -7
- package/contracts/core-api.json +4 -4
- package/contracts/required-operations.json +3 -1
- package/lib/build-manifest.json +4 -4
- package/lib/client.mjs +33 -1
- package/lib/deal-actions.mjs +26 -58
- package/lib/deal-memory-actions.mjs +31 -0
- package/package.json +2 -2
package/AGENT_BRIEFING.md
CHANGED
|
@@ -44,6 +44,14 @@ working memory; it never becomes Page automatically. Page is the bounded,
|
|
|
44
44
|
human-visible current projection, so patch the exact semantic fields described
|
|
45
45
|
by the live contract instead of hiding structured analysis in `notes`.
|
|
46
46
|
|
|
47
|
+
The Markdown Deal Story is a separate sidecar domain, not a sixth Occam
|
|
48
|
+
resource. Use `get_deal_memory` / `llama memory read` for accumulated narrative
|
|
49
|
+
understanding, and `update_deal_memory` / `llama memory write` to replace its
|
|
50
|
+
complete Markdown. Read first and pass the returned opaque version on every
|
|
51
|
+
update; a conflict means read again and reconcile. These calls still go only
|
|
52
|
+
through authenticated Core. Never request AWS credentials or call S3 or the
|
|
53
|
+
sidecar directly.
|
|
54
|
+
|
|
47
55
|
Every Page content field is Agent-writable. A field's `author` or
|
|
48
56
|
`semanticOwner` describes attribution, not permission. Preserve a real human
|
|
49
57
|
judgment with its author; otherwise label the value as an Agent proposal / 待人确认.
|
|
@@ -207,4 +215,5 @@ artifact, then decide whether its content deserves Information or a Page patch.
|
|
|
207
215
|
`DEAL_COMMAND_RETIRED`. Upgrade the CLI and retry the intended four-action
|
|
208
216
|
command.
|
|
209
217
|
- Wiki, admin audit, external pitch, authentication, and skill discovery are
|
|
210
|
-
separate domains.
|
|
218
|
+
separate domains. Deal Memory is also separate. None expands the four-action
|
|
219
|
+
Occam Deal space.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
All notable changes to `@llamaventures/cli` are documented here.
|
|
4
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
5
|
+
this project adheres to [Semantic Versioning](https://semver.org).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [2.1.0] - 2026-09-03
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Add `llama memory read|write` and matching `get_deal_memory` /
|
|
13
|
+
`update_deal_memory` MCP tools for one canonical Markdown Deal Story, using
|
|
14
|
+
the existing Llama login through Core's authenticated sidecar adapter.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Make the authenticated live bootstrap mandatory before Deal work and clarify
|
|
18
|
+
that it loads the server-owned two-part private Brain plus the separate Live
|
|
19
|
+
Deal Page field contract. CLI remains only the tool/transport layer.
|
|
20
|
+
- Teach CLI and MCP Agents that Information never updates Page automatically,
|
|
21
|
+
every Page content field is Agent-writable, and field authorship controls
|
|
22
|
+
attribution rather than permission.
|
|
23
|
+
- Pin the additive Deal Memory operations to Core API 5.1.0 while preserving
|
|
24
|
+
exactly four Occam Deal actions and five Occam resources.
|
|
25
|
+
|
|
26
|
+
## [2.0.3] - 2026-08-31
|
|
27
|
+
|
|
28
|
+
- Show Core validation codes and field paths instead of collapsing malformed
|
|
29
|
+
Deal writes into `Invalid Occam command`.
|
|
30
|
+
- Publish `write_deal` as four operation-specific MCP command shapes while
|
|
31
|
+
retaining one mutation tool.
|
|
32
|
+
- Add copyable write examples to `llama help deal` and omit the duplicate
|
|
33
|
+
rendered briefing from structured bootstrap output.
|
|
34
|
+
- Let Core's private Page contract classify prose and enums instead of keeping
|
|
35
|
+
a second, drifting public-CLI field whitelist.
|
|
36
|
+
- Return a compact `page.patch` revision receipt with exact read-back fields
|
|
37
|
+
instead of echoing the full Live Deal Page after every write.
|
|
38
|
+
- Turn retired top-level commands such as `llama brief`, `fact`, `post`,
|
|
39
|
+
`timeline`, `html`, `memo`, and `pipeline` into self-correcting errors that
|
|
40
|
+
name the CLI 2 replacement and live contract entrypoint.
|
|
41
|
+
|
|
3
42
|
## [2.0.2] - 2026-08-31
|
|
4
43
|
|
|
5
44
|
- Correct the authenticated runtime model: Brain has exactly two private
|
|
@@ -33,20 +72,6 @@
|
|
|
33
72
|
- Keep Wiki, admin audit, external pitch, authentication, preferences, and
|
|
34
73
|
progressive skill discovery as separate non-Deal domains.
|
|
35
74
|
|
|
36
|
-
All notable changes to `@llamaventures/cli` are documented here.
|
|
37
|
-
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
38
|
-
this project adheres to [Semantic Versioning](https://semver.org).
|
|
39
|
-
|
|
40
|
-
## [Unreleased]
|
|
41
|
-
|
|
42
|
-
### Changed
|
|
43
|
-
- Make the authenticated live bootstrap mandatory before Deal work and clarify
|
|
44
|
-
that it loads the server-owned two-part private Brain plus the separate Live
|
|
45
|
-
Deal Page field contract. CLI remains only the tool/transport layer.
|
|
46
|
-
- Teach CLI and MCP Agents that Information never updates Page automatically,
|
|
47
|
-
every Page content field is Agent-writable, and field authorship controls
|
|
48
|
-
attribution rather than permission.
|
|
49
|
-
|
|
50
75
|
## [1.26.0] - 2026-08-11
|
|
51
76
|
|
|
52
77
|
### 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:
|
|
@@ -121,6 +122,27 @@ For user-originated work, preserve the exact words in
|
|
|
121
122
|
Chat and Event are system-owned. There is no caller-controlled Event append or
|
|
122
123
|
general Chat-forging operation.
|
|
123
124
|
|
|
125
|
+
## Deal Memory sidecar
|
|
126
|
+
|
|
127
|
+
Deal Memory is a separate domain alongside the five-resource Deal model. Each
|
|
128
|
+
deal can have one canonical, human-readable Markdown Deal Story:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
llama memory read <dealId>
|
|
132
|
+
llama memory read <dealId> --raw
|
|
133
|
+
llama memory write <dealId> --markdown deal-story.md
|
|
134
|
+
llama memory write <dealId> --markdown deal-story.md --expected-version '"etag-from-read"'
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The first write omits `--expected-version`. Updating an existing story requires
|
|
138
|
+
the opaque `version` returned by `read`; a stale value fails instead of losing a
|
|
139
|
+
concurrent edit. This uses the same `llama auth login` identity. CLI calls the
|
|
140
|
+
authenticated Command Core adapter only—it has no sidecar URL, service token,
|
|
141
|
+
S3 credentials, or direct database access.
|
|
142
|
+
|
|
143
|
+
`llama deal read --detail memory` still means structured Deal Information.
|
|
144
|
+
`llama memory read` means the separate accumulated Markdown Deal Story.
|
|
145
|
+
|
|
124
146
|
## Five Deal resources
|
|
125
147
|
|
|
126
148
|
1. Live Deal Page — current human-visible state.
|
|
@@ -148,7 +170,9 @@ Its Deal surface is also exactly four tools:
|
|
|
148
170
|
- `write_deal`
|
|
149
171
|
|
|
150
172
|
Authentication, skill discovery, Wiki, admin audit, preferences, and external
|
|
151
|
-
pitch remain separate non-Deal domains.
|
|
173
|
+
pitch remain separate non-Deal domains. Deal Memory likewise has two separate
|
|
174
|
+
MCP tools, `get_deal_memory` and `update_deal_memory`; it does not add a fifth
|
|
175
|
+
Occam Deal tool.
|
|
152
176
|
|
|
153
177
|
## Agent bootstrap
|
|
154
178
|
|
package/README.zh-CN.md
CHANGED
|
@@ -116,6 +116,26 @@ llama deal write --json write.json
|
|
|
116
116
|
Chat 和 Event 由系统拥有。Agent 不能自行伪造 Event 类型、顺序、作者、
|
|
117
117
|
时间,也没有通用的 Chat 写入工具。
|
|
118
118
|
|
|
119
|
+
## Deal Memory sidecar
|
|
120
|
+
|
|
121
|
+
Deal Memory 是与五资源 Deal 模型并行、完全解耦的领域。每个 Deal 可以有一个
|
|
122
|
+
人和 Agent 都能直接阅读的 Markdown Deal Story:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
llama memory read <dealId>
|
|
126
|
+
llama memory read <dealId> --raw
|
|
127
|
+
llama memory write <dealId> --markdown deal-story.md
|
|
128
|
+
llama memory write <dealId> --markdown deal-story.md --expected-version '"read 返回的 etag"'
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
第一次创建时不传 `--expected-version`;以后更新必须使用前一次 `read` 返回的
|
|
132
|
+
不透明 `version`,旧版本会安全失败,不会覆盖他人的新内容。它复用同一个
|
|
133
|
+
`llama auth login` 身份。CLI 只调用经过认证的 Command Core adapter,不持有
|
|
134
|
+
sidecar URL、service token、S3 凭证,也不直接访问数据库。
|
|
135
|
+
|
|
136
|
+
`llama deal read --detail memory` 仍表示结构化 Deal Information;
|
|
137
|
+
`llama memory read` 表示独立的 Markdown Deal Story。
|
|
138
|
+
|
|
119
139
|
## Deal 只有五种业务资源
|
|
120
140
|
|
|
121
141
|
1. Live Deal Page:人类看到的当前状态;
|
|
@@ -141,7 +161,9 @@ MCP 的 Deal 工具同样恰好四个:
|
|
|
141
161
|
- `write_deal`
|
|
142
162
|
|
|
143
163
|
认证、skill discovery、Wiki、admin audit、preferences 和 external pitch
|
|
144
|
-
属于其他独立领域,不会扩大 Deal action space。
|
|
164
|
+
属于其他独立领域,不会扩大 Deal action space。Deal Memory 同样是独立领域,
|
|
165
|
+
对应 `get_deal_memory` 和 `update_deal_memory` 两个 MCP 工具,并不增加第五个
|
|
166
|
+
Occam Deal 工具。
|
|
145
167
|
|
|
146
168
|
## Agent 启动
|
|
147
169
|
|
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,8 +22,10 @@ import {
|
|
|
20
22
|
import {
|
|
21
23
|
buildDealReadPath,
|
|
22
24
|
buildDealSearchPath,
|
|
25
|
+
compactDealWriteResult,
|
|
23
26
|
prepareDealCommand,
|
|
24
27
|
} from "../lib/deal-actions.mjs";
|
|
28
|
+
import { buildDealMemoryPath } from "../lib/deal-memory-actions.mjs";
|
|
25
29
|
|
|
26
30
|
const requireFromHere = createRequire(import.meta.url);
|
|
27
31
|
const { version: PKG_VERSION } = requireFromHere("../package.json");
|
|
@@ -36,12 +40,12 @@ function textResult(text, isError = false) {
|
|
|
36
40
|
};
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
async function callApi(method, path, body) {
|
|
43
|
+
async function callApi(method, path, body, transform = (value) => value) {
|
|
40
44
|
try {
|
|
41
|
-
const result = await request(method, path, body);
|
|
45
|
+
const result = transform(await request(method, path, body));
|
|
42
46
|
return textResult(typeof result === "string" ? result : JSON.stringify(result, null, 2));
|
|
43
47
|
} catch (error) {
|
|
44
|
-
return textResult(
|
|
48
|
+
return textResult(formatErrorForDisplay(error), true);
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
51
|
|
|
@@ -51,6 +55,54 @@ const originSchema = z.object({
|
|
|
51
55
|
originatingChatRecordId: z.string().uuid().optional(),
|
|
52
56
|
});
|
|
53
57
|
|
|
58
|
+
const writeDealCommandSchema = z.discriminatedUnion("operation", [
|
|
59
|
+
z.object({
|
|
60
|
+
operation: z.literal("input.submit"),
|
|
61
|
+
dealId: z.string().uuid(),
|
|
62
|
+
format: z.string().min(1).max(120).optional(),
|
|
63
|
+
content: z.any(),
|
|
64
|
+
source: z.record(z.string(), z.any()).optional(),
|
|
65
|
+
origin: originSchema,
|
|
66
|
+
idempotencyKey: z.string().min(1).max(240).optional(),
|
|
67
|
+
}).strict(),
|
|
68
|
+
z.object({
|
|
69
|
+
operation: z.literal("information.put"),
|
|
70
|
+
dealId: z.string().uuid(),
|
|
71
|
+
informationId: z.string().uuid().optional(),
|
|
72
|
+
type: z.string().min(1).max(120),
|
|
73
|
+
labels: z.array(z.string()).optional(),
|
|
74
|
+
subject: z.record(z.string(), z.any()).optional(),
|
|
75
|
+
value: z.any(),
|
|
76
|
+
expectedVersion: z.number().int().positive().optional(),
|
|
77
|
+
origin: originSchema,
|
|
78
|
+
idempotencyKey: z.string().min(1).max(240).optional(),
|
|
79
|
+
}).strict(),
|
|
80
|
+
z.object({
|
|
81
|
+
operation: z.literal("page.patch"),
|
|
82
|
+
dealId: z.string().uuid(),
|
|
83
|
+
patch: z.record(z.string(), z.any()),
|
|
84
|
+
expectedRevision: z.number().int().nonnegative().optional(),
|
|
85
|
+
origin: originSchema,
|
|
86
|
+
idempotencyKey: z.string().min(1).max(240).optional(),
|
|
87
|
+
}).strict(),
|
|
88
|
+
z.object({
|
|
89
|
+
operation: z.literal("artifact.put"),
|
|
90
|
+
dealId: z.string().uuid(),
|
|
91
|
+
artifactId: z.string().uuid().optional(),
|
|
92
|
+
kind: z.string().min(1).max(120),
|
|
93
|
+
title: z.string().min(1).max(500),
|
|
94
|
+
mimeType: z.string().min(1).max(240),
|
|
95
|
+
contentBase64: z.string().min(1).optional(),
|
|
96
|
+
storageKey: z.string().min(1).optional(),
|
|
97
|
+
storageUrl: z.string().url().optional(),
|
|
98
|
+
byteSize: z.number().int().nonnegative().optional(),
|
|
99
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
|
|
100
|
+
metadata: z.record(z.string(), z.any()).optional(),
|
|
101
|
+
origin: originSchema,
|
|
102
|
+
idempotencyKey: z.string().min(1).max(240).optional(),
|
|
103
|
+
}).strict(),
|
|
104
|
+
]);
|
|
105
|
+
|
|
54
106
|
// Deal is deliberately a closed four-tool surface. Do not add split resource
|
|
55
107
|
// tools here: one more tool is one more branch every agent must reason about.
|
|
56
108
|
server.registerTool(
|
|
@@ -70,7 +122,7 @@ server.registerTool(
|
|
|
70
122
|
server.registerTool(
|
|
71
123
|
"read_deal",
|
|
72
124
|
{
|
|
73
|
-
description: "Read one Deal. Live Page
|
|
125
|
+
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
126
|
inputSchema: {
|
|
75
127
|
dealId: z.string().min(1),
|
|
76
128
|
detail: z.enum(["overview", "memory", "files", "conversation", "history", "all"]).optional(),
|
|
@@ -99,36 +151,48 @@ server.registerTool(
|
|
|
99
151
|
server.registerTool(
|
|
100
152
|
"write_deal",
|
|
101
153
|
{
|
|
102
|
-
description: "The only Deal mutation tool: input.submit, information.put, page.patch, or artifact.put. All Page content fields are Agent-writable; author controls attribution, not permission. Use the
|
|
154
|
+
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.",
|
|
155
|
+
// MCP SDK 1.30 only publishes object schemas at the tool root. Nesting one
|
|
156
|
+
// command preserves the real discriminated union instead of flattening four
|
|
157
|
+
// incompatible operations into false affordances.
|
|
158
|
+
inputSchema: { command: writeDealCommandSchema },
|
|
159
|
+
},
|
|
160
|
+
async ({ command }) => callApi(
|
|
161
|
+
"POST",
|
|
162
|
+
"/api/occam/deals/commands",
|
|
163
|
+
prepareDealCommand("write", command),
|
|
164
|
+
(result) => compactDealWriteResult(command, result),
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Deal Memory is a separate sidecar domain. These do not add an Occam Deal
|
|
169
|
+
// business resource or widen the deliberately closed four-tool Deal surface.
|
|
170
|
+
server.registerTool(
|
|
171
|
+
"get_deal_memory",
|
|
172
|
+
{
|
|
173
|
+
description: "Read the complete canonical Markdown Deal Story and its opaque concurrency version.",
|
|
174
|
+
inputSchema: { dealId: z.string().uuid() },
|
|
175
|
+
},
|
|
176
|
+
// @core-api-operation GET /api/deal-memory/{dealId}/story
|
|
177
|
+
async ({ dealId }) => callApi("GET", buildDealMemoryPath(dealId)),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
server.registerTool(
|
|
181
|
+
"update_deal_memory",
|
|
182
|
+
{
|
|
183
|
+
description: "Create or replace the complete Markdown Deal Story. Existing stories require the version returned by get_deal_memory; stale writes fail safely.",
|
|
103
184
|
inputSchema: {
|
|
104
|
-
operation: z.enum(["input.submit", "information.put", "page.patch", "artifact.put"]),
|
|
105
185
|
dealId: z.string().uuid(),
|
|
106
|
-
|
|
107
|
-
|
|
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(),
|
|
186
|
+
markdown: z.string().min(1).max(1_048_576),
|
|
187
|
+
expected_version: z.string().min(1).optional(),
|
|
129
188
|
},
|
|
130
189
|
},
|
|
131
|
-
|
|
190
|
+
// @core-api-operation PUT /api/deal-memory/{dealId}/story
|
|
191
|
+
async ({ dealId, markdown, expected_version }) => callApi(
|
|
192
|
+
"PUT",
|
|
193
|
+
buildDealMemoryPath(dealId),
|
|
194
|
+
{ markdown, ...(expected_version ? { expected_version } : {}) },
|
|
195
|
+
),
|
|
132
196
|
);
|
|
133
197
|
|
|
134
198
|
server.registerTool(
|
|
@@ -158,7 +222,12 @@ server.registerTool(
|
|
|
158
222
|
async ({ limit } = {}) => {
|
|
159
223
|
const params = new URLSearchParams({ clientVersion: PKG_VERSION });
|
|
160
224
|
if (limit) params.set("limit", String(limit));
|
|
161
|
-
|
|
225
|
+
try {
|
|
226
|
+
const result = await request("GET", `/api/agent/manifest?${params}`);
|
|
227
|
+
return textResult(JSON.stringify(structuredBootstrapManifest(result), null, 2));
|
|
228
|
+
} catch (error) {
|
|
229
|
+
return textResult(formatErrorForDisplay(error), true);
|
|
230
|
+
}
|
|
162
231
|
},
|
|
163
232
|
);
|
|
164
233
|
|
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,9 +35,14 @@ 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";
|
|
42
|
+
import {
|
|
43
|
+
buildDealMemoryPath,
|
|
44
|
+
readMarkdownInput,
|
|
45
|
+
} from "../lib/deal-memory-actions.mjs";
|
|
39
46
|
|
|
40
47
|
const requireFromHere = createRequire(import.meta.url);
|
|
41
48
|
const { version: PKG_VERSION } = requireFromHere("../package.json");
|
|
@@ -48,6 +55,10 @@ Deal has exactly four actions:
|
|
|
48
55
|
llama deal create --json <file|->
|
|
49
56
|
llama deal write --json <file|->
|
|
50
57
|
|
|
58
|
+
Deal Memory is a separate sidecar domain through the same login:
|
|
59
|
+
llama memory read <dealId> [--raw]
|
|
60
|
+
llama memory write <dealId> --markdown <file|-> [--expected-version <version>]
|
|
61
|
+
|
|
51
62
|
Separate preserved domains:
|
|
52
63
|
llama auth status|login|logout
|
|
53
64
|
llama token set|show
|
|
@@ -82,13 +93,32 @@ read-modify-write complete Page arrays and preserve sibling slots.
|
|
|
82
93
|
create JSON:
|
|
83
94
|
{"companyName":"Acme","page":{},"information":[],"origin":{"kind":"user","originalUserUtterance":"..."}}
|
|
84
95
|
|
|
85
|
-
write
|
|
86
|
-
input.submit
|
|
96
|
+
write JSON (CLI derives idempotencyKey when omitted):
|
|
97
|
+
input.submit: {"operation":"input.submit","dealId":"<uuid>","content":"<original input>","format":"text","source":{},"origin":{"kind":"user","originalUserUtterance":"..."}}
|
|
98
|
+
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":"..."}}
|
|
99
|
+
page.patch: {"operation":"page.patch","dealId":"<uuid>","patch":{"description":{"en":"...","zh":"..."}},"origin":{"kind":"agent"}}
|
|
100
|
+
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":"..."}}
|
|
101
|
+
|
|
102
|
+
For Information, origin records who caused the write and preserves user words;
|
|
103
|
+
value.source records where the evidence came from.
|
|
87
104
|
|
|
88
105
|
Core owns Chat Records, append-only Deal Events, Drive provisioning, audit,
|
|
89
106
|
and idempotency. User-originated work must preserve exact wording in
|
|
90
107
|
origin.originalUserUtterance or reference origin.originatingChatRecordId.`;
|
|
91
108
|
|
|
109
|
+
const HELP_MEMORY = `Llama Deal Memory — one canonical Markdown Deal Story
|
|
110
|
+
|
|
111
|
+
llama memory read <dealId> [--raw]
|
|
112
|
+
llama memory write <dealId> --markdown <file|-> [--expected-version <version>]
|
|
113
|
+
|
|
114
|
+
Read returns the full JSON record, including its opaque version. --raw prints
|
|
115
|
+
only the exact Markdown. A first write creates the story without a version.
|
|
116
|
+
Every later write must pass the version from the preceding read; stale writes
|
|
117
|
+
fail instead of overwriting newer understanding.
|
|
118
|
+
|
|
119
|
+
This uses your existing Llama authentication. The CLI talks only to Command
|
|
120
|
+
Core and never receives Deal Memory service credentials or AWS access.`;
|
|
121
|
+
|
|
92
122
|
function parseFlags(args, allowed = null) {
|
|
93
123
|
const flags = {};
|
|
94
124
|
const positional = [];
|
|
@@ -115,7 +145,7 @@ function parseFlags(args, allowed = null) {
|
|
|
115
145
|
}
|
|
116
146
|
|
|
117
147
|
function usage(area) {
|
|
118
|
-
console.log(area === "deal" ? HELP_DEAL : HELP_ROOT);
|
|
148
|
+
console.log(area === "deal" ? HELP_DEAL : area === "memory" ? HELP_MEMORY : HELP_ROOT);
|
|
119
149
|
}
|
|
120
150
|
|
|
121
151
|
function assertDealAction(area, action) {
|
|
@@ -127,6 +157,28 @@ function assertDealAction(area, action) {
|
|
|
127
157
|
}
|
|
128
158
|
}
|
|
129
159
|
|
|
160
|
+
const RETIRED_TOP_LEVEL_COMMANDS = Object.freeze({
|
|
161
|
+
brief: "llama deal write --json (operation: page.patch)",
|
|
162
|
+
fact: "llama deal write --json (operation: information.put)",
|
|
163
|
+
post: "llama deal write --json (operation: input.submit)",
|
|
164
|
+
timeline: "llama deal read <dealId> --detail history",
|
|
165
|
+
html: "llama deal write --json (operation: artifact.put)",
|
|
166
|
+
memo: "llama deal write --json (operation: artifact.put)",
|
|
167
|
+
pipeline: "llama deal search|read|create|write",
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
function unknownCommandError(area, action) {
|
|
171
|
+
const replacement = RETIRED_TOP_LEVEL_COMMANDS[area];
|
|
172
|
+
if (replacement) {
|
|
173
|
+
return new Error(
|
|
174
|
+
`\`llama ${area}\` was retired in CLI 2.0.\n` +
|
|
175
|
+
` → Use: ${replacement}\n` +
|
|
176
|
+
" → Contract: llama agent bootstrap",
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
return new Error(`Unknown command: llama ${[area, action].filter(Boolean).join(" ")}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
130
182
|
function onboardingNoAuth() {
|
|
131
183
|
return `Llama team onboarding requires credentials.
|
|
132
184
|
|
|
@@ -441,7 +493,7 @@ async function main() {
|
|
|
441
493
|
const params = new URLSearchParams({ clientVersion: PKG_VERSION });
|
|
442
494
|
if (flags.limit && flags.limit !== true) params.set("limit", String(flags.limit));
|
|
443
495
|
const manifest = await request("GET", `/api/agent/manifest?${params}`);
|
|
444
|
-
if (flags.json) print(manifest);
|
|
496
|
+
if (flags.json) print(structuredBootstrapManifest(manifest));
|
|
445
497
|
else process.stdout.write(`${manifest.briefing || JSON.stringify(manifest, null, 2)}\n`);
|
|
446
498
|
return;
|
|
447
499
|
}
|
|
@@ -539,9 +591,37 @@ async function main() {
|
|
|
539
591
|
if (area === "deal" && ["create", "write"].includes(action)) {
|
|
540
592
|
const { flags } = parseFlags(rest, ["json"]);
|
|
541
593
|
const input = await readJsonInput(flags.json);
|
|
542
|
-
|
|
594
|
+
const command = prepareDealCommand(action, input);
|
|
595
|
+
const response = await request("POST", "/api/occam/deals/commands", command);
|
|
596
|
+
print(compactDealWriteResult(command, response));
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (area === "memory" && action === "read") {
|
|
601
|
+
const { flags, positional } = parseFlags(rest, ["raw"]);
|
|
602
|
+
const path = buildDealMemoryPath(positional[0]);
|
|
603
|
+
// @core-api-operation GET /api/deal-memory/{dealId}/story
|
|
604
|
+
const result = await request("GET", path);
|
|
605
|
+
if (flags.raw) process.stdout.write(result.markdown);
|
|
606
|
+
else print(result);
|
|
543
607
|
return;
|
|
544
608
|
}
|
|
609
|
+
if (area === "memory" && action === "write") {
|
|
610
|
+
const { flags, positional } = parseFlags(rest, ["markdown", "expected-version"]);
|
|
611
|
+
const path = buildDealMemoryPath(positional[0]);
|
|
612
|
+
const markdown = await readMarkdownInput(flags.markdown);
|
|
613
|
+
// @core-api-operation PUT /api/deal-memory/{dealId}/story
|
|
614
|
+
print(await request("PUT", path, {
|
|
615
|
+
markdown,
|
|
616
|
+
...(typeof flags["expected-version"] === "string"
|
|
617
|
+
? { expected_version: flags["expected-version"] }
|
|
618
|
+
: {}),
|
|
619
|
+
}));
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (area === "memory") {
|
|
623
|
+
throw new Error("Memory actions: read, write.");
|
|
624
|
+
}
|
|
545
625
|
|
|
546
626
|
if (area === "wiki") {
|
|
547
627
|
await handleWiki(action, rest);
|
|
@@ -565,12 +645,12 @@ async function main() {
|
|
|
565
645
|
return;
|
|
566
646
|
}
|
|
567
647
|
|
|
568
|
-
throw
|
|
648
|
+
throw unknownCommandError(area, action);
|
|
569
649
|
}
|
|
570
650
|
|
|
571
651
|
main()
|
|
572
652
|
.then(() => maybeNudgeUpdate())
|
|
573
653
|
.catch((error) => {
|
|
574
|
-
console.error(
|
|
654
|
+
console.error(formatErrorForDisplay(error));
|
|
575
655
|
process.exit(1);
|
|
576
656
|
});
|
package/contracts/core-api.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "llama.core-api-contract.v1",
|
|
3
3
|
"name": "llama-core-api",
|
|
4
|
-
"apiVersion": "5.
|
|
4
|
+
"apiVersion": "5.1.0",
|
|
5
5
|
"openapiVersion": "3.0.3",
|
|
6
|
-
"sha256": "
|
|
7
|
-
"pathCount":
|
|
8
|
-
"operationCount":
|
|
6
|
+
"sha256": "948a8d02ae833e8cfa2302bc3c615fc1d3c792ae774ebc25b16be81fd1b85fee",
|
|
7
|
+
"pathCount": 111,
|
|
8
|
+
"operationCount": 138
|
|
9
9
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
{ "method": "GET", "path": "/api/agent/preferences" },
|
|
13
13
|
{ "method": "GET", "path": "/api/agent/skills" },
|
|
14
14
|
{ "method": "GET", "path": "/api/agent/skills/{slug}" },
|
|
15
|
+
{ "method": "GET", "path": "/api/deal-memory/{dealId}/story" },
|
|
15
16
|
{ "method": "GET", "path": "/api/me" },
|
|
16
17
|
{ "method": "GET", "path": "/api/oauth/authorize" },
|
|
17
18
|
{ "method": "GET", "path": "/api/occam/deals" },
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
{ "method": "POST", "path": "/api/oauth/token" },
|
|
29
30
|
{ "method": "POST", "path": "/api/occam/deals/commands" },
|
|
30
31
|
{ "method": "POST", "path": "/api/wiki/{slug}/restore" },
|
|
31
|
-
{ "method": "POST", "path": "/api/wiki/save" }
|
|
32
|
+
{ "method": "POST", "path": "/api/wiki/save" },
|
|
33
|
+
{ "method": "PUT", "path": "/api/deal-memory/{dealId}/story" }
|
|
32
34
|
]
|
|
33
35
|
}
|
package/lib/build-manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "llama.cli-build.v1",
|
|
3
3
|
"packageName": "@llamaventures/cli",
|
|
4
|
-
"packageVersion": "2.0
|
|
5
|
-
"sourceSha": "
|
|
4
|
+
"packageVersion": "2.1.0",
|
|
5
|
+
"sourceSha": "0a29fd1ecdfb2064a90d36e323549a0445b3a21e",
|
|
6
6
|
"sourceKind": "github",
|
|
7
7
|
"sourceDirty": false,
|
|
8
8
|
"coreApiContract": {
|
|
9
9
|
"format": "llama.core-api-contract.v1",
|
|
10
10
|
"name": "llama-core-api",
|
|
11
|
-
"apiVersion": "5.
|
|
11
|
+
"apiVersion": "5.1.0",
|
|
12
12
|
"openapiVersion": "3.0.3",
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "948a8d02ae833e8cfa2302bc3c615fc1d3c792ae774ebc25b16be81fd1b85fee"
|
|
14
14
|
}
|
|
15
15
|
}
|
package/lib/client.mjs
CHANGED
|
@@ -294,6 +294,9 @@ function inferCommand(method, endpoint) {
|
|
|
294
294
|
if (pathname === "/api/occam/deals") return "deal.search";
|
|
295
295
|
if (pathname === "/api/occam/deals/commands") return "deal.write";
|
|
296
296
|
if (/^\/api\/occam\/deals\/[^/]+$/.test(pathname)) return "deal.read";
|
|
297
|
+
if (/^\/api\/deal-memory\/[^/]+\/story$/.test(pathname)) {
|
|
298
|
+
return verb === "GET" ? "memory.read" : "memory.write";
|
|
299
|
+
}
|
|
297
300
|
if (pathname === "/api/me") return "auth.status";
|
|
298
301
|
if (pathname.startsWith("/api/agent/skills")) return "skills.read";
|
|
299
302
|
if (pathname === "/api/agent/manifest") return "agent.bootstrap";
|
|
@@ -534,7 +537,21 @@ function apiResponseError(data, status) {
|
|
|
534
537
|
return error;
|
|
535
538
|
}
|
|
536
539
|
if (data?.code !== "MENTION_APPROVAL_REQUIRED") {
|
|
537
|
-
const
|
|
540
|
+
const issues = Array.isArray(data?.issues)
|
|
541
|
+
? data.issues.map((issue) => {
|
|
542
|
+
const path = Array.isArray(issue?.path) && issue.path.length
|
|
543
|
+
? issue.path.reduce(
|
|
544
|
+
(result, part) => typeof part === "number"
|
|
545
|
+
? `${result}[${part}]`
|
|
546
|
+
: result ? `${result}.${part}` : String(part),
|
|
547
|
+
"",
|
|
548
|
+
)
|
|
549
|
+
: "$";
|
|
550
|
+
const detail = issue?.message || issue?.code || "Invalid value";
|
|
551
|
+
return `- ${path}: ${detail}`;
|
|
552
|
+
})
|
|
553
|
+
: [];
|
|
554
|
+
const error = new Error([message, ...issues].join("\n"));
|
|
538
555
|
if (typeof data?.code === "string") error.code = data.code;
|
|
539
556
|
error.details = data;
|
|
540
557
|
error.status = status;
|
|
@@ -563,6 +580,21 @@ function apiResponseError(data, status) {
|
|
|
563
580
|
return error;
|
|
564
581
|
}
|
|
565
582
|
|
|
583
|
+
export function formatErrorForDisplay(error) {
|
|
584
|
+
const message = error?.message ?? String(error);
|
|
585
|
+
if (/^Error(?:\[[^\]]+\])?:/u.test(message)) return message;
|
|
586
|
+
const code = typeof error?.code === "string" ? `[${error.code}]` : "";
|
|
587
|
+
return `Error${code}: ${message}`;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export function structuredBootstrapManifest(manifest) {
|
|
591
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
592
|
+
return manifest;
|
|
593
|
+
}
|
|
594
|
+
const { briefing: _renderedDuplicate, ...structured } = manifest;
|
|
595
|
+
return structured;
|
|
596
|
+
}
|
|
597
|
+
|
|
566
598
|
async function requestWithRetry(method, endpoint, body, opts, allowRetry) {
|
|
567
599
|
const authHeaders = await getAuthHeaders();
|
|
568
600
|
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
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
export const MAX_DEAL_STORY_BYTES = 1_048_576;
|
|
4
|
+
|
|
5
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
6
|
+
|
|
7
|
+
export function buildDealMemoryPath(dealId) {
|
|
8
|
+
if (!UUID.test(String(dealId || ""))) {
|
|
9
|
+
throw new Error("Deal Memory requires a valid deal UUID");
|
|
10
|
+
}
|
|
11
|
+
return `/api/deal-memory/${encodeURIComponent(dealId)}/story`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function readMarkdownInput(path, stdin = process.stdin) {
|
|
15
|
+
if (!path || path === true) throw new Error("--markdown <file|-> is required");
|
|
16
|
+
const markdown = path === "-"
|
|
17
|
+
? await new Promise((resolve, reject) => {
|
|
18
|
+
let value = "";
|
|
19
|
+
stdin.setEncoding("utf8");
|
|
20
|
+
stdin.on("data", (chunk) => { value += chunk; });
|
|
21
|
+
stdin.on("end", () => resolve(value));
|
|
22
|
+
stdin.on("error", reject);
|
|
23
|
+
})
|
|
24
|
+
: await readFile(path, "utf8");
|
|
25
|
+
if (!markdown) throw new Error("Deal Story Markdown cannot be empty");
|
|
26
|
+
const bytes = Buffer.byteLength(markdown, "utf8");
|
|
27
|
+
if (bytes > MAX_DEAL_STORY_BYTES) {
|
|
28
|
+
throw new Error(`Deal Story is ${bytes} bytes; maximum is ${MAX_DEAL_STORY_BYTES}`);
|
|
29
|
+
}
|
|
30
|
+
return markdown;
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llamaventures/cli",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "CLI + MCP server for the Llama Ventures investment workbench (command.llamaventures.vc).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "npm run test:agent-routing && npm run test:contract",
|
|
8
8
|
"test:agent-routing": "node scripts/verify-agent-routing.mjs",
|
|
9
|
-
"test:contract": "node --test scripts/build-manifest.test.mjs scripts/core-api-call-sites.test.mjs scripts/deal-actions-cli.test.mjs scripts/deal-actions.test.mjs scripts/mcp-four-action-surface.test.mjs scripts/server-compatibility.test.mjs scripts/verify-core-api-contract.test.mjs && node scripts/verify-core-api-contract.mjs",
|
|
9
|
+
"test:contract": "node --test scripts/build-manifest.test.mjs scripts/core-api-call-sites.test.mjs scripts/deal-actions-cli.test.mjs scripts/deal-actions.test.mjs scripts/deal-memory-actions-cli.test.mjs scripts/deal-memory-actions.test.mjs scripts/mcp-four-action-surface.test.mjs scripts/server-compatibility.test.mjs scripts/verify-core-api-contract.test.mjs && node scripts/verify-core-api-contract.mjs",
|
|
10
10
|
"verify:artifact": "node scripts/verify-release-artifact.mjs",
|
|
11
11
|
"verify:release": "npm test && npm run verify:artifact && node scripts/verify-tarball-clean.mjs",
|
|
12
12
|
"prepack": "node scripts/prepare-build-manifest.mjs",
|