plures 1.18.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -52,10 +52,11 @@ if (event.type === "task.completed" && event.data.result?.agreement === 1) {
52
52
  - `plures.tasks.retrieve(id)`, `plures.tasks.list({ status?, limit?, starting_after?, app? })`, `plures.tasks.cancel(id)`
53
53
  - `new Plures(key, { app: "support-agent" })` or `PLURES_APP`: send everything to one of your apps. `plures.apps.list()`, `.create({ name, id? })`, `.retrieve(id)`, `.update(id, { name?, id?, default? })`, `.del(id)`. Each app keeps its own tasks, ledger view, map and quality.
54
54
  - `plures.maps.guide()`, `.retrieve(app)`, `.upload(app, map, { branch? })` (a branch other than main gets its own map, compared with main and noted on its pull request), `.setCheckpoint(app, id, status)`: the agent map, where your agent needs a person. Or run `npx plures map` in your repo: your own coding agent reads the code and only the map is sent.
55
- - `plures.maps.createKey(app, { label? })`, `.keys(app)`, `.revokeKey(app, id)`: map keys for CI, which can only read and upload that app's map. `.workflow(app, branch?)` returns the GitHub Action that maps on every push; `npx plures map workflow` writes it to `.github/workflows/plures-map.yml`. Or use **Map with GitHub** on the app's map page and Plures opens the pull request for you, then **Turn on mapping** merges it. `plures.maps.startRun(app)` and `.runs(app)`: in CI with no Claude key, `npx plures map` starts a run where Plures runs Claude (first map free, then Claude's cost plus 30%, at most $5).
55
+ - `plures.maps.createKey(app, { label? })`, `.keys(app)`, `.revokeKey(app, id)`: map keys for CI, which can only read and upload that app's map. `.workflow(app, branch?)` returns the GitHub Action that maps on every push; `npx plures map workflow` writes it to `.github/workflows/plures-map.yml`. Or use **Map with GitHub** on the app's map page and Plures opens the pull request for you, then **Turn on mapping** merges it. `plures.maps.startRun(app)` and `.runs(app)`: in CI with no Claude key, `npx plures map` starts a run where Plures runs Claude (Claude at cost, at most $5 a run). `plures.maps.estimate(app, "map" | "drift")` shows what the next map or drift fix costs before it starts: the Plures engine fee, 5¢ a step of the app's map (the first map of each app is free; branch maps are free), plus Claude at cost when Plures runs it. `npx plures map` and `npx plures drift fix` check it first and stop if your credit can't cover it. A drift fix is paid by outcome: each quality point it wins back, once people have graded the new outputs, costs 20¢ a step of the map ($3 to $15 a point), at most $500 a fix, with Claude included; no better or worse is free.
56
56
  - In your terminal, `npx plures map` opens your map page so you can watch it build (`--no-open` to skip), keeps one live line of what it's reading, and ends by drawing the map's flows with the checks Plures suggests. On GitHub and other CI it prints a plain log.
57
57
  - `npx plures map` shows each file as Claude Code reads it and sends the file names (never their contents) to Plures, so the map page shows the reading live and the map builds itself when it lands. `plures.maps.progress(app, { session, phase, events })` is the call behind it.
58
58
  - `plures.github.repos()`, `.connect(app, "owner/repo")`, `.turnOn(app)`, `.run(app)` (map now), `.status(app)`, `.disconnect(app)`: Map with GitHub from code. Plures sets the repo's map key secret and opens a pull request adding the workflow; `turnOn` merges it. Installing the Plures GitHub App needs a browser once (`install_url`). From a terminal: `npx plures map github connect`, then `npx plures map github on`; `npx plures map github run` maps again now.
59
+ - `plures.quality.suggestions(app)`: rubrics the Plures engine suggests for the app, from what graders found in its answers (with evidence) and from its agent map (with the step and file).
59
60
  - `plures.quality.sample({ rubric, output, input? })`: send every output; a share is graded by people against the rubric, with personal details taken out first. `plures.quality.rubrics.create({ name, question, options, sample_rate })`, `.retrieve(name)` (score by day, this week against the four before, drift), `.list()`, `.update()`, `.del()`. `plures.quality.compare({ release, input, a, b })` and `plures.quality.release(name)` for blind release checks. `plures.quality.export(rubric)` returns graded samples for evals or fine tuning.
60
61
  - `plures.testMix.set({ unanimous: 70, split: 20, tie: 5, expired: 5 })`, `.get()`, `.clear()`: with a test key, makes a share of test tasks split, tie or expire at random so you can test routing to your team without changing your agent. A task can override it with `metadata.test_mix`, and `metadata.test_outcome` beats both.
61
62
  - `plures.routing.get()`, `plures.routing.set({ groups, rules, team })`: named teams of your own people and rules by category, like `{ groups: [{ name: "Finance", emails: ["ap@acme.com"] }], rules: [{ category: "refunds", route: "team", group: "Finance" }] }`. What you send replaces what's there; the rest stays.
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- var VERSION = "1.18.0";
2
+ var VERSION = "1.22.0";
3
3
  var PluresError = class extends Error {
4
4
  constructor(message, status, type) {
5
5
  super(message);
@@ -107,8 +107,12 @@ var Plures = class {
107
107
  createKey: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`, opts),
108
108
  keys: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`),
109
109
  revokeKey: (app, id) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app)}/map_keys/${encodeURIComponent(id)}`),
110
+ /** What the next map or drift fix will cost before it starts. A map: the Plures engine fee, per step of the app's map
111
+ * (the first map of each app is free), and Claude at cost if Plures runs it. A drift fix: paid by outcome,
112
+ * drift_fix.per_point_cents for each quality point it wins back, at most drift_fix.max_cents, Claude included. blocked says why it can't start. */
113
+ estimate: (app, kind = "map", opts = {}) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_estimate?for=${kind}${opts.case ? `&case=${encodeURIComponent(opts.case)}` : ""}`),
110
114
  /** Starts a map run where Plures runs Claude (CI with no Anthropic key). npx plures map does this. */
111
- startRun: (app) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`, {}),
115
+ startRun: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`, opts),
112
116
  runs: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`),
113
117
  /** While mapping, which files the coding agent is reading, shown live on the map page. File names only. npx plures map does this. */
114
118
  progress: (app, p) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map/progress`, p),
@@ -144,6 +148,9 @@ var Plures = class {
144
148
  * early, and compare two versions blind before you ship. Personal details are taken out first.
145
149
  */
146
150
  quality = {
151
+ /** Rubrics the Plures engine suggests for an app: from what graders found in its answers (source "grades", with
152
+ * evidence) and from its agent map (source "map", with the step and file). Add one with rubrics.create. */
153
+ suggestions: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/rubric_suggestions`),
147
154
  /** Send every output; the rubric's sample rate decides which ones people grade. */
148
155
  sample: (p) => this.req("POST", "/api/v1/samples", p),
149
156
  /** A blind release check: a is the current version's output, b the new one's, for the same input. */
package/dist/cli.cjs CHANGED
@@ -30,7 +30,7 @@ var import_node_path = require("path");
30
30
  var import_node_fs = require("fs");
31
31
 
32
32
  // src/index.ts
33
- var VERSION = "1.18.0";
33
+ var VERSION = "1.22.0";
34
34
  var PluresError = class extends Error {
35
35
  constructor(message, status, type) {
36
36
  super(message);
@@ -138,8 +138,12 @@ var Plures = class {
138
138
  createKey: (app2, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/map_keys`, opts),
139
139
  keys: (app2) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app2)}/map_keys`),
140
140
  revokeKey: (app2, id) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app2)}/map_keys/${encodeURIComponent(id)}`),
141
+ /** What the next map or drift fix will cost before it starts. A map: the Plures engine fee, per step of the app's map
142
+ * (the first map of each app is free), and Claude at cost if Plures runs it. A drift fix: paid by outcome,
143
+ * drift_fix.per_point_cents for each quality point it wins back, at most drift_fix.max_cents, Claude included. blocked says why it can't start. */
144
+ estimate: (app2, kind = "map", opts = {}) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app2)}/map_estimate?for=${kind}${opts.case ? `&case=${encodeURIComponent(opts.case)}` : ""}`),
141
145
  /** Starts a map run where Plures runs Claude (CI with no Anthropic key). npx plures map does this. */
142
- startRun: (app2) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/map_runs`, {}),
146
+ startRun: (app2, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/map_runs`, opts),
143
147
  runs: (app2) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app2)}/map_runs`),
144
148
  /** While mapping, which files the coding agent is reading, shown live on the map page. File names only. npx plures map does this. */
145
149
  progress: (app2, p) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/map/progress`, p),
@@ -175,6 +179,9 @@ var Plures = class {
175
179
  * early, and compare two versions blind before you ship. Personal details are taken out first.
176
180
  */
177
181
  quality = {
182
+ /** Rubrics the Plures engine suggests for an app: from what graders found in its answers (source "grades", with
183
+ * evidence) and from its agent map (source "map", with the step and file). Add one with rubrics.create. */
184
+ suggestions: (app2) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app2)}/rubric_suggestions`),
178
185
  /** Send every output; the rubric's sample rate decides which ones people grade. */
179
186
  sample: (p) => this.req("POST", "/api/v1/samples", p),
180
187
  /** A blind release check: a is the current version's output, b the new one's, for the same input. */
@@ -512,8 +519,7 @@ async function upload(file) {
512
519
  }
513
520
  if (!fancy) console.log(dim(`Sending the map to Plures. No source code is sent.`));
514
521
  let m;
515
- const gitBranch = (0, import_node_child_process.spawnSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf8" }).stdout?.trim();
516
- const branchName = process.env.PLURES_BRANCH || process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || (gitBranch && gitBranch !== "HEAD" ? gitBranch : "");
522
+ const branchName = currentBranch();
517
523
  try {
518
524
  m = await client().maps.upload(app, map2, branchName ? { branch: branchName } : {});
519
525
  } catch (e) {
@@ -563,6 +569,14 @@ With plures-mcp connected, just ask: ${cyan("Map this app for Plures")}`);
563
569
  return;
564
570
  }
565
571
  const before = (0, import_node_fs.existsSync)(FILE) ? (0, import_node_fs.statSync)(FILE).mtimeMs : 0;
572
+ const est = await client().maps.estimate(app).catch(() => null);
573
+ if (est) {
574
+ const br = currentBranch();
575
+ const main2 = !br || br === "main" || br === "master";
576
+ if (est.blocked && main2) die(est.blocked);
577
+ if (main2 && est.engine_fee.free) console.log(dim("Plures engine: this app's first map is free."));
578
+ else if (main2 && est.engine_fee.cents) console.log(dim(`Plures engine: about $${(est.engine_fee.cents / 100).toFixed(2)} (${est.engine_fee.steps} steps at ${est.engine_fee.per_step_cents}\xA2), from your credit when the map uploads.`));
579
+ }
566
580
  const childEnv = { ...process.env };
567
581
  const ownClaude = Boolean(process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.ANTHROPIC_AUTH_TOKEN);
568
582
  if (!ownClaude && (process.env.CI || argv.includes("--plures-claude"))) {
@@ -821,6 +835,10 @@ ${cyan("\u2713")} The check is on the branch ${bold(br)}. Review it with ${cyan(
821
835
  die(e instanceof PluresError ? e.message : String(e));
822
836
  }
823
837
  }
838
+ function currentBranch() {
839
+ const gitBranch = (0, import_node_child_process.spawnSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf8" }).stdout?.trim();
840
+ return process.env.PLURES_BRANCH || process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || (gitBranch && gitBranch !== "HEAD" ? gitBranch : "");
841
+ }
824
842
  async function driftFix(id) {
825
843
  if (!id) die("npx plures drift fix <case id> [--app <app>]");
826
844
  const cl = client();
@@ -836,6 +854,8 @@ async function driftFix(id) {
836
854
  } catch (e) {
837
855
  return fail(e instanceof PluresError ? e.message : String(e));
838
856
  }
857
+ const est = await cl.maps.estimate(app, "drift", { case: id }).catch(() => null);
858
+ if (est?.blocked) return fail(est.blocked);
839
859
  if (!hasClaude()) return fail("Claude Code isn't installed here, so nothing could read the code.");
840
860
  const git = (...a) => (0, import_node_child_process.spawnSync)("git", a, { encoding: "utf8" });
841
861
  if (git("status", "--porcelain").stdout.trim()) return fail("This repo has changes that aren't committed. Commit or stash them first.");
@@ -843,7 +863,7 @@ async function driftFix(id) {
843
863
  const ownClaude = Boolean(process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.ANTHROPIC_AUTH_TOKEN);
844
864
  if (!ownClaude && process.env.CI) {
845
865
  try {
846
- const run = await cl.maps.startRun(app);
866
+ const run = await cl.maps.startRun(app, { purpose: "drift", case: id });
847
867
  childEnv.ANTHROPIC_BASE_URL = run.base_url;
848
868
  childEnv.ANTHROPIC_API_KEY = run.token;
849
869
  } catch (e) {
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  Plures,
4
4
  PluresError,
5
5
  VERSION
6
- } from "./chunk-RKIJTDS3.js";
6
+ } from "./chunk-3P2G67SF.js";
7
7
 
8
8
  // src/cli.ts
9
9
  import { spawnSync, spawn } from "child_process";
@@ -228,8 +228,7 @@ async function upload(file) {
228
228
  }
229
229
  if (!fancy) console.log(dim(`Sending the map to Plures. No source code is sent.`));
230
230
  let m;
231
- const gitBranch = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf8" }).stdout?.trim();
232
- const branchName = process.env.PLURES_BRANCH || process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || (gitBranch && gitBranch !== "HEAD" ? gitBranch : "");
231
+ const branchName = currentBranch();
233
232
  try {
234
233
  m = await client().maps.upload(app, map2, branchName ? { branch: branchName } : {});
235
234
  } catch (e) {
@@ -279,6 +278,14 @@ With plures-mcp connected, just ask: ${cyan("Map this app for Plures")}`);
279
278
  return;
280
279
  }
281
280
  const before = existsSync(FILE) ? statSync(FILE).mtimeMs : 0;
281
+ const est = await client().maps.estimate(app).catch(() => null);
282
+ if (est) {
283
+ const br = currentBranch();
284
+ const main2 = !br || br === "main" || br === "master";
285
+ if (est.blocked && main2) die(est.blocked);
286
+ if (main2 && est.engine_fee.free) console.log(dim("Plures engine: this app's first map is free."));
287
+ else if (main2 && est.engine_fee.cents) console.log(dim(`Plures engine: about $${(est.engine_fee.cents / 100).toFixed(2)} (${est.engine_fee.steps} steps at ${est.engine_fee.per_step_cents}\xA2), from your credit when the map uploads.`));
288
+ }
282
289
  const childEnv = { ...process.env };
283
290
  const ownClaude = Boolean(process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.ANTHROPIC_AUTH_TOKEN);
284
291
  if (!ownClaude && (process.env.CI || argv.includes("--plures-claude"))) {
@@ -537,6 +544,10 @@ ${cyan("\u2713")} The check is on the branch ${bold(br)}. Review it with ${cyan(
537
544
  die(e instanceof PluresError ? e.message : String(e));
538
545
  }
539
546
  }
547
+ function currentBranch() {
548
+ const gitBranch = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf8" }).stdout?.trim();
549
+ return process.env.PLURES_BRANCH || process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || (gitBranch && gitBranch !== "HEAD" ? gitBranch : "");
550
+ }
540
551
  async function driftFix(id) {
541
552
  if (!id) die("npx plures drift fix <case id> [--app <app>]");
542
553
  const cl = client();
@@ -552,6 +563,8 @@ async function driftFix(id) {
552
563
  } catch (e) {
553
564
  return fail(e instanceof PluresError ? e.message : String(e));
554
565
  }
566
+ const est = await cl.maps.estimate(app, "drift", { case: id }).catch(() => null);
567
+ if (est?.blocked) return fail(est.blocked);
555
568
  if (!hasClaude()) return fail("Claude Code isn't installed here, so nothing could read the code.");
556
569
  const git = (...a) => spawnSync("git", a, { encoding: "utf8" });
557
570
  if (git("status", "--porcelain").stdout.trim()) return fail("This repo has changes that aren't committed. Commit or stash them first.");
@@ -559,7 +572,7 @@ async function driftFix(id) {
559
572
  const ownClaude = Boolean(process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.ANTHROPIC_AUTH_TOKEN);
560
573
  if (!ownClaude && process.env.CI) {
561
574
  try {
562
- const run = await cl.maps.startRun(app);
575
+ const run = await cl.maps.startRun(app, { purpose: "drift", case: id });
563
576
  childEnv.ANTHROPIC_BASE_URL = run.base_url;
564
577
  childEnv.ANTHROPIC_API_KEY = run.token;
565
578
  } catch (e) {
package/dist/index.cjs CHANGED
@@ -26,7 +26,7 @@ __export(index_exports, {
26
26
  default: () => index_default
27
27
  });
28
28
  module.exports = __toCommonJS(index_exports);
29
- var VERSION = "1.18.0";
29
+ var VERSION = "1.22.0";
30
30
  var PluresError = class extends Error {
31
31
  constructor(message, status, type) {
32
32
  super(message);
@@ -134,8 +134,12 @@ var Plures = class {
134
134
  createKey: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`, opts),
135
135
  keys: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`),
136
136
  revokeKey: (app, id) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app)}/map_keys/${encodeURIComponent(id)}`),
137
+ /** What the next map or drift fix will cost before it starts. A map: the Plures engine fee, per step of the app's map
138
+ * (the first map of each app is free), and Claude at cost if Plures runs it. A drift fix: paid by outcome,
139
+ * drift_fix.per_point_cents for each quality point it wins back, at most drift_fix.max_cents, Claude included. blocked says why it can't start. */
140
+ estimate: (app, kind = "map", opts = {}) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_estimate?for=${kind}${opts.case ? `&case=${encodeURIComponent(opts.case)}` : ""}`),
137
141
  /** Starts a map run where Plures runs Claude (CI with no Anthropic key). npx plures map does this. */
138
- startRun: (app) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`, {}),
142
+ startRun: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`, opts),
139
143
  runs: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`),
140
144
  /** While mapping, which files the coding agent is reading, shown live on the map page. File names only. npx plures map does this. */
141
145
  progress: (app, p) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map/progress`, p),
@@ -171,6 +175,9 @@ var Plures = class {
171
175
  * early, and compare two versions blind before you ship. Personal details are taken out first.
172
176
  */
173
177
  quality = {
178
+ /** Rubrics the Plures engine suggests for an app: from what graders found in its answers (source "grades", with
179
+ * evidence) and from its agent map (source "map", with the step and file). Add one with rubrics.create. */
180
+ suggestions: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/rubric_suggestions`),
174
181
  /** Send every output; the rubric's sample rate decides which ones people grade. */
175
182
  sample: (p) => this.req("POST", "/api/v1/samples", p),
176
183
  /** A blind release check: a is the current version's output, b the new one's, for the same input. */
package/dist/index.d.cts CHANGED
@@ -1,3 +1,48 @@
1
+ type RubricSuggestion = {
2
+ object: "rubric_suggestion";
3
+ name: string;
4
+ question: string;
5
+ options: string[];
6
+ sample_rate: number;
7
+ why: string;
8
+ source: "map" | "grades";
9
+ evidence?: {
10
+ n: number;
11
+ examples: string[];
12
+ };
13
+ node: {
14
+ id: string;
15
+ label: string;
16
+ file: string | null;
17
+ line: number | null;
18
+ } | null;
19
+ };
20
+ type MapEstimate = {
21
+ object: "map_estimate";
22
+ app: string;
23
+ for: "map" | "drift";
24
+ engine_fee: {
25
+ steps: number | null;
26
+ cents: number | null;
27
+ free: boolean;
28
+ per_step_cents: number;
29
+ basis: string;
30
+ };
31
+ claude_cents_if_plures_runs: number | null;
32
+ /** For a drift fix: paid by outcome, per_point_cents for each quality point won back, at most max_cents. Claude included. */
33
+ drift_fix?: {
34
+ steps: number | null;
35
+ per_point_cents: number;
36
+ lost_points: number | null;
37
+ max_cents: number;
38
+ min_point_cents: number;
39
+ max_point_cents: number;
40
+ cap_cents: number;
41
+ outcome: true;
42
+ free_under_guarantee: boolean;
43
+ };
44
+ blocked: string | null;
45
+ };
1
46
  type Option = {
2
47
  id: string;
3
48
  label: string;
@@ -480,7 +525,7 @@ type WebhookEvent = {
480
525
  created_at: string;
481
526
  data: Task;
482
527
  };
483
- declare const VERSION = "1.18.0";
528
+ declare const VERSION = "1.22.0";
484
529
  type GitHubLink = {
485
530
  object: "github_link";
486
531
  app: string;
@@ -617,12 +662,22 @@ declare class Plures {
617
662
  id: string;
618
663
  deleted: true;
619
664
  }>;
665
+ /** What the next map or drift fix will cost before it starts. A map: the Plures engine fee, per step of the app's map
666
+ * (the first map of each app is free), and Claude at cost if Plures runs it. A drift fix: paid by outcome,
667
+ * drift_fix.per_point_cents for each quality point it wins back, at most drift_fix.max_cents, Claude included. blocked says why it can't start. */
668
+ estimate: (app: string, kind?: "map" | "drift", opts?: {
669
+ case?: string;
670
+ }) => Promise<MapEstimate>;
620
671
  /** Starts a map run where Plures runs Claude (CI with no Anthropic key). npx plures map does this. */
621
- startRun: (app: string) => Promise<{
672
+ startRun: (app: string, opts?: {
673
+ purpose?: "map" | "drift";
674
+ case?: string;
675
+ }) => Promise<{
622
676
  object: "map_run";
623
677
  id: string;
624
678
  app: string;
625
679
  free: boolean;
680
+ purpose: "map" | "drift";
626
681
  max_cents: number;
627
682
  token: string;
628
683
  base_url: string;
@@ -723,6 +778,13 @@ declare class Plures {
723
778
  * early, and compare two versions blind before you ship. Personal details are taken out first.
724
779
  */
725
780
  quality: {
781
+ /** Rubrics the Plures engine suggests for an app: from what graders found in its answers (source "grades", with
782
+ * evidence) and from its agent map (source "map", with the step and file). Add one with rubrics.create. */
783
+ suggestions: (app: string) => Promise<{
784
+ object: "list";
785
+ app: string;
786
+ data: RubricSuggestion[];
787
+ }>;
726
788
  /** Send every output; the rubric's sample rate decides which ones people grade. */
727
789
  sample: (p: {
728
790
  rubric: string;
@@ -898,4 +960,4 @@ declare class Plures {
898
960
  static verifyWebhook(rawBody: string, signatureHeader: string | null, secret: string, toleranceSec?: number): Promise<WebhookEvent>;
899
961
  }
900
962
 
901
- export { type AgentMap, type App, type Balance, type CheckOverrides, type CheckResult, type Consensus, type CreateTask, type DriftCase, type DriftDiagnosis, type Escalation, type GitHubLink, type MapChanges, type MapCheckpoint, type MapDraft, type MapKey, type MapNode, type Mix, type Option, Plures, PluresError, type Release, type Result, type Route, type Routing, type RoutingRule, type RoutingUpdate, type Rubric, type RubricParams, type Sample, type StoredAgentMap, type Task, type TaskStatus, type TeamTally, type TeamVote, type TestMix, type TurnParams, VERSION, type WebhookEvent, Plures as default };
963
+ export { type AgentMap, type App, type Balance, type CheckOverrides, type CheckResult, type Consensus, type CreateTask, type DriftCase, type DriftDiagnosis, type Escalation, type GitHubLink, type MapChanges, type MapCheckpoint, type MapDraft, type MapEstimate, type MapKey, type MapNode, type Mix, type Option, Plures, PluresError, type Release, type Result, type Route, type Routing, type RoutingRule, type RoutingUpdate, type Rubric, type RubricParams, type RubricSuggestion, type Sample, type StoredAgentMap, type Task, type TaskStatus, type TeamTally, type TeamVote, type TestMix, type TurnParams, VERSION, type WebhookEvent, Plures as default };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,48 @@
1
+ type RubricSuggestion = {
2
+ object: "rubric_suggestion";
3
+ name: string;
4
+ question: string;
5
+ options: string[];
6
+ sample_rate: number;
7
+ why: string;
8
+ source: "map" | "grades";
9
+ evidence?: {
10
+ n: number;
11
+ examples: string[];
12
+ };
13
+ node: {
14
+ id: string;
15
+ label: string;
16
+ file: string | null;
17
+ line: number | null;
18
+ } | null;
19
+ };
20
+ type MapEstimate = {
21
+ object: "map_estimate";
22
+ app: string;
23
+ for: "map" | "drift";
24
+ engine_fee: {
25
+ steps: number | null;
26
+ cents: number | null;
27
+ free: boolean;
28
+ per_step_cents: number;
29
+ basis: string;
30
+ };
31
+ claude_cents_if_plures_runs: number | null;
32
+ /** For a drift fix: paid by outcome, per_point_cents for each quality point won back, at most max_cents. Claude included. */
33
+ drift_fix?: {
34
+ steps: number | null;
35
+ per_point_cents: number;
36
+ lost_points: number | null;
37
+ max_cents: number;
38
+ min_point_cents: number;
39
+ max_point_cents: number;
40
+ cap_cents: number;
41
+ outcome: true;
42
+ free_under_guarantee: boolean;
43
+ };
44
+ blocked: string | null;
45
+ };
1
46
  type Option = {
2
47
  id: string;
3
48
  label: string;
@@ -480,7 +525,7 @@ type WebhookEvent = {
480
525
  created_at: string;
481
526
  data: Task;
482
527
  };
483
- declare const VERSION = "1.18.0";
528
+ declare const VERSION = "1.22.0";
484
529
  type GitHubLink = {
485
530
  object: "github_link";
486
531
  app: string;
@@ -617,12 +662,22 @@ declare class Plures {
617
662
  id: string;
618
663
  deleted: true;
619
664
  }>;
665
+ /** What the next map or drift fix will cost before it starts. A map: the Plures engine fee, per step of the app's map
666
+ * (the first map of each app is free), and Claude at cost if Plures runs it. A drift fix: paid by outcome,
667
+ * drift_fix.per_point_cents for each quality point it wins back, at most drift_fix.max_cents, Claude included. blocked says why it can't start. */
668
+ estimate: (app: string, kind?: "map" | "drift", opts?: {
669
+ case?: string;
670
+ }) => Promise<MapEstimate>;
620
671
  /** Starts a map run where Plures runs Claude (CI with no Anthropic key). npx plures map does this. */
621
- startRun: (app: string) => Promise<{
672
+ startRun: (app: string, opts?: {
673
+ purpose?: "map" | "drift";
674
+ case?: string;
675
+ }) => Promise<{
622
676
  object: "map_run";
623
677
  id: string;
624
678
  app: string;
625
679
  free: boolean;
680
+ purpose: "map" | "drift";
626
681
  max_cents: number;
627
682
  token: string;
628
683
  base_url: string;
@@ -723,6 +778,13 @@ declare class Plures {
723
778
  * early, and compare two versions blind before you ship. Personal details are taken out first.
724
779
  */
725
780
  quality: {
781
+ /** Rubrics the Plures engine suggests for an app: from what graders found in its answers (source "grades", with
782
+ * evidence) and from its agent map (source "map", with the step and file). Add one with rubrics.create. */
783
+ suggestions: (app: string) => Promise<{
784
+ object: "list";
785
+ app: string;
786
+ data: RubricSuggestion[];
787
+ }>;
726
788
  /** Send every output; the rubric's sample rate decides which ones people grade. */
727
789
  sample: (p: {
728
790
  rubric: string;
@@ -898,4 +960,4 @@ declare class Plures {
898
960
  static verifyWebhook(rawBody: string, signatureHeader: string | null, secret: string, toleranceSec?: number): Promise<WebhookEvent>;
899
961
  }
900
962
 
901
- export { type AgentMap, type App, type Balance, type CheckOverrides, type CheckResult, type Consensus, type CreateTask, type DriftCase, type DriftDiagnosis, type Escalation, type GitHubLink, type MapChanges, type MapCheckpoint, type MapDraft, type MapKey, type MapNode, type Mix, type Option, Plures, PluresError, type Release, type Result, type Route, type Routing, type RoutingRule, type RoutingUpdate, type Rubric, type RubricParams, type Sample, type StoredAgentMap, type Task, type TaskStatus, type TeamTally, type TeamVote, type TestMix, type TurnParams, VERSION, type WebhookEvent, Plures as default };
963
+ export { type AgentMap, type App, type Balance, type CheckOverrides, type CheckResult, type Consensus, type CreateTask, type DriftCase, type DriftDiagnosis, type Escalation, type GitHubLink, type MapChanges, type MapCheckpoint, type MapDraft, type MapEstimate, type MapKey, type MapNode, type Mix, type Option, Plures, PluresError, type Release, type Result, type Route, type Routing, type RoutingRule, type RoutingUpdate, type Rubric, type RubricParams, type RubricSuggestion, type Sample, type StoredAgentMap, type Task, type TaskStatus, type TeamTally, type TeamVote, type TestMix, type TurnParams, VERSION, type WebhookEvent, Plures as default };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  PluresError,
4
4
  VERSION,
5
5
  index_default
6
- } from "./chunk-RKIJTDS3.js";
6
+ } from "./chunk-3P2G67SF.js";
7
7
  export {
8
8
  Plures,
9
9
  PluresError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plures",
3
- "version": "1.18.0",
3
+ "version": "1.22.0",
4
4
  "description": "Human judgment as an API for AI agents. Send a decision to ID verified people and get their consensus back.",
5
5
  "keywords": [
6
6
  "human-in-the-loop",