@llamaventures/cli 1.23.0 → 1.24.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/CHANGELOG.md CHANGED
@@ -6,6 +6,24 @@ this project adheres to [Semantic Versioning](https://semver.org).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.24.0] - 2026-08-05
10
+
11
+ ### Added
12
+ - Add `llama admin workflow audit --deal <uuid> | --all` for the read-only
13
+ Workflow V2 production integrity auditor in Llama Command 3.25.0.
14
+
15
+ ## [1.23.1] - 2026-08-05
16
+
17
+ ### Added
18
+ - Add the canonical `llama workflow` command family and typed MCP tools for
19
+ reading and changing Investment Workflow V2 through one versioned API.
20
+ - Add audited workflow initialization for legacy migration and formal post-IC
21
+ execution status updates for Term Sheet, Verbal Commit, and Invested.
22
+
23
+ ### Changed
24
+ - Reject direct Deal status writes and retired `stage_gates` / `stage4_gate`
25
+ mutations so CLI and MCP cannot bypass the server state machine.
26
+
9
27
  ## [1.23.0] - 2026-08-05
10
28
 
11
29
  ### Added
package/README.md CHANGED
@@ -100,7 +100,10 @@ llama activity updated-deals --since 7d # meaningful updates grouped by deal
100
100
  llama deal create "Acme AI" --source alex --deal-owner owner@llamaventures.vc --source-direction Outbound --status Interested
101
101
  llama deal ingest <dealId> --file packet.json # atomic multi-fact + optional Feed note; retry-safe
102
102
  llama deal fact add <dealId> --category funding --claim "Raised a seed round" --source "deck p3" --source-url https://...
103
- llama deal update <dealId> status Diligence
103
+ llama workflow show <dealId>
104
+ llama workflow initialize <dealId> --reason "Migrate legacy workflow state without changing stage"
105
+ llama workflow proceed <dealId> --transition begin_preliminary --reason "Ready to begin research"
106
+ llama workflow execution-status <dealId> invested --reason "Wire confirmed"
104
107
  llama post <dealId> "note body"
105
108
  llama post <dealId> "@name please respond" --cue # only after explicit approval
106
109
  llama brief add-text <dealId> --heading "..." --body "..."
@@ -110,11 +113,23 @@ llama mentions
110
113
  llama agent-onboard # server-owned agent workflow contract
111
114
  ```
112
115
 
116
+ System admins can run the read-only production integrity auditor against one
117
+ Workflow V2 deal or every persisted V2 snapshot:
118
+
119
+ ```bash
120
+ llama admin workflow audit --deal <dealId>
121
+ llama admin workflow audit --all
122
+ ```
123
+
113
124
  Status vocabulary — `Interested`: tracked before any contact ·
114
125
  `Outreached`: contacted, no response yet · `Sourced`: real relationship
115
126
  signal exists. `sourceDirection` is separate: `Inbound` came to the firm,
116
127
  `Outbound` we reached out first.
117
128
 
129
+ Deal stage is controlled only by Investment Workflow V2. Direct
130
+ `deal update ... status ...` and legacy `stage_gates` writes are rejected;
131
+ use `llama workflow show` followed by the matching formal workflow command.
132
+
118
133
  For a deck, meeting note, email, or research packet, prefer `deal ingest` over a
119
134
  loop of `deal fact add` calls. The JSON object accepts `source`, up to 50
120
135
  `facts`, an optional `note`, and an optional `idempotencyKey`. The server commits
package/README.zh-CN.md CHANGED
@@ -122,7 +122,10 @@ llama activity updated-deals --since 7d # 按 deal 聚合的实质更新
122
122
  llama deal create "Acme AI" --source alex --deal-owner owner@llamaventures.vc --source-direction Outbound --status Interested
123
123
  llama deal ingest <dealId> --file packet.json # 多条 facts + 可选 Feed note,一次提交且可安全重试
124
124
  llama deal fact add <dealId> --category funding --claim "Raised a seed round" --source "deck p3" --source-url https://...
125
- llama deal update <dealId> status Diligence
125
+ llama workflow show <dealId>
126
+ llama workflow initialize <dealId> --reason "迁移旧状态,不改变当前阶段"
127
+ llama workflow proceed <dealId> --transition begin_preliminary --reason "开始初步研究"
128
+ llama workflow execution-status <dealId> invested --reason "已确认打款"
126
129
  llama post <dealId> "备注内容"
127
130
  llama post <dealId> "@name 请回复" --cue # 仅在用户明确授权后使用
128
131
  llama brief add-text <dealId> --heading "..." --body "..."
@@ -132,6 +135,13 @@ llama mentions
132
135
  llama agent-onboard # 服务端下发的 agent 工作契约
133
136
  ```
134
137
 
138
+ 系统管理员可以只读审计一个 Workflow V2 deal,或审计全部已持久化的 V2 快照:
139
+
140
+ ```bash
141
+ llama admin workflow audit --deal <dealId>
142
+ llama admin workflow audit --all
143
+ ```
144
+
135
145
  Status 语义——`Interested`:接触前先记录关注 · `Outreached`:已联系、
136
146
  尚无回应 · `Sourced`:已有真实关系信号。`sourceDirection` 是独立维度:
137
147
  `Inbound` 流入,`Outbound` 我们主动。
package/bin/llama-mcp.mjs CHANGED
@@ -74,6 +74,27 @@ function jsonResult(value, isError = false) {
74
74
  return textResult(JSON.stringify(value, null, 2), isError);
75
75
  }
76
76
 
77
+ async function callWorkflow(dealId, type, fields) {
78
+ try {
79
+ const path = `/api/deals/${encodeURIComponent(dealId)}/workflow`;
80
+ // @core-api-operation GET /api/deals/{dealId}/workflow
81
+ const current = await request("GET", path);
82
+ const expectedRevision = current?.workflow?.revision;
83
+ if (!Number.isInteger(expectedRevision)) {
84
+ return textResult("Error: Investment Workflow V2 is not initialized for this deal.", true);
85
+ }
86
+ // @core-api-operation POST /api/deals/{dealId}/workflow
87
+ return callApi("POST", path, {
88
+ type,
89
+ requestId: `mcp:${type}:${randomUUID()}`,
90
+ expectedRevision,
91
+ ...fields,
92
+ });
93
+ } catch (err) {
94
+ return textResult(`Error: ${err?.message ?? String(err)}`, true);
95
+ }
96
+ }
97
+
77
98
  function splitSources(value) {
78
99
  if (Array.isArray(value)) return value.filter(Boolean);
79
100
  if (!value || value === true) return undefined;
@@ -494,7 +515,7 @@ server.registerTool(
494
515
  "deal_update",
495
516
  {
496
517
  description:
497
- "Update a single whitelisted field on a deal. Writable fields: status, theirStage, " +
518
+ "Update a single non-workflow field on a deal. Writable fields: theirStage, " +
498
519
  "notes, stage, dealOwner, source, sourceDirection, description, website, location, founders, " +
499
520
  "proposedAmount, roundSize, valuation, sector, subsector, foundedYear, leadInvestor, " +
500
521
  "investors. Logs a field_change event in deal_events.",
@@ -504,8 +525,93 @@ server.registerTool(
504
525
  value: z.union([z.string(), z.number(), z.null()]).describe("new value"),
505
526
  },
506
527
  },
507
- async ({ dealId, field, value }) =>
508
- callApi("POST", "/api/deals/update", { dealId, field, value })
528
+ async ({ dealId, field, value }) => {
529
+ if (field === "status") {
530
+ return textResult("Error: Direct status writes are retired. Use the workflow_* tools.", true);
531
+ }
532
+ return callApi("POST", "/api/deals/update", { dealId, field, value });
533
+ }
534
+ );
535
+
536
+ server.registerTool(
537
+ "workflow_show",
538
+ {
539
+ description: "Read the canonical Investment Workflow V2 state, revision, current transition, and blockers for a deal.",
540
+ inputSchema: { dealId: z.string() },
541
+ },
542
+ async ({ dealId }) => callApi("GET", `/api/deals/${encodeURIComponent(dealId)}/workflow`),
543
+ );
544
+
545
+ server.registerTool(
546
+ "workflow_initialize",
547
+ {
548
+ description: "Persist the canonical Investment Workflow V2 bootstrap state for an unmigrated deal without changing its semantic stage. Intended for audited migration and recovery.",
549
+ inputSchema: { dealId: z.string(), reason: z.string().min(1) },
550
+ },
551
+ async ({ dealId, reason }) => callWorkflow(dealId, "initialize", { reason }),
552
+ );
553
+
554
+ server.registerTool(
555
+ "workflow_request_partner_support",
556
+ {
557
+ description: "Submit a formal Partner Support request. Core refuses it until every current stage requirement is satisfied or waived.",
558
+ inputSchema: { dealId: z.string(), partnerId: z.string(), reason: z.string().min(1) },
559
+ },
560
+ async ({ dealId, partnerId, reason }) => callWorkflow(dealId, "request_partner_support", { partnerId, reason }),
561
+ );
562
+
563
+ server.registerTool(
564
+ "workflow_decide_partner_support",
565
+ {
566
+ description: "Record the named Partner's own Support, Need more, or Pass decision. Core verifies caller identity; never use on another person's behalf.",
567
+ inputSchema: { dealId: z.string(), decision: z.enum(["support", "need_more", "pass"]), reason: z.string().min(1) },
568
+ },
569
+ async ({ dealId, decision, reason }) => callWorkflow(dealId, "partner_support_decision", { decision, reason }),
570
+ );
571
+
572
+ server.registerTool(
573
+ "workflow_proceed",
574
+ {
575
+ description: "Apply the current formal workflow transition. Core re-evaluates every guard atomically.",
576
+ inputSchema: { dealId: z.string(), transitionKey: z.string(), reason: z.string().min(1) },
577
+ },
578
+ async ({ dealId, transitionKey, reason }) => callWorkflow(dealId, "proceed", { transitionKey, reason }),
579
+ );
580
+
581
+ server.registerTool(
582
+ "workflow_resolve_guard",
583
+ {
584
+ description: "Record an audited workflow guard resolution. Waivers require a concrete reason and remain visible in history.",
585
+ inputSchema: { dealId: z.string(), guardKey: z.string(), status: z.enum(["satisfied", "unsatisfied", "waived"]), reason: z.string().min(1) },
586
+ },
587
+ async ({ dealId, guardKey, status, reason }) => callWorkflow(dealId, "resolve_guard", { guardKey, status, reason }),
588
+ );
589
+
590
+ server.registerTool(
591
+ "workflow_control",
592
+ {
593
+ description: "Hold, resume, pass, restore, or return a deal through the canonical workflow control path.",
594
+ inputSchema: { dealId: z.string(), action: z.enum(["hold", "resume", "pass", "restore", "return"]), reason: z.string().min(1), holdDisposition: z.enum(["stalled", "future"]).optional() },
595
+ },
596
+ async ({ dealId, action, reason, holdDisposition }) => callWorkflow(dealId, "control", { action, reason, ...(holdDisposition ? { holdDisposition } : {}) }),
597
+ );
598
+
599
+ server.registerTool(
600
+ "workflow_vote",
601
+ {
602
+ description: "Cast the signed-in Partner's own Formal IC vote through Investment Workflow V2.",
603
+ inputSchema: { dealId: z.string(), vote: z.enum(["yes", "no"]), reason: z.string().min(1) },
604
+ },
605
+ async ({ dealId, vote, reason }) => callWorkflow(dealId, "cast_vote", { vote, reason }),
606
+ );
607
+
608
+ server.registerTool(
609
+ "workflow_update_execution_status",
610
+ {
611
+ description: "Update the canonical post-IC execution status. Available only after the formal IC decision.",
612
+ inputSchema: { dealId: z.string(), executionStatus: z.enum(["Term Sheet", "Verbal Commit", "Invested"]), reason: z.string().min(1) },
613
+ },
614
+ async ({ dealId, executionStatus, reason }) => callWorkflow(dealId, "update_execution_status", { executionStatus, reason }),
509
615
  );
510
616
 
511
617
  // ============================================================
package/bin/llama.mjs CHANGED
@@ -36,6 +36,7 @@ import { LLAMA_CLI_CLIENT_ID, pkceLoopbackFlow, revokeToken as revokeOAuthToken
36
36
  import { deleteBundle, detectBackend, readBundle, writeBundle } from "../lib/oauth-storage.mjs";
37
37
  import { maybeNudgeUpdate, getUpdateNudge } from "../lib/version-check.mjs";
38
38
  import { getBuildInfo } from "../lib/build-info.mjs";
39
+ import { workflowAuditPath } from "../lib/workflow-audit.mjs";
39
40
 
40
41
  const requireFromHere = createRequire(import.meta.url);
41
42
  const { version: PKG_VERSION } = requireFromHere("../package.json");
@@ -391,7 +392,7 @@ Deals:
391
392
  llama deal show <dealId>
392
393
  llama deal feed <dealId> # every contribution (facts + notes), human-typed or assistant-drafted, newest first
393
394
  llama deal update <dealId> <field> <value>
394
- Writable fields: status, theirStage, stage, notes, dealOwner, source, sourceDirection,
395
+ Writable fields: theirStage, stage, notes, source, sourceDirection,
395
396
  description, website, location, founders, founderInfo, proposedAmount,
396
397
  roundSize, valuation, deckLink, folderUrl, sector, subsector,
397
398
  foundedYear, leadInvestor, investors, agentActive.
@@ -399,7 +400,6 @@ Deals:
399
400
  the deal page (~280 chars). Meeting notes and narrative go in a comment
400
401
  (llama post); verifiable claims go in facts (llama deal fact add).
401
402
  e.g. llama deal update <dealId> website https://acme.ai
402
- llama deal update <dealId> status Interested
403
403
  llama deal update <dealId> sector "Developer Tools"
404
404
  llama deal update <dealId> foundedYear 2024
405
405
  llama deal update <dealId> leadInvestor "Acme Capital"
@@ -418,6 +418,19 @@ Deals:
418
418
  [--limit 200] [--offset 0]
419
419
  llama deal list [--owner ...] [--status ...] [...same flags as search]
420
420
 
421
+ Investment Workflow V2 (the only stage-write surface):
422
+ llama workflow show <dealId>
423
+ llama workflow initialize <dealId> --reason "..." # audited legacy migration/bootstrap
424
+ llama workflow request-support <dealId> --partner <userId> --reason "..."
425
+ llama workflow decide-support <dealId> support|need_more|pass --reason "..."
426
+ llama workflow proceed <dealId> --transition <key> --reason "..."
427
+ llama workflow waive <dealId> --guard <key> --reason "..."
428
+ llama workflow control <dealId> hold|resume|pass|restore|return --reason "..." [--disposition stalled|future]
429
+ llama workflow organize-ic <dealId> --note "..."
430
+ llama workflow vote <dealId> yes|no --reason "..."
431
+ llama workflow reassign-owner <dealId> --owner <userId> --reason "..."
432
+ llama workflow execution-status <dealId> term-sheet|verbal-commit|invested --reason "..."
433
+
421
434
  Agent activity (read-only, cheap read model over append-only activity):
422
435
  llama activity new-deals [--since 24h|7d|<ISO>] [--limit 50]
423
436
  llama activity updated-deals [--since 24h|7d|<ISO>] [--limit 50] [--deal <uuid>]
@@ -591,6 +604,7 @@ Deal page HTML (hand-authored sandboxed pages on /deals/<id>/browse/<slug>):
591
604
  update_deal_browse_html tool and the MCP html_upload_file tool.
592
605
 
593
606
  Admin (system admin only — server returns 403 for non-admin tokens):
607
+ llama admin workflow audit --deal <uuid> | --all
594
608
  llama admin auth-events [--kind X] [--actor email] [--subject email] [--since 24h|7d|30d|<ISO>] [--limit 100]
595
609
  llama admin deal-events [--kind X] [--actor email] [--deal <uuid>] [--since 24h] [--limit 100]
596
610
  llama admin agent-events [--kind tool_call|loop_stalled|max_turns_reached] [--agent-kind deal|secretary|main|inbox]
@@ -1446,6 +1460,65 @@ async function main() {
1446
1460
  return;
1447
1461
  }
1448
1462
 
1463
+ if (area === "workflow") {
1464
+ const dealId = rest[0];
1465
+ if (!dealId) throw new Error("Usage: llama workflow show|initialize|request-support|decide-support|proceed|waive|control|organize-ic|vote|reassign-owner|execution-status <dealId> [...flags]");
1466
+ if (action === "show") {
1467
+ print(await request("GET", `/api/deals/${encodeURIComponent(dealId)}/workflow`));
1468
+ return;
1469
+ }
1470
+ const { flags, positional } = parseFlags(rest.slice(1));
1471
+ const current = await request("GET", `/api/deals/${encodeURIComponent(dealId)}/workflow`);
1472
+ const expectedRevision = current?.workflow?.revision;
1473
+ if (!Number.isInteger(expectedRevision)) {
1474
+ throw new Error("This deal has no initialized Investment Workflow V2 state. Open its workflow panel once, then retry.");
1475
+ }
1476
+ const base = {
1477
+ requestId: `cli:${action}:${randomUUID()}`,
1478
+ expectedRevision,
1479
+ };
1480
+ let command;
1481
+ if (action === "initialize") {
1482
+ if (!flags.reason) throw new Error('Usage: llama workflow initialize <dealId> --reason "..."');
1483
+ command = { ...base, type: "initialize", reason: String(flags.reason) };
1484
+ } else if (action === "request-support") {
1485
+ if (!flags.partner || !flags.reason) throw new Error('Usage: llama workflow request-support <dealId> --partner <userId> --reason "..."');
1486
+ command = { ...base, type: "request_partner_support", partnerId: String(flags.partner), reason: String(flags.reason) };
1487
+ } else if (action === "decide-support") {
1488
+ const decision = positional[0];
1489
+ if (!["support", "need_more", "pass"].includes(decision) || !flags.reason) throw new Error('Usage: llama workflow decide-support <dealId> support|need_more|pass --reason "..."');
1490
+ command = { ...base, type: "partner_support_decision", decision, reason: String(flags.reason) };
1491
+ } else if (action === "proceed") {
1492
+ if (!flags.transition || !flags.reason) throw new Error('Usage: llama workflow proceed <dealId> --transition <key> --reason "..."');
1493
+ command = { ...base, type: "proceed", transitionKey: String(flags.transition), reason: String(flags.reason) };
1494
+ } else if (action === "waive") {
1495
+ if (!flags.guard || !flags.reason) throw new Error('Usage: llama workflow waive <dealId> --guard <key> --reason "..."');
1496
+ command = { ...base, type: "resolve_guard", guardKey: String(flags.guard), status: "waived", reason: String(flags.reason) };
1497
+ } else if (action === "control") {
1498
+ const control = positional[0];
1499
+ if (!["hold", "resume", "pass", "restore", "return"].includes(control) || !flags.reason) throw new Error('Usage: llama workflow control <dealId> hold|resume|pass|restore|return --reason "..." [--disposition stalled|future]');
1500
+ command = { ...base, type: "control", action: control, reason: String(flags.reason), ...(flags.disposition ? { holdDisposition: String(flags.disposition) } : {}) };
1501
+ } else if (action === "organize-ic") {
1502
+ if (!flags.note) throw new Error('Usage: llama workflow organize-ic <dealId> --note "..."');
1503
+ command = { ...base, type: "organize_ic", note: String(flags.note) };
1504
+ } else if (action === "vote") {
1505
+ const vote = positional[0];
1506
+ if (!["yes", "no"].includes(vote) || !flags.reason) throw new Error('Usage: llama workflow vote <dealId> yes|no --reason "..."');
1507
+ command = { ...base, type: "cast_vote", vote, reason: String(flags.reason) };
1508
+ } else if (action === "reassign-owner") {
1509
+ if (!flags.owner || !flags.reason) throw new Error('Usage: llama workflow reassign-owner <dealId> --owner <userId> --reason "..."');
1510
+ command = { ...base, type: "reassign_owner", ownerId: String(flags.owner), ownerName: "resolved by server", reason: String(flags.reason) };
1511
+ } else if (action === "execution-status") {
1512
+ const status = { "term-sheet": "Term Sheet", "verbal-commit": "Verbal Commit", invested: "Invested" }[positional[0]];
1513
+ if (!status || !flags.reason) throw new Error('Usage: llama workflow execution-status <dealId> term-sheet|verbal-commit|invested --reason "..."');
1514
+ command = { ...base, type: "update_execution_status", executionStatus: status, reason: String(flags.reason) };
1515
+ } else {
1516
+ throw new Error(`Unknown workflow command "${action || ""}".`);
1517
+ }
1518
+ print(await request("POST", `/api/deals/${encodeURIComponent(dealId)}/workflow`, command));
1519
+ return;
1520
+ }
1521
+
1449
1522
  if (area === "deal" && action === "show") {
1450
1523
  const dealId = rest[0];
1451
1524
  if (!dealId) throw new Error("Usage: llama deal show <dealId>");
@@ -1464,6 +1537,9 @@ async function main() {
1464
1537
  const [dealId, field, ...valueParts] = rest;
1465
1538
  const value = valueParts.join(" ");
1466
1539
  if (!dealId || !field) throw new Error("Usage: llama deal update <dealId> <field> <value>");
1540
+ if (field === "status") {
1541
+ throw new Error("Direct status writes are retired. Use `llama workflow show <dealId>` and a formal `llama workflow ...` command.");
1542
+ }
1467
1543
  print(await request("POST", "/api/deals/update", { dealId, field, value }));
1468
1544
  return;
1469
1545
  }
@@ -1497,6 +1573,9 @@ async function main() {
1497
1573
  const sub = rest[0];
1498
1574
  const dealId = rest[1];
1499
1575
  const key = rest[2];
1576
+ if (["stage_gates", "stage4_gate"].includes(key)) {
1577
+ throw new Error(`Legacy ${key} is retired. Use \`llama workflow ...\` commands.`);
1578
+ }
1500
1579
  if (sub === "set") {
1501
1580
  const raw = rest.slice(3).join(" ");
1502
1581
  if (!dealId || !key || !raw) {
@@ -2326,12 +2405,21 @@ Routing — is this the right command?
2326
2405
  // - agent-events : every AI tool call / loop_stalled / max_turns (AI ops)
2327
2406
  if (area === "admin") {
2328
2407
  const sub = action;
2329
- const valid = ["auth-events", "deal-events", "agent-events"];
2408
+ const valid = ["workflow", "auth-events", "deal-events", "agent-events"];
2330
2409
  if (!valid.includes(sub)) {
2331
2410
  throw new Error(
2332
2411
  `Unknown admin sub-command "${sub || ""}". Use: ${valid.join(", ")}`
2333
2412
  );
2334
2413
  }
2414
+ if (sub === "workflow") {
2415
+ if (rest[0] !== "audit") {
2416
+ throw new Error("Usage: llama admin workflow audit --deal <uuid> | --all");
2417
+ }
2418
+ const { flags } = parseFlags(rest.slice(1), ["deal", "all"]);
2419
+ // @core-api-operation GET /api/admin/workflow-audit
2420
+ print(await request("GET", workflowAuditPath(flags)));
2421
+ return;
2422
+ }
2335
2423
  const { flags } = parseFlags(rest);
2336
2424
  const params = new URLSearchParams();
2337
2425
  // Common filters across all three.
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "format": "llama.core-api-contract.v1",
3
3
  "name": "llama-core-api",
4
- "apiVersion": "3.5.0",
4
+ "apiVersion": "3.25.0",
5
5
  "openapiVersion": "3.0.3",
6
- "sha256": "31c7d23ee06bd4282aa2451698604cc30df4792d4a0e9674593a809b4eb55767",
7
- "pathCount": 216,
8
- "operationCount": 281
6
+ "sha256": "f06bbd31d4e0e3fbf5f418a772616375f2a907535b0c7783060ba87b0565a0f7",
7
+ "pathCount": 235,
8
+ "operationCount": 306
9
9
  }
@@ -0,0 +1,19 @@
1
+ # Investment Workflow V2 client contract
2
+
3
+ CLI and MCP treat `GET/POST /api/deals/{dealId}/workflow` as the only workflow
4
+ read/write surface. A write must first read the current revision, submit one
5
+ typed command with a unique request id, and display the canonical response.
6
+
7
+ Never add a client shortcut that writes `deals.status`,
8
+ `extra.investment_workflow_v2` snapshots, `extra.stage_gates`, or
9
+ `extra.stage4_gate`. Partner
10
+ decisions are always made by the authenticated Partner; admin capability does
11
+ not imply permission to impersonate a decision maker.
12
+
13
+ `workflow initialize` is the audited bootstrap command for legacy migration.
14
+ It persists the state returned by `workflow show` without advancing a stage or
15
+ resolving a guard. Never translate legacy approvals into V2 approvals silently.
16
+
17
+ Post-IC compatibility status is also owned by V2. Use `workflow
18
+ execution-status` / `workflow_update_execution_status` for Term Sheet, Verbal
19
+ Commit, and Invested; never reopen direct `deal update ... status` writes.
@@ -14,6 +14,10 @@
14
14
  "method": "GET",
15
15
  "path": "/api/admin/deal-events"
16
16
  },
17
+ {
18
+ "method": "GET",
19
+ "path": "/api/admin/workflow-audit"
20
+ },
17
21
  {
18
22
  "method": "GET",
19
23
  "path": "/api/agent/activity"
@@ -78,6 +82,14 @@
78
82
  "method": "GET",
79
83
  "path": "/api/deals/{dealId}"
80
84
  },
85
+ {
86
+ "method": "GET",
87
+ "path": "/api/deals/{dealId}/workflow"
88
+ },
89
+ {
90
+ "method": "POST",
91
+ "path": "/api/deals/{dealId}/workflow"
92
+ },
81
93
  {
82
94
  "method": "POST",
83
95
  "path": "/api/deals/{dealId}/agent-runs/{runId}/revert"
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "format": "llama.cli-build.v1",
3
3
  "packageName": "@llamaventures/cli",
4
- "packageVersion": "1.23.0",
5
- "sourceSha": "3f3fb4ae90469e4c561bc8ad319c59d877d0fcf2",
4
+ "packageVersion": "1.24.0",
5
+ "sourceSha": "501f361cc0aeaa8746e1387a11f6224ca66bca13",
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": "3.5.0",
11
+ "apiVersion": "3.25.0",
12
12
  "openapiVersion": "3.0.3",
13
- "sha256": "31c7d23ee06bd4282aa2451698604cc30df4792d4a0e9674593a809b4eb55767"
13
+ "sha256": "f06bbd31d4e0e3fbf5f418a772616375f2a907535b0c7783060ba87b0565a0f7"
14
14
  }
15
15
  }
@@ -0,0 +1,13 @@
1
+ export function workflowAuditPath(flags) {
2
+ const deal = typeof flags.deal === "string" && flags.deal.trim()
3
+ ? flags.deal.trim()
4
+ : null;
5
+ const all = flags.all === true;
6
+ if ((!deal && !all) || (deal && all)) {
7
+ throw new Error("Usage: llama admin workflow audit --deal <uuid> | --all");
8
+ }
9
+ const params = new URLSearchParams();
10
+ if (deal) params.set("deal", deal);
11
+ if (all) params.set("all", "true");
12
+ return `/api/admin/workflow-audit?${params.toString()}`;
13
+ }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@llamaventures/cli",
3
- "version": "1.23.0",
3
+ "version": "1.24.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/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/investment-workflow-contract.test.mjs scripts/server-compatibility.test.mjs scripts/verify-core-api-contract.test.mjs scripts/workflow-audit.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",