@minzicat/pi-team 0.1.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/Dockerfile +82 -0
- package/LICENSE +21 -0
- package/README.md +114 -0
- package/bin/pi-team.js +2 -0
- package/dist/agent.js +252 -0
- package/dist/agent.js.map +1 -0
- package/dist/cli.js +371 -0
- package/dist/cli.js.map +1 -0
- package/dist/http-inject.js +136 -0
- package/dist/http-inject.js.map +1 -0
- package/dist/inject-server.js +101 -0
- package/dist/inject-server.js.map +1 -0
- package/dist/observer.js +151 -0
- package/dist/observer.js.map +1 -0
- package/dist/orchestra.js +192 -0
- package/dist/orchestra.js.map +1 -0
- package/dist/tmux.js +54 -0
- package/dist/tmux.js.map +1 -0
- package/dist/transcript.js +71 -0
- package/dist/transcript.js.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/usage/plans.js +214 -0
- package/dist/usage/plans.js.map +1 -0
- package/dist/usage/reporter.js +69 -0
- package/dist/usage/reporter.js.map +1 -0
- package/dist/usage/tracker.js +200 -0
- package/dist/usage/tracker.js.map +1 -0
- package/dist/usage/types.js +18 -0
- package/dist/usage/types.js.map +1 -0
- package/docker/entrypoint.sh +94 -0
- package/docker-compose.yml +28 -0
- package/examples/emotion-debate-topic.md +20 -0
- package/examples/emotion-debate.sh +20 -0
- package/package.json +60 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan catalog.
|
|
3
|
+
*
|
|
4
|
+
* Hardcoded subscription limits for the plans we care about. Numbers come
|
|
5
|
+
* from publicly documented sources:
|
|
6
|
+
*
|
|
7
|
+
* - OpenAI Codex: https://developers.openai.com/codex/pricing/
|
|
8
|
+
* - Anthropic Claude: https://support.anthropic.com/en/articles/11145838
|
|
9
|
+
*
|
|
10
|
+
* These are ranges — the real cap depends on task complexity, context size,
|
|
11
|
+
* and server load. We track both the min (conservative) and max (optimistic)
|
|
12
|
+
* bounds. For Anthropic, published caps are *hours of use per week* for
|
|
13
|
+
* subscription tiers; for Codex, they're *messages per 5h window* with
|
|
14
|
+
* separate weekly caps.
|
|
15
|
+
*
|
|
16
|
+
* When a model isn't listed, it falls back to the plan's default. When a
|
|
17
|
+
* plan isn't listed at all, we default to "api" mode (pay-per-token, show
|
|
18
|
+
* dollar cost, no window tracking).
|
|
19
|
+
*/
|
|
20
|
+
const FIVE_HOURS_MS = 5 * 60 * 60 * 1000;
|
|
21
|
+
const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000;
|
|
22
|
+
/**
|
|
23
|
+
* OpenAI Codex via ChatGPT subscriptions.
|
|
24
|
+
*
|
|
25
|
+
* Local messages and cloud tasks share the 5h window. We track local-message
|
|
26
|
+
* caps here since pi agents run locally. Numbers are the "Local Messages / 5h"
|
|
27
|
+
* ranges published on the Codex pricing page.
|
|
28
|
+
*/
|
|
29
|
+
export const openaiPlus = {
|
|
30
|
+
id: "openai-plus",
|
|
31
|
+
provider: "openai-codex",
|
|
32
|
+
tier: "plus",
|
|
33
|
+
mode: "subscription",
|
|
34
|
+
description: "ChatGPT Plus ($20/mo) — includes Codex CLI access",
|
|
35
|
+
short: {
|
|
36
|
+
durationMs: FIVE_HOURS_MS,
|
|
37
|
+
unit: "messages",
|
|
38
|
+
capByModel: {
|
|
39
|
+
"gpt-5.4": { min: 33, max: 168 },
|
|
40
|
+
"gpt-5.4-mini": { min: 110, max: 560 },
|
|
41
|
+
"gpt-5.3-codex": { min: 45, max: 225 },
|
|
42
|
+
"gpt-5.3-codex-mini": { min: 150, max: 760 },
|
|
43
|
+
"gpt-5.1-codex-mini": { min: 150, max: 760 },
|
|
44
|
+
"gpt-5.2-codex": { min: 45, max: 225 },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
// Plus has no separately-published weekly cap; the 5h window is the practical limit.
|
|
48
|
+
};
|
|
49
|
+
export const openaiPro = {
|
|
50
|
+
id: "openai-pro",
|
|
51
|
+
provider: "openai-codex",
|
|
52
|
+
tier: "pro",
|
|
53
|
+
mode: "subscription",
|
|
54
|
+
// 6x Plus matches the published Pro ratio. Leaving the description and
|
|
55
|
+
// the numbers consistent so users don't see a "6x values but 2x promo"
|
|
56
|
+
// mismatch. Source: openai.com/chatgpt/pricing and the Codex docs.
|
|
57
|
+
description: "ChatGPT Pro ($200/mo) — 6x Plus limits",
|
|
58
|
+
short: {
|
|
59
|
+
durationMs: FIVE_HOURS_MS,
|
|
60
|
+
unit: "messages",
|
|
61
|
+
capByModel: {
|
|
62
|
+
// 6x the Plus ranges
|
|
63
|
+
"gpt-5.4": { min: 198, max: 1008 },
|
|
64
|
+
"gpt-5.4-mini": { min: 660, max: 3360 },
|
|
65
|
+
"gpt-5.3-codex": { min: 270, max: 1350 },
|
|
66
|
+
"gpt-5.3-codex-mini": { min: 900, max: 4560 },
|
|
67
|
+
"gpt-5.1-codex-mini": { min: 900, max: 4560 },
|
|
68
|
+
"gpt-5.2-codex": { min: 270, max: 1350 },
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
export const openaiBusiness = {
|
|
73
|
+
id: "openai-business",
|
|
74
|
+
provider: "openai-codex",
|
|
75
|
+
tier: "business",
|
|
76
|
+
mode: "subscription",
|
|
77
|
+
description: "ChatGPT Business ($25/user/mo) — ~2x Plus with team features",
|
|
78
|
+
short: {
|
|
79
|
+
durationMs: FIVE_HOURS_MS,
|
|
80
|
+
unit: "messages",
|
|
81
|
+
capByModel: {
|
|
82
|
+
"gpt-5.4": { min: 66, max: 336 },
|
|
83
|
+
"gpt-5.4-mini": { min: 220, max: 1120 },
|
|
84
|
+
"gpt-5.3-codex": { min: 90, max: 450 },
|
|
85
|
+
"gpt-5.3-codex-mini": { min: 300, max: 1520 },
|
|
86
|
+
"gpt-5.1-codex-mini": { min: 300, max: 1520 },
|
|
87
|
+
"gpt-5.2-codex": { min: 90, max: 450 },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Anthropic Claude subscriptions.
|
|
93
|
+
*
|
|
94
|
+
* Anthropic publishes per-model *hours of use* per week, plus a shared 5-hour
|
|
95
|
+
* session window with model-specific message caps. The official docs use
|
|
96
|
+
* hours; we track both as rough message-count approximations derived from
|
|
97
|
+
* typical-task assumptions. These ARE the least precise of all plans — the
|
|
98
|
+
* real enforcement is opaque.
|
|
99
|
+
*/
|
|
100
|
+
export const anthropicPro = {
|
|
101
|
+
id: "anthropic-pro",
|
|
102
|
+
provider: "anthropic",
|
|
103
|
+
tier: "pro",
|
|
104
|
+
mode: "subscription",
|
|
105
|
+
description: "Claude Pro ($20/mo) — Sonnet + Haiku; limited Opus",
|
|
106
|
+
short: {
|
|
107
|
+
durationMs: FIVE_HOURS_MS,
|
|
108
|
+
unit: "messages",
|
|
109
|
+
capByModel: {
|
|
110
|
+
"claude-sonnet-4-5": { min: 45, max: 225 },
|
|
111
|
+
"claude-sonnet-4-6": { min: 45, max: 225 },
|
|
112
|
+
"claude-opus-4-6": { min: 5, max: 25 },
|
|
113
|
+
"claude-opus-4-5": { min: 5, max: 25 },
|
|
114
|
+
"claude-haiku-4-5": { min: 150, max: 700 },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
long: {
|
|
118
|
+
durationMs: ONE_WEEK_MS,
|
|
119
|
+
unit: "hours",
|
|
120
|
+
capByModel: {
|
|
121
|
+
"claude-sonnet-4-5": { min: 40, max: 40 },
|
|
122
|
+
"claude-sonnet-4-6": { min: 40, max: 40 },
|
|
123
|
+
"claude-opus-4-6": { min: 5, max: 5 },
|
|
124
|
+
"claude-opus-4-5": { min: 5, max: 5 },
|
|
125
|
+
"claude-haiku-4-5": { min: 80, max: 80 },
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
export const anthropicMax5x = {
|
|
130
|
+
id: "anthropic-max-5x",
|
|
131
|
+
provider: "anthropic",
|
|
132
|
+
tier: "max-5x",
|
|
133
|
+
mode: "subscription",
|
|
134
|
+
description: "Claude Max 5x ($100/mo) — 5x the Pro limits",
|
|
135
|
+
short: {
|
|
136
|
+
durationMs: FIVE_HOURS_MS,
|
|
137
|
+
unit: "messages",
|
|
138
|
+
capByModel: {
|
|
139
|
+
"claude-sonnet-4-5": { min: 225, max: 1125 },
|
|
140
|
+
"claude-sonnet-4-6": { min: 225, max: 1125 },
|
|
141
|
+
"claude-opus-4-6": { min: 25, max: 125 },
|
|
142
|
+
"claude-opus-4-5": { min: 25, max: 125 },
|
|
143
|
+
"claude-haiku-4-5": { min: 750, max: 3500 },
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
long: {
|
|
147
|
+
durationMs: ONE_WEEK_MS,
|
|
148
|
+
unit: "hours",
|
|
149
|
+
capByModel: {
|
|
150
|
+
"claude-sonnet-4-5": { min: 200, max: 200 },
|
|
151
|
+
"claude-sonnet-4-6": { min: 200, max: 200 },
|
|
152
|
+
"claude-opus-4-6": { min: 25, max: 25 },
|
|
153
|
+
"claude-opus-4-5": { min: 25, max: 25 },
|
|
154
|
+
"claude-haiku-4-5": { min: 400, max: 400 },
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
export const anthropicMax20x = {
|
|
159
|
+
id: "anthropic-max-20x",
|
|
160
|
+
provider: "anthropic",
|
|
161
|
+
tier: "max-20x",
|
|
162
|
+
mode: "subscription",
|
|
163
|
+
description: "Claude Max 20x ($200/mo) — 20x the Pro limits",
|
|
164
|
+
short: {
|
|
165
|
+
durationMs: FIVE_HOURS_MS,
|
|
166
|
+
unit: "messages",
|
|
167
|
+
capByModel: {
|
|
168
|
+
"claude-sonnet-4-5": { min: 900, max: 4500 },
|
|
169
|
+
"claude-sonnet-4-6": { min: 900, max: 4500 },
|
|
170
|
+
"claude-opus-4-6": { min: 100, max: 500 },
|
|
171
|
+
"claude-opus-4-5": { min: 100, max: 500 },
|
|
172
|
+
"claude-haiku-4-5": { min: 3000, max: 14000 },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
long: {
|
|
176
|
+
durationMs: ONE_WEEK_MS,
|
|
177
|
+
unit: "hours",
|
|
178
|
+
capByModel: {
|
|
179
|
+
"claude-sonnet-4-5": { min: 800, max: 800 },
|
|
180
|
+
"claude-sonnet-4-6": { min: 800, max: 800 },
|
|
181
|
+
"claude-opus-4-6": { min: 100, max: 100 },
|
|
182
|
+
"claude-opus-4-5": { min: 100, max: 100 },
|
|
183
|
+
"claude-haiku-4-5": { min: 1600, max: 1600 },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* API (pay-per-token) — no window tracking, dollar cost is meaningful.
|
|
189
|
+
*/
|
|
190
|
+
export const apiPlan = {
|
|
191
|
+
id: "api",
|
|
192
|
+
provider: "*",
|
|
193
|
+
tier: "pay-per-token",
|
|
194
|
+
mode: "api",
|
|
195
|
+
description: "Pay-per-token via direct API key — dollar cost is real",
|
|
196
|
+
};
|
|
197
|
+
export const PLAN_CATALOG = {
|
|
198
|
+
"openai-plus": openaiPlus,
|
|
199
|
+
"openai-pro": openaiPro,
|
|
200
|
+
"openai-business": openaiBusiness,
|
|
201
|
+
"anthropic-pro": anthropicPro,
|
|
202
|
+
"anthropic-max-5x": anthropicMax5x,
|
|
203
|
+
"anthropic-max-20x": anthropicMax20x,
|
|
204
|
+
api: apiPlan,
|
|
205
|
+
};
|
|
206
|
+
export function getPlan(id) {
|
|
207
|
+
if (!id)
|
|
208
|
+
return apiPlan;
|
|
209
|
+
return PLAN_CATALOG[id] ?? apiPlan;
|
|
210
|
+
}
|
|
211
|
+
export function listPlans() {
|
|
212
|
+
return Object.keys(PLAN_CATALOG);
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=plans.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plans.js","sourceRoot":"","sources":["../../src/usage/plans.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a UsageSnapshot as a compact single-line status string, suitable
|
|
3
|
+
* for display in the observer after each turn.
|
|
4
|
+
*
|
|
5
|
+
* Examples:
|
|
6
|
+
* claude │ sonnet-4-5 │ 5h: 12/45–225 msg (26%) · wk: 0.4/40h
|
|
7
|
+
* codex │ gpt-5.4 high │ 5h: 5/33–168 msg (15%)
|
|
8
|
+
* api │ haiku-4-5 │ 1.2K in / 340 out $0.0042
|
|
9
|
+
*/
|
|
10
|
+
const DIM = "\x1b[2m";
|
|
11
|
+
const BOLD = "\x1b[1m";
|
|
12
|
+
const RESET = "\x1b[0m";
|
|
13
|
+
const GREEN = "\x1b[38;5;42m";
|
|
14
|
+
export function formatSnapshot(snap, opts = {}) {
|
|
15
|
+
const color = opts.color ?? true;
|
|
16
|
+
const c = (code, text) => (color ? `${code}${text}${RESET}` : text);
|
|
17
|
+
const agentLabel = c(BOLD, snap.agentName.padEnd(8));
|
|
18
|
+
const modelLabel = shortenModel(snap.model).padEnd(18);
|
|
19
|
+
if (snap.planMode === "api") {
|
|
20
|
+
// Pay-per-token display: tokens + dollar cost
|
|
21
|
+
const { input, output } = snap.sessionTokens;
|
|
22
|
+
const tokens = `${formatNum(input)} in / ${formatNum(output)} out`;
|
|
23
|
+
const cost = `$${snap.sessionDollarCost.toFixed(4)}`;
|
|
24
|
+
return `${agentLabel} │ ${modelLabel} │ ${tokens} ${c(DIM, cost)}`;
|
|
25
|
+
}
|
|
26
|
+
// Subscription display: windows
|
|
27
|
+
const parts = [];
|
|
28
|
+
if (snap.windows.short) {
|
|
29
|
+
parts.push(formatWindow("5h", snap.windows.short, color, opts.optimistic ?? false));
|
|
30
|
+
}
|
|
31
|
+
if (snap.windows.long) {
|
|
32
|
+
parts.push(formatWindow("wk", snap.windows.long, color, opts.optimistic ?? false));
|
|
33
|
+
}
|
|
34
|
+
const planLabel = c(DIM, snap.planId);
|
|
35
|
+
return `${agentLabel} │ ${modelLabel} │ ${parts.join(" · ")} ${planLabel}`;
|
|
36
|
+
}
|
|
37
|
+
function formatWindow(label, w, color, optimistic) {
|
|
38
|
+
const cap = optimistic && w.capMax ? w.capMax : w.cap;
|
|
39
|
+
const used = w.unit === "hours" ? w.used : Math.round(w.used);
|
|
40
|
+
const capStr = w.capMax && w.cap !== w.capMax ? `${w.cap}–${w.capMax}` : String(cap);
|
|
41
|
+
const unit = w.unit === "messages" ? "msg" : w.unit === "tokens" ? "tok" : "h";
|
|
42
|
+
const percent = cap > 0 ? Math.min(100, (w.used / cap) * 100) : 0;
|
|
43
|
+
const pctStr = `${percent.toFixed(0)}%`;
|
|
44
|
+
const usedStr = w.unit === "hours" ? used.toFixed(1) : String(used);
|
|
45
|
+
const body = `${label}: ${usedStr}/${capStr} ${unit} (${pctStr})`;
|
|
46
|
+
if (!color)
|
|
47
|
+
return body;
|
|
48
|
+
if (percent >= 90)
|
|
49
|
+
return `\x1b[38;5;196m${body}${RESET}`;
|
|
50
|
+
if (percent >= 75)
|
|
51
|
+
return `\x1b[38;5;220m${body}${RESET}`;
|
|
52
|
+
if (percent >= 50)
|
|
53
|
+
return `${GREEN}${body}${RESET}`;
|
|
54
|
+
return `${DIM}${body}${RESET}`;
|
|
55
|
+
}
|
|
56
|
+
function shortenModel(model) {
|
|
57
|
+
// Drop common prefixes for display
|
|
58
|
+
return model
|
|
59
|
+
.replace(/^claude-/, "")
|
|
60
|
+
.replace(/-20\d{6}$/, "");
|
|
61
|
+
}
|
|
62
|
+
function formatNum(n) {
|
|
63
|
+
if (n >= 1_000_000)
|
|
64
|
+
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
65
|
+
if (n >= 1_000)
|
|
66
|
+
return `${(n / 1_000).toFixed(1)}K`;
|
|
67
|
+
return String(n);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/usage/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UsageTracker: reads per-agent pi session JSONL files, extracts turn usage,
|
|
3
|
+
* and computes rolling-window aggregates against each agent's declared plan.
|
|
4
|
+
*
|
|
5
|
+
* Strategy:
|
|
6
|
+
* - Each agent's pi writes to agent-<name>.session.jsonl
|
|
7
|
+
* - Every "message" entry with role="assistant" has a `usage` object
|
|
8
|
+
* containing token counts, cost, provider, model, and timestamp
|
|
9
|
+
* - After each turn, the orchestrator calls tracker.syncAgent(name) which
|
|
10
|
+
* re-reads the agent's session file, extracts new turns, and recomputes
|
|
11
|
+
* the snapshot
|
|
12
|
+
*
|
|
13
|
+
* We deliberately do NOT tail the file in real-time (fs.watch). Pi's RPC
|
|
14
|
+
* mode already tells us exactly when turns end via agent_end, which is the
|
|
15
|
+
* only time we need a fresh snapshot. Polling at turn boundaries is simpler
|
|
16
|
+
* and avoids racing with pi's writer.
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
19
|
+
import { getPlan } from "./plans.js";
|
|
20
|
+
export class UsageTracker {
|
|
21
|
+
turnsByAgent = new Map();
|
|
22
|
+
plansByAgent = new Map();
|
|
23
|
+
register(agent) {
|
|
24
|
+
this.turnsByAgent.set(agent.name, []);
|
|
25
|
+
this.plansByAgent.set(agent.name, getPlan(agent.planId));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Re-parse the agent's session jsonl and refresh its turn list.
|
|
29
|
+
* Idempotent; safe to call after every turn.
|
|
30
|
+
*/
|
|
31
|
+
syncAgent(agentName, sessionPath) {
|
|
32
|
+
if (!existsSync(sessionPath))
|
|
33
|
+
return;
|
|
34
|
+
const turns = [];
|
|
35
|
+
const raw = readFileSync(sessionPath, "utf8");
|
|
36
|
+
for (const line of raw.split("\n")) {
|
|
37
|
+
if (!line.trim())
|
|
38
|
+
continue;
|
|
39
|
+
let entry;
|
|
40
|
+
try {
|
|
41
|
+
entry = JSON.parse(line);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (entry.type !== "message")
|
|
47
|
+
continue;
|
|
48
|
+
const m = entry.message;
|
|
49
|
+
if (!m || m.role !== "assistant")
|
|
50
|
+
continue;
|
|
51
|
+
const usage = m.usage;
|
|
52
|
+
if (!usage)
|
|
53
|
+
continue;
|
|
54
|
+
const timestamp = parseTimestamp(entry.timestamp) ?? Date.now();
|
|
55
|
+
turns.push({
|
|
56
|
+
agentName,
|
|
57
|
+
timestamp,
|
|
58
|
+
provider: String(m.provider ?? ""),
|
|
59
|
+
model: String(m.model ?? ""),
|
|
60
|
+
tokens: {
|
|
61
|
+
input: Number(usage.input ?? 0),
|
|
62
|
+
output: Number(usage.output ?? 0),
|
|
63
|
+
cacheRead: Number(usage.cacheRead ?? 0),
|
|
64
|
+
cacheWrite: Number(usage.cacheWrite ?? 0),
|
|
65
|
+
total: Number(usage.totalTokens ?? 0),
|
|
66
|
+
},
|
|
67
|
+
dollarCost: Number(usage.cost?.total ?? 0),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
this.turnsByAgent.set(agentName, turns);
|
|
71
|
+
}
|
|
72
|
+
snapshot(agentName) {
|
|
73
|
+
const turns = this.turnsByAgent.get(agentName);
|
|
74
|
+
const plan = this.plansByAgent.get(agentName);
|
|
75
|
+
if (!turns || !plan)
|
|
76
|
+
return null;
|
|
77
|
+
// `total` is computed as the sum of the four individual counters (one
|
|
78
|
+
// source of truth). Pi's own `usage.totalTokens` can include cached
|
|
79
|
+
// tokens in a different way across providers, so summing here keeps
|
|
80
|
+
// session totals consistent with the field breakdown exposed via
|
|
81
|
+
// /state and /usage.
|
|
82
|
+
const sessionTokens = {
|
|
83
|
+
input: 0,
|
|
84
|
+
output: 0,
|
|
85
|
+
cacheRead: 0,
|
|
86
|
+
cacheWrite: 0,
|
|
87
|
+
total: 0,
|
|
88
|
+
};
|
|
89
|
+
let sessionDollarCost = 0;
|
|
90
|
+
let latestProvider = plan.provider;
|
|
91
|
+
let latestModel = "";
|
|
92
|
+
for (const t of turns) {
|
|
93
|
+
sessionTokens.input += t.tokens.input;
|
|
94
|
+
sessionTokens.output += t.tokens.output;
|
|
95
|
+
sessionTokens.cacheRead += t.tokens.cacheRead;
|
|
96
|
+
sessionTokens.cacheWrite += t.tokens.cacheWrite;
|
|
97
|
+
sessionDollarCost += t.dollarCost;
|
|
98
|
+
latestProvider = t.provider || latestProvider;
|
|
99
|
+
latestModel = t.model || latestModel;
|
|
100
|
+
}
|
|
101
|
+
sessionTokens.total =
|
|
102
|
+
sessionTokens.input + sessionTokens.output + sessionTokens.cacheRead + sessionTokens.cacheWrite;
|
|
103
|
+
const now = Date.now();
|
|
104
|
+
const shortSnap = plan.short ? computeWindow(turns, plan.short, now) : undefined;
|
|
105
|
+
const longSnap = plan.long ? computeWindow(turns, plan.long, now) : undefined;
|
|
106
|
+
const messagesShort = plan.short
|
|
107
|
+
? turns.filter((t) => t.timestamp >= now - plan.short.durationMs).length
|
|
108
|
+
: turns.length;
|
|
109
|
+
const messagesLong = plan.long
|
|
110
|
+
? turns.filter((t) => t.timestamp >= now - plan.long.durationMs).length
|
|
111
|
+
: turns.length;
|
|
112
|
+
return {
|
|
113
|
+
agentName,
|
|
114
|
+
provider: latestProvider,
|
|
115
|
+
model: latestModel,
|
|
116
|
+
planId: plan.id,
|
|
117
|
+
planMode: plan.mode,
|
|
118
|
+
sessionTokens,
|
|
119
|
+
sessionDollarCost,
|
|
120
|
+
windows: { short: shortSnap, long: longSnap },
|
|
121
|
+
messagesShort,
|
|
122
|
+
messagesLong,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
snapshotAll() {
|
|
126
|
+
return Array.from(this.turnsByAgent.keys())
|
|
127
|
+
.map((name) => this.snapshot(name))
|
|
128
|
+
.filter((s) => s !== null);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function computeWindow(turns, window, now) {
|
|
132
|
+
const windowStart = now - window.durationMs;
|
|
133
|
+
const inWindow = turns.filter((t) => t.timestamp >= windowStart);
|
|
134
|
+
// Cap is model-specific. Use the most-used model in the window to pick the cap.
|
|
135
|
+
const modelCounts = new Map();
|
|
136
|
+
for (const t of inWindow) {
|
|
137
|
+
modelCounts.set(t.model, (modelCounts.get(t.model) ?? 0) + 1);
|
|
138
|
+
}
|
|
139
|
+
const dominantModel = maxBy(Array.from(modelCounts.entries()), ([, c]) => c)?.[0] ?? "";
|
|
140
|
+
const modelCap = window.capByModel[dominantModel];
|
|
141
|
+
let used;
|
|
142
|
+
if (window.unit === "messages") {
|
|
143
|
+
used = inWindow.length;
|
|
144
|
+
}
|
|
145
|
+
else if (window.unit === "tokens") {
|
|
146
|
+
used = inWindow.reduce((sum, t) => sum + t.tokens.total, 0);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
// hours — approximate by assuming each assistant turn represents some
|
|
150
|
+
// fraction of an hour. Without true session-duration data, we use
|
|
151
|
+
// total output tokens as a proxy, calibrated at 2000 output tokens
|
|
152
|
+
// ≈ 1 minute of "active use" (rough but honest).
|
|
153
|
+
const outputTokens = inWindow.reduce((sum, t) => sum + t.tokens.output, 0);
|
|
154
|
+
used = outputTokens / 2000 / 60; // hours
|
|
155
|
+
}
|
|
156
|
+
// Earliest turn in window tells us when the oldest will roll out.
|
|
157
|
+
// inWindow is filtered in iteration order — not guaranteed to be
|
|
158
|
+
// chronologically sorted if the underlying session JSONL was ever
|
|
159
|
+
// re-ordered or merged. Scan explicitly.
|
|
160
|
+
let oldest = now;
|
|
161
|
+
if (inWindow.length > 0) {
|
|
162
|
+
oldest = inWindow[0].timestamp;
|
|
163
|
+
for (let i = 1; i < inWindow.length; i++) {
|
|
164
|
+
if (inWindow[i].timestamp < oldest)
|
|
165
|
+
oldest = inWindow[i].timestamp;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const nextResetAt = oldest + window.durationMs;
|
|
169
|
+
return {
|
|
170
|
+
durationMs: window.durationMs,
|
|
171
|
+
used,
|
|
172
|
+
cap: modelCap?.min ?? 0,
|
|
173
|
+
capMax: modelCap?.max,
|
|
174
|
+
unit: window.unit,
|
|
175
|
+
windowStart,
|
|
176
|
+
nextResetAt,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function parseTimestamp(raw) {
|
|
180
|
+
if (typeof raw === "number")
|
|
181
|
+
return raw;
|
|
182
|
+
if (typeof raw === "string") {
|
|
183
|
+
const t = Date.parse(raw);
|
|
184
|
+
return Number.isNaN(t) ? null : t;
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
function maxBy(items, key) {
|
|
189
|
+
let best;
|
|
190
|
+
let bestKey = -Infinity;
|
|
191
|
+
for (const item of items) {
|
|
192
|
+
const k = key(item);
|
|
193
|
+
if (k > bestKey) {
|
|
194
|
+
bestKey = k;
|
|
195
|
+
best = item;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return best;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/usage/tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage tracking types.
|
|
3
|
+
*
|
|
4
|
+
* Two things we care about:
|
|
5
|
+
*
|
|
6
|
+
* 1. TurnUsage — what a single agent turn cost, sourced from pi's session
|
|
7
|
+
* JSONL (input/output/cache tokens + dollar cost + timestamp + model).
|
|
8
|
+
*
|
|
9
|
+
* 2. WindowUsage — aggregation of turns into rolling time windows, compared
|
|
10
|
+
* against the agent's declared subscription plan. This is the thing users
|
|
11
|
+
* actually care about on subscriptions: "how much of my 5-hour budget am
|
|
12
|
+
* I burning through right now?"
|
|
13
|
+
*
|
|
14
|
+
* Dollar cost is always computed (from pi's pricing) but is MEANINGLESS for
|
|
15
|
+
* subscription users. The plan-aware view is the primary display.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/usage/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pi-team container entrypoint.
|
|
3
|
+
#
|
|
4
|
+
# Reads configuration from env vars and launches a tmux session running the
|
|
5
|
+
# orchestrator, plus ttyd for web observation. ttyd runs in the foreground so
|
|
6
|
+
# the container stays alive as long as the debate does.
|
|
7
|
+
#
|
|
8
|
+
# Required env:
|
|
9
|
+
# PITEAM_NAME session name (e.g. "emotions")
|
|
10
|
+
# PITEAM_AGENTS comma-separated agent specs: "name:provider/model[:thinking],..."
|
|
11
|
+
# PITEAM_TOPIC inline topic string, OR
|
|
12
|
+
# PITEAM_TOPIC_FILE path to a topic file
|
|
13
|
+
#
|
|
14
|
+
# Optional env:
|
|
15
|
+
# PITEAM_MAX_TURNS default 20
|
|
16
|
+
# PITEAM_HTTP_PORT default 7682
|
|
17
|
+
# PITEAM_TTYD_PORT default 7681
|
|
18
|
+
# PITEAM_SESSIONS_DIR default /data/sessions
|
|
19
|
+
# PITEAM_HTTP_TOKEN if set, require Bearer token for HTTP inject
|
|
20
|
+
# PITEAM_TTYD_CREDS user:password for ttyd basic auth (recommended for hosted)
|
|
21
|
+
# PITEAM_TTYD_ARGS extra args passed to ttyd
|
|
22
|
+
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
|
|
25
|
+
: "${PITEAM_NAME:?PITEAM_NAME is required}"
|
|
26
|
+
: "${PITEAM_AGENTS:?PITEAM_AGENTS is required}"
|
|
27
|
+
|
|
28
|
+
MAX_TURNS="${PITEAM_MAX_TURNS:-20}"
|
|
29
|
+
HTTP_PORT="${PITEAM_HTTP_PORT:-7682}"
|
|
30
|
+
TTYD_PORT="${PITEAM_TTYD_PORT:-7681}"
|
|
31
|
+
SESSIONS_DIR="${PITEAM_SESSIONS_DIR:-/data/sessions}"
|
|
32
|
+
|
|
33
|
+
mkdir -p "$SESSIONS_DIR"
|
|
34
|
+
|
|
35
|
+
# Resolve topic: prefer file, fallback to inline
|
|
36
|
+
TOPIC_ARGS=()
|
|
37
|
+
if [[ -n "${PITEAM_TOPIC_FILE:-}" ]]; then
|
|
38
|
+
TOPIC_ARGS=(--topic-file "$PITEAM_TOPIC_FILE")
|
|
39
|
+
elif [[ -n "${PITEAM_TOPIC:-}" ]]; then
|
|
40
|
+
TOPIC_ARGS=(--topic "$PITEAM_TOPIC")
|
|
41
|
+
else
|
|
42
|
+
echo "error: set PITEAM_TOPIC or PITEAM_TOPIC_FILE" >&2
|
|
43
|
+
exit 2
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Split comma-separated agent specs into repeated --agent args
|
|
47
|
+
AGENT_ARGS=()
|
|
48
|
+
IFS=',' read -ra SPECS <<< "$PITEAM_AGENTS"
|
|
49
|
+
for spec in "${SPECS[@]}"; do
|
|
50
|
+
# Trim whitespace
|
|
51
|
+
spec="${spec#"${spec%%[![:space:]]*}"}"
|
|
52
|
+
spec="${spec%"${spec##*[![:space:]]}"}"
|
|
53
|
+
[[ -z "$spec" ]] && continue
|
|
54
|
+
AGENT_ARGS+=(--agent "$spec")
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
# Start the tmux session in the background
|
|
58
|
+
pi-team start \
|
|
59
|
+
--name "$PITEAM_NAME" \
|
|
60
|
+
"${AGENT_ARGS[@]}" \
|
|
61
|
+
"${TOPIC_ARGS[@]}" \
|
|
62
|
+
--max-turns "$MAX_TURNS" \
|
|
63
|
+
--sessions-dir "$SESSIONS_DIR" \
|
|
64
|
+
--http-port "$HTTP_PORT"
|
|
65
|
+
|
|
66
|
+
# Wait for tmux session to be created (pi-team exits after detaching)
|
|
67
|
+
for i in 1 2 3 4 5; do
|
|
68
|
+
if tmux has-session -t "piteam-${PITEAM_NAME}" 2>/dev/null; then
|
|
69
|
+
break
|
|
70
|
+
fi
|
|
71
|
+
sleep 1
|
|
72
|
+
done
|
|
73
|
+
|
|
74
|
+
echo ""
|
|
75
|
+
echo "==============================================="
|
|
76
|
+
echo " pi-team session \"${PITEAM_NAME}\" is running"
|
|
77
|
+
echo " web observer: http://0.0.0.0:${TTYD_PORT}/"
|
|
78
|
+
echo " http inject: POST http://0.0.0.0:${HTTP_PORT}/inject"
|
|
79
|
+
echo " status: GET http://0.0.0.0:${HTTP_PORT}/state"
|
|
80
|
+
echo "==============================================="
|
|
81
|
+
echo ""
|
|
82
|
+
|
|
83
|
+
# Launch ttyd in the foreground pointing at the tmux session
|
|
84
|
+
TTYD_CMD=(ttyd -p "$TTYD_PORT" -W)
|
|
85
|
+
if [[ -n "${PITEAM_TTYD_CREDS:-}" ]]; then
|
|
86
|
+
TTYD_CMD+=(-c "$PITEAM_TTYD_CREDS")
|
|
87
|
+
fi
|
|
88
|
+
if [[ -n "${PITEAM_TTYD_ARGS:-}" ]]; then
|
|
89
|
+
# shellcheck disable=SC2206
|
|
90
|
+
TTYD_CMD+=($PITEAM_TTYD_ARGS)
|
|
91
|
+
fi
|
|
92
|
+
TTYD_CMD+=(tmux attach -t "piteam-${PITEAM_NAME}")
|
|
93
|
+
|
|
94
|
+
exec "${TTYD_CMD[@]}"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
services:
|
|
2
|
+
pi-team:
|
|
3
|
+
build: .
|
|
4
|
+
image: pi-team:local
|
|
5
|
+
container_name: pi-team
|
|
6
|
+
environment:
|
|
7
|
+
# Agent configuration
|
|
8
|
+
PITEAM_NAME: ${PITEAM_NAME:-debate}
|
|
9
|
+
PITEAM_AGENTS: ${PITEAM_AGENTS:-claude:anthropic/claude-sonnet-4-5,codex:openai-codex/gpt-5.4:xhigh}
|
|
10
|
+
PITEAM_TOPIC_FILE: /topics/topic.md
|
|
11
|
+
PITEAM_MAX_TURNS: ${PITEAM_MAX_TURNS:-20}
|
|
12
|
+
# Optional auth for hosted deployments
|
|
13
|
+
PITEAM_TTYD_CREDS: ${PITEAM_TTYD_CREDS:-}
|
|
14
|
+
PITEAM_HTTP_TOKEN: ${PITEAM_HTTP_TOKEN:-}
|
|
15
|
+
# LLM API keys (pass through from .env)
|
|
16
|
+
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
|
|
17
|
+
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
|
18
|
+
GOOGLE_API_KEY: ${GOOGLE_API_KEY:-}
|
|
19
|
+
ports:
|
|
20
|
+
- "${PITEAM_TTYD_PORT:-7681}:7681" # web tmux observer
|
|
21
|
+
- "${PITEAM_HTTP_PORT:-7682}:7682" # HTTP inject endpoint
|
|
22
|
+
volumes:
|
|
23
|
+
- ./topics:/topics:ro # mount your topic files here
|
|
24
|
+
- piteam-sessions:/data # persist session transcripts
|
|
25
|
+
restart: unless-stopped
|
|
26
|
+
|
|
27
|
+
volumes:
|
|
28
|
+
piteam-sessions:
|