@wrongstack/plugins 0.309.1 → 0.310.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.
@@ -45,6 +45,8 @@ export interface ModelPricing {
45
45
  input: number;
46
46
  /** Cost per 1M output (completion) tokens in USD. */
47
47
  output: number;
48
+ /** Cost per 1M prompt-cache read tokens; input rate is the safe fallback. */
49
+ cacheRead?: number | undefined;
48
50
  }
49
51
  declare const plugin: Plugin;
50
52
  export default plugin;
@@ -12,13 +12,15 @@ var __copyProps = (to, from, except, desc) => {
12
12
  };
13
13
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
14
14
 
15
+ // src/cost-tracker/index.ts
16
+ import { expectDefined } from "@wrongstack/core/utils";
17
+
15
18
  // src/runtime/index.ts
16
19
  var runtime_exports = {};
17
20
  __reExport(runtime_exports, runtime_star);
18
21
  import * as runtime_star from "@wrongstack/plugin-sdk/runtime";
19
22
 
20
23
  // src/cost-tracker/index.ts
21
- import { expectDefined } from "@wrongstack/core/utils";
22
24
  var API_VERSION = "^0.1.10";
23
25
  var PRICING = {
24
26
  "gpt-4o": { input: 5, output: 15 },
@@ -48,14 +50,14 @@ function readCostTrackerConfig(raw) {
48
50
  };
49
51
  }
50
52
  var modelKeyCache = new runtime_exports.BoundedMap({ max: 256 });
51
- function estimateCost(model, promptTokens, completionTokens) {
53
+ function estimateCost(model, freshTokens, completionTokens, cachedTokens = 0) {
52
54
  let key = modelKeyCache.get(model);
53
55
  if (!key) {
54
56
  key = model.toLowerCase();
55
57
  modelKeyCache.set(model, key);
56
58
  }
57
59
  const pricing = pricingOverrides[key] ?? bundledFromRegistry[key] ?? PRICING[key] ?? DEFAULT_PRICING;
58
- const inputCost = promptTokens / 1e6 * pricing.input;
60
+ const inputCost = freshTokens / 1e6 * pricing.input + cachedTokens / 1e6 * (pricing.cacheRead ?? pricing.input);
59
61
  const outputCost = completionTokens / 1e6 * pricing.output;
60
62
  return inputCost + outputCost;
61
63
  }
@@ -89,12 +91,17 @@ var plugin = {
89
91
  },
90
92
  pricingOverrides: {
91
93
  type: "object",
92
- description: "Per-model pricing overrides in USD per 1M tokens. Keys are lowercased model names; values are { input, output }. Takes precedence over the bundled PRICING table.",
94
+ description: "Per-model pricing overrides in USD per 1M tokens. Values are { input, output, cacheRead? }.",
93
95
  additionalProperties: {
94
96
  type: "object",
95
97
  properties: {
96
98
  input: { type: "number", minimum: 0, description: "Cost per 1M input tokens in USD" },
97
- output: { type: "number", minimum: 0, description: "Cost per 1M output tokens in USD" }
99
+ output: { type: "number", minimum: 0, description: "Cost per 1M output tokens in USD" },
100
+ cacheRead: {
101
+ type: "number",
102
+ minimum: 0,
103
+ description: "Cost per 1M prompt-cache read tokens in USD"
104
+ }
98
105
  },
99
106
  required: ["input", "output"],
100
107
  additionalProperties: false
@@ -127,7 +134,12 @@ var plugin = {
127
134
  const input = v["input"];
128
135
  const output = v["output"];
129
136
  if (typeof input !== "number" || typeof output !== "number") continue;
130
- pricingOverrides[model.toLowerCase()] = { input, output };
137
+ const cacheRead = v["cacheRead"];
138
+ pricingOverrides[model.toLowerCase()] = {
139
+ input,
140
+ output,
141
+ ...typeof cacheRead === "number" ? { cacheRead } : {}
142
+ };
131
143
  }
132
144
  }
133
145
  if (api.modelsRegistry) {
@@ -142,7 +154,8 @@ var plugin = {
142
154
  if (cost && typeof cost.input === "number" && typeof cost.output === "number") {
143
155
  bundledFromRegistry[modelId.toLowerCase()] = {
144
156
  input: cost.input,
145
- output: cost.output
157
+ output: cost.output,
158
+ ...typeof cost.cache_read === "number" ? { cacheRead: cost.cache_read } : {}
146
159
  };
147
160
  hydrated += 1;
148
161
  }
@@ -169,10 +182,12 @@ var plugin = {
169
182
  api.onEvent("provider.response", async (payload) => {
170
183
  const usage = payload.usage;
171
184
  const model = payload.ctx?.model ?? "unknown";
172
- const promptTokens = usage.input ?? 0;
185
+ const cachedTokens = usage.cacheRead ?? 0;
186
+ const freshTokens = (usage.input ?? 0) + (usage.cacheWrite ?? 0);
187
+ const promptTokens = freshTokens + cachedTokens;
173
188
  const completionTokens = usage.output ?? 0;
174
189
  const totalTokens = promptTokens + completionTokens;
175
- const costUsd = estimateCost(model, promptTokens, completionTokens);
190
+ const costUsd = estimateCost(model, freshTokens, completionTokens, cachedTokens);
176
191
  const record = {
177
192
  promptTokens,
178
193
  completionTokens,
package/dist/index.js CHANGED
@@ -4370,14 +4370,14 @@ function readCostTrackerConfig(raw) {
4370
4370
  };
4371
4371
  }
4372
4372
  var modelKeyCache = new runtime_exports.BoundedMap({ max: 256 });
4373
- function estimateCost(model, promptTokens, completionTokens) {
4373
+ function estimateCost(model, freshTokens, completionTokens, cachedTokens = 0) {
4374
4374
  let key = modelKeyCache.get(model);
4375
4375
  if (!key) {
4376
4376
  key = model.toLowerCase();
4377
4377
  modelKeyCache.set(model, key);
4378
4378
  }
4379
4379
  const pricing = pricingOverrides[key] ?? bundledFromRegistry[key] ?? PRICING[key] ?? DEFAULT_PRICING;
4380
- const inputCost = promptTokens / 1e6 * pricing.input;
4380
+ const inputCost = freshTokens / 1e6 * pricing.input + cachedTokens / 1e6 * (pricing.cacheRead ?? pricing.input);
4381
4381
  const outputCost = completionTokens / 1e6 * pricing.output;
4382
4382
  return inputCost + outputCost;
4383
4383
  }
@@ -4411,12 +4411,17 @@ var plugin14 = {
4411
4411
  },
4412
4412
  pricingOverrides: {
4413
4413
  type: "object",
4414
- description: "Per-model pricing overrides in USD per 1M tokens. Keys are lowercased model names; values are { input, output }. Takes precedence over the bundled PRICING table.",
4414
+ description: "Per-model pricing overrides in USD per 1M tokens. Values are { input, output, cacheRead? }.",
4415
4415
  additionalProperties: {
4416
4416
  type: "object",
4417
4417
  properties: {
4418
4418
  input: { type: "number", minimum: 0, description: "Cost per 1M input tokens in USD" },
4419
- output: { type: "number", minimum: 0, description: "Cost per 1M output tokens in USD" }
4419
+ output: { type: "number", minimum: 0, description: "Cost per 1M output tokens in USD" },
4420
+ cacheRead: {
4421
+ type: "number",
4422
+ minimum: 0,
4423
+ description: "Cost per 1M prompt-cache read tokens in USD"
4424
+ }
4420
4425
  },
4421
4426
  required: ["input", "output"],
4422
4427
  additionalProperties: false
@@ -4449,7 +4454,12 @@ var plugin14 = {
4449
4454
  const input = v["input"];
4450
4455
  const output = v["output"];
4451
4456
  if (typeof input !== "number" || typeof output !== "number") continue;
4452
- pricingOverrides[model.toLowerCase()] = { input, output };
4457
+ const cacheRead = v["cacheRead"];
4458
+ pricingOverrides[model.toLowerCase()] = {
4459
+ input,
4460
+ output,
4461
+ ...typeof cacheRead === "number" ? { cacheRead } : {}
4462
+ };
4453
4463
  }
4454
4464
  }
4455
4465
  if (api.modelsRegistry) {
@@ -4464,7 +4474,8 @@ var plugin14 = {
4464
4474
  if (cost && typeof cost.input === "number" && typeof cost.output === "number") {
4465
4475
  bundledFromRegistry[modelId.toLowerCase()] = {
4466
4476
  input: cost.input,
4467
- output: cost.output
4477
+ output: cost.output,
4478
+ ...typeof cost.cache_read === "number" ? { cacheRead: cost.cache_read } : {}
4468
4479
  };
4469
4480
  hydrated += 1;
4470
4481
  }
@@ -4491,10 +4502,12 @@ var plugin14 = {
4491
4502
  api.onEvent("provider.response", async (payload) => {
4492
4503
  const usage = payload.usage;
4493
4504
  const model = payload.ctx?.model ?? "unknown";
4494
- const promptTokens = usage.input ?? 0;
4505
+ const cachedTokens = usage.cacheRead ?? 0;
4506
+ const freshTokens = (usage.input ?? 0) + (usage.cacheWrite ?? 0);
4507
+ const promptTokens = freshTokens + cachedTokens;
4495
4508
  const completionTokens = usage.output ?? 0;
4496
4509
  const totalTokens = promptTokens + completionTokens;
4497
- const costUsd = estimateCost(model, promptTokens, completionTokens);
4510
+ const costUsd = estimateCost(model, freshTokens, completionTokens, cachedTokens);
4498
4511
  const record = {
4499
4512
  promptTokens,
4500
4513
  completionTokens,
@@ -22673,9 +22686,10 @@ var plugin60 = {
22673
22686
  var todo_listener_default = plugin60;
22674
22687
 
22675
22688
  // src/todo-tracker/index.ts
22676
- import * as fsp from "node:fs/promises";
22677
22689
  import { randomUUID } from "node:crypto";
22690
+ import * as fsp from "node:fs/promises";
22678
22691
  import { atomicWrite as atomicWrite3, ensureDir as ensureDir3 } from "@wrongstack/core/utils";
22692
+ import { nowIso } from "@wrongstack/primitives";
22679
22693
  function deriveFilePath(api) {
22680
22694
  const raw = api.config.extensions?.["todo-tracker"];
22681
22695
  const explicit = typeof raw?.["filePath"] === "string" ? raw["filePath"] : null;
@@ -22720,9 +22734,6 @@ var state56 = {
22720
22734
  /** Most recent mutation for /diag plugins visibility. */
22721
22735
  lastMutation: null
22722
22736
  };
22723
- function nowIso() {
22724
- return (/* @__PURE__ */ new Date()).toISOString();
22725
- }
22726
22737
  function ensureFile() {
22727
22738
  if (!state56.file) {
22728
22739
  state56.file = {
@@ -1,3 +1,32 @@
1
+ /**
2
+ * todo-tracker plugin — Persistent, project-scoped todo backlog that
3
+ * survives across sessions.
4
+ *
5
+ * Why a separate plugin from the built-in `todo` tool?
6
+ * - The built-in `todo` tool mutates `ctx.todos`, which is
7
+ * session-scoped and auto-clears when all items complete.
8
+ * - todo-tracker writes to disk (`~/.wrongstack/projects/<slug>/todo-tracker.json`)
9
+ * and survives across sessions. Items are explicit add/complete;
10
+ * no auto-clear.
11
+ *
12
+ * Use cases:
13
+ * - Backlog of work the user wants to track over days/weeks
14
+ * - Items the LLM noticed but didn't finish — pull them into a fresh
15
+ * session via `todo_tracker_pull` (the LLM then registers them with
16
+ * the session's `ctx.todos` via the built-in `todo` tool)
17
+ * - Per-project scratchpad that survives `wstack resume <id>`
18
+ *
19
+ * Tools registered:
20
+ * - todo_tracker_list : List items, filterable by status/tag/priority
21
+ * - todo_tracker_add : Append a new item
22
+ * - todo_tracker_complete : Mark an item completed (idempotent)
23
+ * - todo_tracker_drop : Mark an item dropped (idempotent)
24
+ * - todo_tracker_remove : Permanently delete by id
25
+ * - todo_tracker_pull : Return pending items for LLM to promote
26
+ * into the session's ctx.todos via the
27
+ * built-in `todo` tool
28
+ * - todo_tracker_status : Counters + last update timestamp
29
+ */
1
30
  import type { Plugin } from '@wrongstack/core/types';
2
31
  declare const plugin: Plugin;
3
32
  export default plugin;
@@ -1,7 +1,8 @@
1
1
  // src/todo-tracker/index.ts
2
- import * as fsp from "node:fs/promises";
3
2
  import { randomUUID } from "node:crypto";
3
+ import * as fsp from "node:fs/promises";
4
4
  import { atomicWrite, ensureDir } from "@wrongstack/core/utils";
5
+ import { nowIso } from "@wrongstack/primitives";
5
6
  function deriveFilePath(api) {
6
7
  const raw = api.config.extensions?.["todo-tracker"];
7
8
  const explicit = typeof raw?.["filePath"] === "string" ? raw["filePath"] : null;
@@ -46,9 +47,6 @@ var state = {
46
47
  /** Most recent mutation for /diag plugins visibility. */
47
48
  lastMutation: null
48
49
  };
49
- function nowIso() {
50
- return (/* @__PURE__ */ new Date()).toISOString();
51
- }
52
50
  function ensureFile() {
53
51
  if (!state.file) {
54
52
  state.file = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/plugins",
3
- "version": "0.309.1",
3
+ "version": "0.310.0",
4
4
  "description": "Official WrongStack collection of focused plugins for code quality, security, observability, planning, and agent coordination",
5
5
  "license": "MIT",
6
6
  "author": "ECOSTACK TECHNOLOGY OÜ",
@@ -303,9 +303,10 @@
303
303
  "vitest": "^4.1.11"
304
304
  },
305
305
  "dependencies": {
306
- "@wrongstack/plugin-sdk": "0.309.1",
307
- "@wrongstack/core": "0.309.1",
308
- "@wrongstack/tools": "0.309.1"
306
+ "@wrongstack/core": "0.310.0",
307
+ "@wrongstack/tools": "0.310.0",
308
+ "@wrongstack/primitives": "0.310.0",
309
+ "@wrongstack/plugin-sdk": "0.310.0"
309
310
  },
310
311
  "scripts": {
311
312
  "build": "node ../../scripts/build-package.mjs",