agent-orchestrator-kit 0.9.0 β 0.10.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 +6 -0
- package/README.md +5 -2
- package/bin/agent-orchestrator.js +16 -2
- package/bin/cursor-cost-estimate.js +39 -13
- package/bin/spend-collect.js +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.10.0] - 2026-08-31
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **`costUsdEstimated` first-class** in `metrics.json` (`spend`, platforms, models, sessions, sources). Cursor writes a labeled estimate whenever tokens exist: grok-4.5/4.6 keep xAI API rates (`costSource: "api-estimate"`); other models use a versioned fallback of $3/1M input + $15/1M output (or $3.50/1M when only `totalTokens` is present, `costSource: "api-estimate-fallback"`). This is **not** a Cursor invoice and is never mixed into billed `costUsd`. Self-report `cost_usd` stays billed and is not copied into `costUsdEstimated`. Amp without a `Cost:` line leaves `costUsd: null` (tokens are not converted to USD; credits stay separate). Archived `metrics.json` files are not backfilled.
|
|
11
|
+
|
|
7
12
|
## [0.9.0] - 2026-08-31
|
|
8
13
|
|
|
9
14
|
### Added
|
|
@@ -252,6 +257,7 @@ All notable changes to this project will be documented in this file.
|
|
|
252
257
|
### Added
|
|
253
258
|
- Initial release: 5-role orchestration pipeline, `/opsx:*` commands, IDE sync
|
|
254
259
|
|
|
260
|
+
[0.10.0]: https://github.com/makshc2/agent-orchestrator-kit/compare/v0.9.0...v0.10.0
|
|
255
261
|
[0.9.0]: https://github.com/makshc2/agent-orchestrator-kit/compare/v0.8.0...v0.9.0
|
|
256
262
|
[0.8.0]: https://github.com/makshc2/agent-orchestrator-kit/compare/v0.7.0...v0.8.0
|
|
257
263
|
[0.7.0]: https://github.com/makshc2/agent-orchestrator-kit/compare/v0.6.0...v0.7.0
|
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ npx agent-orchestrator-kit@latest init --profile generic --ci gitlab --spec-veri
|
|
|
74
74
|
|
|
75
75
|
See [Installation](#installation) for profile/CI options.
|
|
76
76
|
|
|
77
|
-
**π Already have the kit installed? Upgrade to latest (UTC timestamps + Amp stamp parse, Amp billed `$`, Cursor API estimate, archive auto-collect in v0.9.0; locked Amp/Cursor client in v0.8.0; `## Metrics` self-report + opt-in `--collect` in v0.7.0 β **BREAKING:** `--no-collect` is gone; change metrics in v0.5.0+, factory phases 1β3 in v0.4.0+, lean pipeline / archive CLI in v0.3.0+, handoff CLI in v0.1.14+, Figma PAT in v0.1.11+):**
|
|
77
|
+
**π Already have the kit installed? Upgrade to latest (Cursor fallback estimate + first-class `costUsdEstimated` in v0.10.0; UTC timestamps + Amp stamp parse, Amp billed `$`, Cursor API estimate, archive auto-collect in v0.9.0; locked Amp/Cursor client in v0.8.0; `## Metrics` self-report + opt-in `--collect` in v0.7.0 β **BREAKING:** `--no-collect` is gone; change metrics in v0.5.0+, factory phases 1β3 in v0.4.0+, lean pipeline / archive CLI in v0.3.0+, handoff CLI in v0.1.14+, Figma PAT in v0.1.11+):**
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
npx agent-orchestrator-kit@latest update
|
|
@@ -804,7 +804,7 @@ Every change accumulates git-tracked `openspec/changes/<name>/metrics.json` β
|
|
|
804
804
|
- **`## Metrics` self-report** β Session Exit fills `handoff.md` with `platform`, `model`, `input_tokens`, `output_tokens`, `cost_usd`, `amp_credits`, `spend_source` (`unknown` when missing). Persist reads that section; `metrics.json` is the source of truth for what landed. CLI flags do not rewrite the section.
|
|
805
805
|
- **`session.model`** β `--model` β `## Metrics: model` β `AOK_MODEL` β collected sources (only with `--collect`) β `null` (stderr warning). Never a Closed role.
|
|
806
806
|
- **Session start** β `handoff --restore` writes a `pending` marker (`startedAt`, expected role).
|
|
807
|
-
- **Session end** β `handoff <name>` closes the pending session: duration, closed role, mapped phase (`explore` / `design` / `spec` / `review` / `apply` / `archive`), runtime (local/cloud), tasks snapshot (`n/m`), and spend from flags β self-report β adapters. `--input-tokens` / `--output-tokens` / `--total-tokens` / `--cost-usd` override session totals only and do not wipe `spendByPlatform` / `spendByModel`. Amp billed USD comes from `amp threads usage --details` (
|
|
807
|
+
- **Session end** β `handoff <name>` closes the pending session: duration, closed role, mapped phase (`explore` / `design` / `spec` / `review` / `apply` / `archive`), runtime (local/cloud), tasks snapshot (`n/m`), and spend from flags β self-report β adapters. `--input-tokens` / `--output-tokens` / `--total-tokens` / `--cost-usd` override session totals only and do not wipe `spendByPlatform` / `spendByModel`. Amp billed USD comes from `amp threads usage --details` (`Cost: $N` only; missing line β `costUsd: null`), not from converting credits. Cursor always writes a labeled `costUsdEstimated` when tokens exist: grok-4.6/4.5 use xAI API rates (`costSource: api-estimate`, including `-fast` and the 200k cliff); other models use a versioned fallback of $3/1M input + $15/1M output (or $3.50/1M when only `totalTokens` is present, `costSource: api-estimate-fallback`). That estimate is **not** the Cursor invoice and is never added into billed `costUsd`. Timestamps are ISO-8601 UTC (`β¦Z`). No restore marker? Pass `--started-at <iso>` or the duration stays honestly `null`.
|
|
808
808
|
- **Archive** β successful `archive <name>` always creates or finalizes `metrics.json`, sets `archivedAt`, appends an Archiver session, collects the locked client (Cursor hook / Amp export+usage / Claude JSONL) in `[last session.endedAt, now]`, and prints the same human summary as `metrics <name>`. `--collect` still runs all three adapters. Leftover apply `## Metrics` that repeats the previous session is ignored so those tokens are not counted twice.
|
|
809
809
|
- **Platform** β `--platform` β `## Metrics: platform` β `AOK_PLATFORM` β **pending client from `--restore`** β host env (Amp / Cursor / Claude Code) β collected sources (`cursor|claude|amp` only). Invalid `--platform` fails before persist/move.
|
|
810
810
|
- **Locked client** β `--restore` records `pending.platform` and Amp `pending.threadId` before phase work. Persist follows that clientβs flow even if persist runs in another shell (no `AMP_*` / `CURSOR_*`). Amp: `amp threads export` plus `amp threads usage --details` (`AOK_AMP_BIN`) and local `threads/*.json`. Export supplies `model` / tokens / `agentMode`; usage supplies billed `$`. If Amp runs tools over a pipe (`/dev/null`), thread id comes from `amp threads list`, not stale `session.json` `lastThreadId`. Cursor: spend hook file. Claude: `~/.claude/projects`. `--collect` still runs all three adapters.
|
|
@@ -1003,6 +1003,9 @@ Phase bounds and non-goals: [`openspec/specs/agentic-factory-roadmap/spec.md`](o
|
|
|
1003
1003
|
|
|
1004
1004
|
## Changelog
|
|
1005
1005
|
|
|
1006
|
+
### 0.10.0
|
|
1007
|
+
- `costUsdEstimated` is first-class on `metrics.json`; Cursor writes a labeled estimate whenever tokens exist β grok via xAI API (`costSource: api-estimate`), other models via versioned fallback $3/1M in + $15/1M out (or $3.50/1M when only `totalTokens`, `costSource: api-estimate-fallback`). The estimate is **not** an invoice and is never mixed into billed `costUsd`. Amp without a `Cost:` line leaves `costUsd: null`; self-report `cost_usd` stays billed.
|
|
1008
|
+
|
|
1006
1009
|
### 0.9.0
|
|
1007
1010
|
- **UTC timestamps** in `metrics.json` (`β¦Z`; broken Amp stamps parsed and normalized)
|
|
1008
1011
|
- Amp `threads usage --details` billed `$` + `agentMode` (never stored as `session.model`)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program } from 'commander';
|
|
3
3
|
import pc from 'picocolors';
|
|
4
|
-
import { readFileSync, existsSync, mkdirSync, copyFileSync, readdirSync, statSync, writeFileSync, rmSync, renameSync, chmodSync } from 'fs';
|
|
4
|
+
import { readFileSync, existsSync, mkdirSync, copyFileSync, readdirSync, statSync, writeFileSync, rmSync, renameSync, chmodSync, realpathSync } from 'fs';
|
|
5
5
|
import { join, dirname, basename, resolve } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { execSync } from 'child_process';
|
|
@@ -4248,4 +4248,18 @@ program
|
|
|
4248
4248
|
for (const line of renderMetricsSummary(metrics)) console.log(line);
|
|
4249
4249
|
});
|
|
4250
4250
|
|
|
4251
|
-
|
|
4251
|
+
function isDirectCliRun() {
|
|
4252
|
+
const entry = process.argv[1];
|
|
4253
|
+
if (!entry) return false;
|
|
4254
|
+
try {
|
|
4255
|
+
return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));
|
|
4256
|
+
} catch {
|
|
4257
|
+
return resolve(entry) === fileURLToPath(import.meta.url);
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
|
|
4261
|
+
if (isDirectCliRun()) {
|
|
4262
|
+
program.parse();
|
|
4263
|
+
}
|
|
4264
|
+
|
|
4265
|
+
export { formatMetricsCostLine, resolveSessionSpend };
|
|
@@ -14,6 +14,10 @@ const GROK_46 = {
|
|
|
14
14
|
longAt: 200000,
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
const FALLBACK_INPUT_PER_M = 3;
|
|
18
|
+
const FALLBACK_OUTPUT_PER_M = 15;
|
|
19
|
+
const FALLBACK_TOTAL_PER_M = 3.5;
|
|
20
|
+
|
|
17
21
|
function ratesForModel(model) {
|
|
18
22
|
const id = String(model || '').toLowerCase();
|
|
19
23
|
if (!id) return null;
|
|
@@ -32,19 +36,41 @@ function ratesForModel(model) {
|
|
|
32
36
|
return rates;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
export function estimateCursorCostUsd({ model, inputTokens, outputTokens, cacheReadTokens } = {}) {
|
|
39
|
+
export function estimateCursorCostUsd({ model, inputTokens, outputTokens, cacheReadTokens, totalTokens } = {}) {
|
|
36
40
|
const rates = ratesForModel(model);
|
|
37
|
-
if (
|
|
41
|
+
if (rates) {
|
|
42
|
+
const input = numOrNull(inputTokens);
|
|
43
|
+
const output = numOrNull(outputTokens) ?? 0;
|
|
44
|
+
if (input == null && output == 0) return null;
|
|
45
|
+
const totalInput = input ?? 0;
|
|
46
|
+
const cached = Math.min(numOrNull(cacheReadTokens) ?? 0, totalInput);
|
|
47
|
+
const fresh = Math.max(0, totalInput - cached);
|
|
48
|
+
const long = totalInput >= rates.longAt;
|
|
49
|
+
const inputRate = long ? rates.longInputPerM : rates.inputPerM;
|
|
50
|
+
const cachedRate = long ? rates.longCachedPerM : rates.cachedPerM;
|
|
51
|
+
const outputRate = long ? rates.longOutputPerM : rates.outputPerM;
|
|
52
|
+
const usd = (fresh * inputRate + cached * cachedRate + output * outputRate) / 1e6;
|
|
53
|
+
return Math.round(usd * 10000) / 10000;
|
|
54
|
+
}
|
|
38
55
|
const input = numOrNull(inputTokens);
|
|
39
|
-
const output = numOrNull(outputTokens)
|
|
40
|
-
if (input
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return
|
|
56
|
+
const output = numOrNull(outputTokens);
|
|
57
|
+
if (input != null || output != null) {
|
|
58
|
+
const usd = ((input ?? 0) * FALLBACK_INPUT_PER_M + (output ?? 0) * FALLBACK_OUTPUT_PER_M) / 1e6;
|
|
59
|
+
return Math.round(usd * 10000) / 10000;
|
|
60
|
+
}
|
|
61
|
+
const total = numOrNull(totalTokens);
|
|
62
|
+
if (total != null) {
|
|
63
|
+
const usd = total * FALLBACK_TOTAL_PER_M / 1e6;
|
|
64
|
+
return Math.round(usd * 10000) / 10000;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function describeCursorCostEstimate(args) {
|
|
70
|
+
const usd = estimateCursorCostUsd(args);
|
|
71
|
+
if (usd == null) return null;
|
|
72
|
+
return {
|
|
73
|
+
usd,
|
|
74
|
+
costSource: ratesForModel(args && args.model) != null ? 'api-estimate' : 'api-estimate-fallback',
|
|
75
|
+
};
|
|
50
76
|
}
|
package/bin/spend-collect.js
CHANGED
|
@@ -4,7 +4,7 @@ import { homedir as osHomedir } from 'os';
|
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
5
|
import { listRecentAmpThreadIds } from './session-client.js';
|
|
6
6
|
import { formatUtcIso, parseFlexibleIso } from './metrics-time.js';
|
|
7
|
-
import {
|
|
7
|
+
import { describeCursorCostEstimate } from './cursor-cost-estimate.js';
|
|
8
8
|
import { ampAgentMode, matchAmpUsageModel, parseAmpUsageDetails } from './amp-usage.js';
|
|
9
9
|
|
|
10
10
|
const PLATFORMS = ['cursor', 'claude', 'amp'];
|
|
@@ -535,7 +535,7 @@ function collectCursor({ cwd, windowStart, windowEnd, existing, notes }) {
|
|
|
535
535
|
if (inputTokens == null && outputTokens == null) continue;
|
|
536
536
|
const model = row.model || row.modelId;
|
|
537
537
|
const cacheReadTokens = numOrNull(row.cacheReadTokens);
|
|
538
|
-
const
|
|
538
|
+
const described = describeCursorCostEstimate({
|
|
539
539
|
model,
|
|
540
540
|
inputTokens,
|
|
541
541
|
outputTokens,
|
|
@@ -551,8 +551,8 @@ function collectCursor({ cwd, windowStart, windowEnd, existing, notes }) {
|
|
|
551
551
|
ampCredits: null,
|
|
552
552
|
at: row.at,
|
|
553
553
|
cacheReadTokens,
|
|
554
|
-
costUsdEstimated:
|
|
555
|
-
costSource:
|
|
554
|
+
costUsdEstimated: described?.usd ?? null,
|
|
555
|
+
costSource: described?.costSource ?? null,
|
|
556
556
|
});
|
|
557
557
|
const previous = bestById.get(id);
|
|
558
558
|
if (!previous || (record.totalTokens ?? 0) >= (previous.totalTokens ?? 0)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-orchestrator-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Universal AI agent orchestration kit for Cursor, Claude Code, and Amp Code β spec-driven OpenSpec pipeline, conductor subagents, durable session handoff, factory gates and MCP setup, cloud-agent handoff, and optional local Figma PAT setup",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agent",
|