syntaur 0.66.1 → 0.67.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "syntaur",
3
- "version": "0.66.1",
3
+ "version": "0.67.0",
4
4
  "description": "Syntaur protocol skills for AI coding agents — assignment, project, plan, and session lifecycle. Cross-agent (Claude Code, Codex, Cursor, OpenCode, Gemini CLI, and more via skills.sh).",
5
5
  "author": {
6
6
  "name": "Brennen",
@@ -11731,7 +11731,7 @@ function toDiscovered(meta) {
11731
11731
  transcriptPath: meta.path
11732
11732
  };
11733
11733
  }
11734
- var detectDir, claudeSessions, codexSessions, AGENT_TARGETS, AGENT_TARGETS_BY_ID;
11734
+ var detectDir, claudeSessions, codexSessions, piSessions, AGENT_TARGETS, AGENT_TARGETS_BY_ID;
11735
11735
  var init_registry = __esm({
11736
11736
  "src/targets/registry.ts"() {
11737
11737
  "use strict";
@@ -11759,6 +11759,16 @@ var init_registry = __esm({
11759
11759
  }
11760
11760
  }
11761
11761
  };
11762
+ piSessions = {
11763
+ globs: (root) => [join10(root ?? resolvePiSessionsRoot(), "*", "*.jsonl")],
11764
+ parse: async (file) => toDiscovered(await extractPiSessionMeta(file)),
11765
+ walk: async function* (opts = {}) {
11766
+ for await (const meta of walkPiSessions({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
11767
+ const d = toDiscovered(meta);
11768
+ if (d) yield d;
11769
+ }
11770
+ }
11771
+ };
11762
11772
  AGENT_TARGETS = [
11763
11773
  {
11764
11774
  id: "cursor",
@@ -11815,6 +11825,7 @@ var init_registry = __esm({
11815
11825
  detect: detectDir(home(".pi")),
11816
11826
  skillsDir: { global: home(".pi", "agent", "skills") },
11817
11827
  instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
11828
+ sessions: piSessions,
11818
11829
  tier3: {
11819
11830
  kind: "pi-extension",
11820
11831
  source: "platforms/pi/extensions/syntaur",
@@ -25101,6 +25112,41 @@ function runRollup() {
25101
25112
  return { daysComputed, rowsWritten };
25102
25113
  }
25103
25114
 
25115
+ // src/usage/pricing.ts
25116
+ var PER_MILLION = 1e6;
25117
+ var MODEL_PRICING = {
25118
+ // Moonshot Kimi K2.6 — the model pi emits today. Official Moonshot list price.
25119
+ // source: https://platform.moonshot.ai/ (official) — cross-checked
25120
+ // https://openrouter.ai/moonshotai/kimi-k2.6 (retrieved 2026-06-17)
25121
+ "moonshotai/kimi-k2.6": { input: 0.95, output: 4, cacheRead: 0.16, cacheWrite: 0.95 },
25122
+ // Moonshot Kimi K2.5 — prior Kimi model. Official Moonshot list price.
25123
+ // source: https://platform.moonshot.ai/ — cross-checked
25124
+ // https://openrouter.ai/moonshotai/kimi-k2.5 (retrieved 2026-06-17)
25125
+ "moonshotai/kimi-k2.5": { input: 0.6, output: 3, cacheRead: 0.1, cacheWrite: 0.6 },
25126
+ // Z.ai (Zhipu) GLM-5.2 — official z.ai platform list price.
25127
+ // source: https://docs.z.ai/guides/overview/pricing — cross-checked
25128
+ // https://openrouter.ai/z-ai/glm-5 (retrieved 2026-06-18)
25129
+ "zai-org/glm-5.2": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 1.4 },
25130
+ // MiniMax M2.5 — official MiniMax platform list price. No separately-pinned
25131
+ // cached-read rate is published, so cacheRead is set conservatively to the
25132
+ // input rate (upper bound); revise if MiniMax publishes a cache rate.
25133
+ // source: https://platform.minimax.io/docs/guides/pricing-token-plan
25134
+ // — cross-checked https://openrouter.ai/minimax/minimax-m2.5 (retrieved 2026-06-18)
25135
+ "minimaxai/minimax-m2.5": { input: 0.15, output: 0.9, cacheRead: 0.15, cacheWrite: 0.15 }
25136
+ // NOTE: opaque Synthetic tier aliases like `syn:large:text` have no public
25137
+ // per-token rate (they route to whatever Synthetic assigns), so they remain
25138
+ // unpriced (→ $0). Reseller discounts (e.g. DeepInfra K2.6 0.75/3.50/0.15) are
25139
+ // rejected by the canonical-source rule and are NOT used here.
25140
+ };
25141
+ function normalizeModelKey(model) {
25142
+ return model.replace(/^\s*\[[^\]]*\]\s*/, "").replace(/^hf:/i, "").trim().toLowerCase();
25143
+ }
25144
+ function priceForModel(model, tokens) {
25145
+ const rate = MODEL_PRICING[normalizeModelKey(model)];
25146
+ if (!rate) return null;
25147
+ return (tokens.inputTokens * rate.input + tokens.outputTokens * rate.output + tokens.cacheReadTokens * rate.cacheRead + tokens.cacheCreationTokens * rate.cacheWrite) / PER_MILLION;
25148
+ }
25149
+
25104
25150
  // src/usage/collect.ts
25105
25151
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
25106
25152
  function formatDayFromCcusageDate(yyyymmdd) {
@@ -25134,6 +25180,12 @@ async function collectAndPersist() {
25134
25180
  cwd,
25135
25181
  eventTs: row.eventTs
25136
25182
  });
25183
+ const totalCost = row.totalCost > 0 ? row.totalCost : priceForModel(row.model, {
25184
+ inputTokens: row.inputTokens,
25185
+ outputTokens: row.outputTokens,
25186
+ cacheCreationTokens: row.cacheCreationTokens,
25187
+ cacheReadTokens: row.cacheReadTokens
25188
+ }) ?? 0;
25137
25189
  return {
25138
25190
  sessionId: row.sessionId,
25139
25191
  model: row.model,
@@ -25144,7 +25196,7 @@ async function collectAndPersist() {
25144
25196
  cacheCreationTokens: row.cacheCreationTokens,
25145
25197
  cacheReadTokens: row.cacheReadTokens,
25146
25198
  totalTokens: row.totalTokens,
25147
- totalCost: row.totalCost,
25199
+ totalCost,
25148
25200
  cwd,
25149
25201
  projectSlug: attr.projectSlug ?? "",
25150
25202
  assignmentSlug: attr.assignmentSlug ?? "",
@@ -25161,8 +25213,72 @@ async function collectAndPersist() {
25161
25213
  tx.immediate();
25162
25214
  return { isFirstRun, rowsIngested: enriched.length };
25163
25215
  }
25216
+ function backfillZeroCostEvents() {
25217
+ const db5 = getUsageDb();
25218
+ const select = db5.prepare(
25219
+ `SELECT session_id, model, input_tokens, output_tokens,
25220
+ cache_creation_tokens, cache_read_tokens
25221
+ FROM usage_events
25222
+ WHERE total_cost = 0`
25223
+ );
25224
+ const update = db5.prepare(
25225
+ `UPDATE usage_events SET total_cost = @cost, updated_at = @updatedAt
25226
+ WHERE session_id = @sessionId AND model = @model AND total_cost = 0`
25227
+ );
25228
+ let updated = 0;
25229
+ const tx = db5.transaction(() => {
25230
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
25231
+ for (const r of select.all()) {
25232
+ const cost = priceForModel(r.model, {
25233
+ inputTokens: r.input_tokens,
25234
+ outputTokens: r.output_tokens,
25235
+ cacheCreationTokens: r.cache_creation_tokens,
25236
+ cacheReadTokens: r.cache_read_tokens
25237
+ });
25238
+ if (cost !== null && cost > 0) {
25239
+ updated += update.run({ cost, updatedAt, sessionId: r.session_id, model: r.model }).changes;
25240
+ }
25241
+ }
25242
+ });
25243
+ tx.immediate();
25244
+ return updated;
25245
+ }
25246
+ function reattributeOrphanEvents() {
25247
+ const db5 = getUsageDb();
25248
+ const select = db5.prepare(
25249
+ `SELECT session_id, model, cwd, event_ts
25250
+ FROM usage_events
25251
+ WHERE project_slug = '' AND assignment_slug = ''`
25252
+ );
25253
+ const update = db5.prepare(
25254
+ `UPDATE usage_events
25255
+ SET project_slug = @projectSlug, assignment_slug = @assignmentSlug, updated_at = @updatedAt
25256
+ WHERE session_id = @sessionId AND model = @model
25257
+ AND project_slug = '' AND assignment_slug = ''`
25258
+ );
25259
+ let updated = 0;
25260
+ const tx = db5.transaction(() => {
25261
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
25262
+ for (const r of select.all()) {
25263
+ const attr = resolveAttribution({
25264
+ sessionId: r.session_id,
25265
+ cwd: r.cwd,
25266
+ eventTs: r.event_ts
25267
+ });
25268
+ const projectSlug = attr.projectSlug ?? "";
25269
+ const assignmentSlug = attr.assignmentSlug ?? "";
25270
+ if (projectSlug !== "" || assignmentSlug !== "") {
25271
+ updated += update.run({ projectSlug, assignmentSlug, updatedAt, sessionId: r.session_id, model: r.model }).changes;
25272
+ }
25273
+ }
25274
+ });
25275
+ tx.immediate();
25276
+ return updated;
25277
+ }
25164
25278
  async function collectUsage() {
25165
25279
  const info = await collectAndPersist();
25280
+ backfillZeroCostEvents();
25281
+ reattributeOrphanEvents();
25166
25282
  runRollup();
25167
25283
  advanceMetaIso("usage_collector_heartbeat", (/* @__PURE__ */ new Date()).toISOString());
25168
25284
  return info;