@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.
@@ -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: