@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
package/dist/observer.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observer: consumes Orchestra events and renders them live to stdout.
|
|
3
|
+
*
|
|
4
|
+
* Colors per agent + dim timestamps + indent wrap. Designed to look decent
|
|
5
|
+
* inside a tmux pane. Also writes plain-text events to a log file for
|
|
6
|
+
* offline review (tmux scrollback is nice, files are persistent).
|
|
7
|
+
*/
|
|
8
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { dirname } from "node:path";
|
|
10
|
+
import { formatSnapshot } from "./usage/reporter.js";
|
|
11
|
+
const AGENT_COLORS = [
|
|
12
|
+
"\x1b[38;5;39m", // cyan-blue
|
|
13
|
+
"\x1b[38;5;208m", // orange
|
|
14
|
+
"\x1b[38;5;141m", // purple
|
|
15
|
+
"\x1b[38;5;42m", // green
|
|
16
|
+
"\x1b[38;5;220m", // yellow
|
|
17
|
+
"\x1b[38;5;198m", // pink
|
|
18
|
+
];
|
|
19
|
+
const HUMAN_COLOR = "\x1b[38;5;15m\x1b[1m"; // bold white
|
|
20
|
+
const DIM = "\x1b[2m";
|
|
21
|
+
const RESET = "\x1b[0m";
|
|
22
|
+
const BOLD = "\x1b[1m";
|
|
23
|
+
function timestamp() {
|
|
24
|
+
return new Date().toISOString().slice(11, 19);
|
|
25
|
+
}
|
|
26
|
+
export class Observer {
|
|
27
|
+
logPath;
|
|
28
|
+
colorByAgent = new Map();
|
|
29
|
+
streamingAgent = null;
|
|
30
|
+
constructor(orchestra, logPath) {
|
|
31
|
+
this.logPath = logPath;
|
|
32
|
+
if (this.logPath) {
|
|
33
|
+
mkdirSync(dirname(this.logPath), { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
// Assign colors stably per agent based on config order
|
|
36
|
+
for (let i = 0; i < orchestra.config.agents.length; i++) {
|
|
37
|
+
const name = orchestra.config.agents[i].name;
|
|
38
|
+
this.colorByAgent.set(name, AGENT_COLORS[i % AGENT_COLORS.length]);
|
|
39
|
+
}
|
|
40
|
+
orchestra.on("event", (e) => this.onEvent(e));
|
|
41
|
+
orchestra.on("delta", (agentName, text) => this.onDelta(agentName, text));
|
|
42
|
+
orchestra.on("tool", (agentName, toolName, phase) => this.onTool(agentName, toolName, phase));
|
|
43
|
+
orchestra.on("usage", (snap) => this.onUsage(snap));
|
|
44
|
+
}
|
|
45
|
+
onUsage(snap) {
|
|
46
|
+
const line = formatSnapshot(snap, { color: true });
|
|
47
|
+
process.stdout.write(`${DIM} ⤷ ${RESET}${line}\n`);
|
|
48
|
+
this.writeLog(`usage: ${formatSnapshot(snap, { color: false })}`);
|
|
49
|
+
}
|
|
50
|
+
colorFor(from) {
|
|
51
|
+
if (from === "human")
|
|
52
|
+
return HUMAN_COLOR;
|
|
53
|
+
return this.colorByAgent.get(from) ?? "";
|
|
54
|
+
}
|
|
55
|
+
writeLog(line) {
|
|
56
|
+
if (!this.logPath)
|
|
57
|
+
return;
|
|
58
|
+
// Strip ANSI for log
|
|
59
|
+
const plain = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
60
|
+
try {
|
|
61
|
+
appendFileSync(this.logPath, `${plain}\n`);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
/* ignore */
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
onEvent(e) {
|
|
68
|
+
switch (e.type) {
|
|
69
|
+
case "message": {
|
|
70
|
+
const m = e.message;
|
|
71
|
+
const color = this.colorFor(m.from);
|
|
72
|
+
const header = `${DIM}[${timestamp()}]${RESET} ${color}${BOLD}${m.from}${RESET}${color} ›${RESET}`;
|
|
73
|
+
// For agent messages where we just streamed deltas, content is already
|
|
74
|
+
// printed via onDelta. Only print full body for injections and seed topic.
|
|
75
|
+
if (m.from === "human" || m.role === "user") {
|
|
76
|
+
process.stdout.write(`\n${header} ${color}${m.content}${RESET}\n\n`);
|
|
77
|
+
this.writeLog(`[${timestamp()}] ${m.from}: ${m.content}`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// finalize the streaming line
|
|
81
|
+
if (this.streamingAgent === m.from) {
|
|
82
|
+
process.stdout.write(`${RESET}\n`);
|
|
83
|
+
this.streamingAgent = null;
|
|
84
|
+
}
|
|
85
|
+
// Full message log entry
|
|
86
|
+
this.writeLog(`[${timestamp()}] ${m.from}: ${m.content}`);
|
|
87
|
+
}
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
case "turn_start": {
|
|
91
|
+
const color = this.colorFor(e.agent);
|
|
92
|
+
process.stdout.write(`\n${DIM}[${timestamp()}]${RESET} ${color}${BOLD}${e.agent}${RESET}${color} ›${RESET} `);
|
|
93
|
+
this.streamingAgent = e.agent;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
case "turn_end": {
|
|
97
|
+
if (this.streamingAgent === e.agent) {
|
|
98
|
+
process.stdout.write(`${RESET}\n`);
|
|
99
|
+
this.streamingAgent = null;
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
case "agent_error": {
|
|
104
|
+
process.stdout.write(`\n\x1b[31m[error]\x1b[0m ${e.agent}: ${e.error}\n`);
|
|
105
|
+
this.writeLog(`[error] ${e.agent}: ${e.error}`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
case "human_inject": {
|
|
109
|
+
const m = e.message;
|
|
110
|
+
process.stdout.write(`\n${HUMAN_COLOR}↘ human (injected)${RESET} ${m.content}${RESET}\n\n`);
|
|
111
|
+
this.writeLog(`[${timestamp()}] [INJECT] human: ${m.content}`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
case "info": {
|
|
115
|
+
process.stdout.write(`${DIM}· ${e.info}${RESET}\n`);
|
|
116
|
+
this.writeLog(`[${timestamp()}] · ${e.info}`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
case "stop": {
|
|
120
|
+
process.stdout.write(`\n${DIM}· session stopped${RESET}\n`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
onDelta(agentName, text) {
|
|
126
|
+
if (this.streamingAgent !== agentName) {
|
|
127
|
+
// Different agent started streaming (shouldn't happen in round-robin but safe)
|
|
128
|
+
if (this.streamingAgent) {
|
|
129
|
+
process.stdout.write(`${RESET}\n`);
|
|
130
|
+
}
|
|
131
|
+
const color = this.colorFor(agentName);
|
|
132
|
+
process.stdout.write(`\n${DIM}[${timestamp()}]${RESET} ${color}${BOLD}${agentName}${RESET}${color} ›${RESET} `);
|
|
133
|
+
this.streamingAgent = agentName;
|
|
134
|
+
}
|
|
135
|
+
const color = this.colorFor(agentName);
|
|
136
|
+
// Indent continuation lines so attribution stays visible
|
|
137
|
+
const indented = text.replace(/\n/g, `\n${color}│${RESET} `);
|
|
138
|
+
process.stdout.write(`${color}${indented}${RESET}`);
|
|
139
|
+
}
|
|
140
|
+
onTool(agentName, toolName, phase) {
|
|
141
|
+
if (phase === "start") {
|
|
142
|
+
const color = this.colorFor(agentName);
|
|
143
|
+
process.stdout.write(`\n${color} ⚙ ${toolName}${RESET}`);
|
|
144
|
+
this.writeLog(`[${timestamp()}] ${agentName} tool_start: ${toolName}`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
this.writeLog(`[${timestamp()}] ${agentName} tool_end: ${toolName}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=observer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observer.js","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestra: the central coordinator for a multi-agent team session.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Spawn one Agent per AgentSpec
|
|
6
|
+
* - Route messages between them via the shared Transcript
|
|
7
|
+
* - Accept human injections at turn boundaries
|
|
8
|
+
* - Stream events to observers (stdout, log, etc.)
|
|
9
|
+
* - Handle graceful shutdown
|
|
10
|
+
*/
|
|
11
|
+
import { EventEmitter } from "node:events";
|
|
12
|
+
import { mkdirSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { Agent } from "./agent.js";
|
|
15
|
+
import { Transcript } from "./transcript.js";
|
|
16
|
+
import { UsageTracker } from "./usage/tracker.js";
|
|
17
|
+
export class Orchestra extends EventEmitter {
|
|
18
|
+
config;
|
|
19
|
+
transcript;
|
|
20
|
+
agents = [];
|
|
21
|
+
usage;
|
|
22
|
+
agentLastSeen = new Map();
|
|
23
|
+
agentSessionPaths = new Map();
|
|
24
|
+
pendingInjections = [];
|
|
25
|
+
running = false;
|
|
26
|
+
turn = 0;
|
|
27
|
+
sessionDir;
|
|
28
|
+
constructor(config) {
|
|
29
|
+
super();
|
|
30
|
+
this.config = config;
|
|
31
|
+
this.sessionDir = join(config.sessionsDir, config.name);
|
|
32
|
+
mkdirSync(this.sessionDir, { recursive: true });
|
|
33
|
+
this.transcript = new Transcript(join(this.sessionDir, "transcript.jsonl"));
|
|
34
|
+
this.usage = new UsageTracker();
|
|
35
|
+
}
|
|
36
|
+
async start() {
|
|
37
|
+
if (this.running)
|
|
38
|
+
return;
|
|
39
|
+
this.running = true;
|
|
40
|
+
this.emitEvent({ type: "info", timestamp: Date.now(), info: `starting session "${this.config.name}"` });
|
|
41
|
+
// Spawn agents in parallel
|
|
42
|
+
for (const spec of this.config.agents) {
|
|
43
|
+
const agent = this.createAgent(spec);
|
|
44
|
+
this.agents.push(agent);
|
|
45
|
+
this.agentLastSeen.set(spec.name, 0);
|
|
46
|
+
}
|
|
47
|
+
await Promise.all(this.agents.map((a) => a.start()));
|
|
48
|
+
this.emitEvent({
|
|
49
|
+
type: "info",
|
|
50
|
+
timestamp: Date.now(),
|
|
51
|
+
info: `agents ready: ${this.agents.map((a) => a.name).join(", ")}`,
|
|
52
|
+
});
|
|
53
|
+
// Seed the transcript with the topic
|
|
54
|
+
const seeded = this.transcript.append({
|
|
55
|
+
from: "human",
|
|
56
|
+
role: "user",
|
|
57
|
+
content: this.config.topic,
|
|
58
|
+
});
|
|
59
|
+
this.emitEvent({ type: "message", timestamp: seeded.timestamp, message: seeded });
|
|
60
|
+
// Main loop
|
|
61
|
+
try {
|
|
62
|
+
await this.runLoop();
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
this.running = false;
|
|
66
|
+
this.emitEvent({ type: "stop", timestamp: Date.now() });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
createAgent(spec) {
|
|
70
|
+
const sessionPath = join(this.sessionDir, `agent-${spec.name}.session.jsonl`);
|
|
71
|
+
this.agentSessionPaths.set(spec.name, sessionPath);
|
|
72
|
+
this.usage.register({
|
|
73
|
+
name: spec.name,
|
|
74
|
+
provider: spec.provider,
|
|
75
|
+
model: spec.model,
|
|
76
|
+
sessionPath,
|
|
77
|
+
planId: spec.planId ?? "api",
|
|
78
|
+
});
|
|
79
|
+
const agent = new Agent({ spec, sessionPath });
|
|
80
|
+
agent.on("delta", (name, text) => {
|
|
81
|
+
this.emit("delta", name, text);
|
|
82
|
+
});
|
|
83
|
+
agent.on("tool_start", (name, tool) => {
|
|
84
|
+
this.emit("tool", name, tool, "start");
|
|
85
|
+
});
|
|
86
|
+
agent.on("tool_end", (name, tool) => {
|
|
87
|
+
this.emit("tool", name, tool, "end");
|
|
88
|
+
});
|
|
89
|
+
agent.on("exit", (name, code) => {
|
|
90
|
+
this.emitEvent({
|
|
91
|
+
type: "agent_error",
|
|
92
|
+
timestamp: Date.now(),
|
|
93
|
+
agent: name,
|
|
94
|
+
error: `pi process exited with code ${code}`,
|
|
95
|
+
});
|
|
96
|
+
this.running = false;
|
|
97
|
+
});
|
|
98
|
+
return agent;
|
|
99
|
+
}
|
|
100
|
+
async runLoop() {
|
|
101
|
+
while (this.running) {
|
|
102
|
+
if (this.config.maxTurns > 0 && this.turn >= this.config.maxTurns) {
|
|
103
|
+
this.emitEvent({
|
|
104
|
+
type: "info",
|
|
105
|
+
timestamp: Date.now(),
|
|
106
|
+
info: `reached max turns (${this.config.maxTurns})`,
|
|
107
|
+
});
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
// Drain any pending human injections into the transcript BEFORE this turn,
|
|
111
|
+
// so the active agent sees them.
|
|
112
|
+
this.drainInjections();
|
|
113
|
+
const agentIdx = this.turn % this.agents.length;
|
|
114
|
+
const agent = this.agents[agentIdx];
|
|
115
|
+
const lastSeen = this.agentLastSeen.get(agent.name) ?? 0;
|
|
116
|
+
const promptText = this.transcript.renderForAgent(agent.name, lastSeen);
|
|
117
|
+
if (!promptText.trim()) {
|
|
118
|
+
// Nothing new for this agent — skip its turn to avoid prompting with empty input
|
|
119
|
+
this.turn++;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
this.emitEvent({ type: "turn_start", timestamp: Date.now(), agent: agent.name });
|
|
123
|
+
let replyText;
|
|
124
|
+
try {
|
|
125
|
+
replyText = await agent.prompt(promptText);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
this.emitEvent({
|
|
129
|
+
type: "agent_error",
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
agent: agent.name,
|
|
132
|
+
error: err.message,
|
|
133
|
+
});
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
this.agentLastSeen.set(agent.name, this.transcript.lastSeq());
|
|
137
|
+
const msg = this.transcript.append({
|
|
138
|
+
from: agent.name,
|
|
139
|
+
role: "assistant",
|
|
140
|
+
content: replyText,
|
|
141
|
+
});
|
|
142
|
+
this.emitEvent({ type: "message", timestamp: msg.timestamp, message: msg });
|
|
143
|
+
// Refresh usage snapshot for this agent and emit it
|
|
144
|
+
const sessionPath = this.agentSessionPaths.get(agent.name);
|
|
145
|
+
if (sessionPath) {
|
|
146
|
+
this.usage.syncAgent(agent.name, sessionPath);
|
|
147
|
+
const snap = this.usage.snapshot(agent.name);
|
|
148
|
+
if (snap)
|
|
149
|
+
this.emit("usage", snap);
|
|
150
|
+
}
|
|
151
|
+
this.emitEvent({ type: "turn_end", timestamp: Date.now(), agent: agent.name });
|
|
152
|
+
this.turn++;
|
|
153
|
+
if (this.config.turnDelayMs > 0) {
|
|
154
|
+
await new Promise((r) => setTimeout(r, this.config.turnDelayMs));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
drainInjections() {
|
|
159
|
+
while (this.pendingInjections.length > 0) {
|
|
160
|
+
const content = this.pendingInjections.shift();
|
|
161
|
+
const msg = this.transcript.append({ from: "human", role: "user", content });
|
|
162
|
+
this.emitEvent({ type: "human_inject", timestamp: msg.timestamp, message: msg });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Queue a human message. Will be delivered to the next agent at the next
|
|
167
|
+
* turn boundary. Every subsequent agent will see it because renderForAgent
|
|
168
|
+
* includes all messages since their last turn.
|
|
169
|
+
*/
|
|
170
|
+
inject(content) {
|
|
171
|
+
this.pendingInjections.push(content);
|
|
172
|
+
// Don't emit an info event here — it would interleave with a streaming agent's
|
|
173
|
+
// output. The injection will appear as a human_inject event at the next turn boundary.
|
|
174
|
+
}
|
|
175
|
+
async stop() {
|
|
176
|
+
this.running = false;
|
|
177
|
+
await Promise.all(this.agents.map((a) => a.abort()));
|
|
178
|
+
for (const a of this.agents)
|
|
179
|
+
a.stop();
|
|
180
|
+
}
|
|
181
|
+
emitEvent(event) {
|
|
182
|
+
this.emit("event", event);
|
|
183
|
+
}
|
|
184
|
+
lastMessages(n) {
|
|
185
|
+
const all = this.transcript.all();
|
|
186
|
+
return all.slice(Math.max(0, all.length - n));
|
|
187
|
+
}
|
|
188
|
+
usageSnapshots() {
|
|
189
|
+
return this.usage.snapshotAll();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=orchestra.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestra.js","sourceRoot":"","sources":["../src/orchestra.ts"],"names":[],"mappings":"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"}
|
package/dist/tmux.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tmux UI helper: creates a 3-pane layout for observing a team session.
|
|
3
|
+
*
|
|
4
|
+
* Layout:
|
|
5
|
+
* ┌──────────────────────────────────────────────┐
|
|
6
|
+
* │ │
|
|
7
|
+
* │ pane 0: orchestrator stream (main view) │
|
|
8
|
+
* │ ─ colored agent messages + deltas │
|
|
9
|
+
* │ │
|
|
10
|
+
* ├─────────────────────────┬────────────────────┤
|
|
11
|
+
* │ pane 1: inject prompt │ pane 2: meta/tail │
|
|
12
|
+
* │ (user types here) │ (log tail) │
|
|
13
|
+
* └─────────────────────────┴────────────────────┘
|
|
14
|
+
*
|
|
15
|
+
* Users attach with `tmux attach -t <name>` and detach with Ctrl+b d.
|
|
16
|
+
*/
|
|
17
|
+
import { execSync, spawnSync } from "node:child_process";
|
|
18
|
+
export function tmuxInstalled() {
|
|
19
|
+
const r = spawnSync("tmux", ["-V"], { stdio: "ignore" });
|
|
20
|
+
return r.status === 0;
|
|
21
|
+
}
|
|
22
|
+
export function sessionExists(name) {
|
|
23
|
+
const r = spawnSync("tmux", ["has-session", "-t", name], { stdio: "ignore" });
|
|
24
|
+
return r.status === 0;
|
|
25
|
+
}
|
|
26
|
+
export function killSession(name) {
|
|
27
|
+
if (sessionExists(name)) {
|
|
28
|
+
spawnSync("tmux", ["kill-session", "-t", name], { stdio: "ignore" });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create a new detached tmux session running the orchestrator in pane 0,
|
|
33
|
+
* the inject client in pane 1, and a log tail in pane 2.
|
|
34
|
+
*/
|
|
35
|
+
export function createLayout(opts) {
|
|
36
|
+
if (sessionExists(opts.sessionName)) {
|
|
37
|
+
throw new Error(`tmux session "${opts.sessionName}" already exists`);
|
|
38
|
+
}
|
|
39
|
+
// Large default size so wrap looks OK even if first attach is small
|
|
40
|
+
execSync(`tmux new-session -d -s ${shell(opts.sessionName)} -x 220 -y 55 ${shell(opts.orchestratorCmd)}`, { stdio: "inherit" });
|
|
41
|
+
// Split horizontally (bottom pane)
|
|
42
|
+
execSync(`tmux split-window -v -p 25 -t ${shell(opts.sessionName)} ${shell(opts.injectCmd)}`, {
|
|
43
|
+
stdio: "inherit",
|
|
44
|
+
});
|
|
45
|
+
// Split the bottom pane vertically for a log tail
|
|
46
|
+
execSync(`tmux split-window -h -p 50 -t ${shell(opts.sessionName)} ${shell(`tail -f ${opts.logPath}`)}`, { stdio: "inherit" });
|
|
47
|
+
// Return focus to main orchestrator pane
|
|
48
|
+
execSync(`tmux select-pane -t ${shell(`${opts.sessionName}.0`)}`, { stdio: "inherit" });
|
|
49
|
+
}
|
|
50
|
+
function shell(s) {
|
|
51
|
+
// POSIX single-quote escape
|
|
52
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=tmux.js.map
|
package/dist/tmux.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux.js","sourceRoot":"","sources":["../src/tmux.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,UAAU,aAAa;IAC5B,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACzD,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC9E,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACvC,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,SAAS,CAAC,MAAM,EAAE,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACtE,CAAC;AACF,CAAC;AASD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAuB;IACnD,IAAI,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,CAAC,WAAW,kBAAkB,CAAC,CAAC;IACtE,CAAC;IACD,oEAAoE;IACpE,QAAQ,CACP,0BAA0B,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,iBAAiB,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,EAC/F,EAAE,KAAK,EAAE,SAAS,EAAE,CACpB,CAAC;IACF,mCAAmC;IACnC,QAAQ,CAAC,iCAAiC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE;QAC7F,KAAK,EAAE,SAAS;KAChB,CAAC,CAAC;IACH,kDAAkD;IAClD,QAAQ,CACP,iCAAiC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAC9F,EAAE,KAAK,EAAE,SAAS,EAAE,CACpB,CAAC;IACF,yCAAyC;IACzC,QAAQ,CAAC,uBAAuB,KAAK,CAAC,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;AACzF,CAAC;AAED,SAAS,KAAK,CAAC,CAAS;IACvB,4BAA4B;IAC5B,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared transcript for a team session.
|
|
3
|
+
*
|
|
4
|
+
* Stores every message (human injection, agent reply, system event) in
|
|
5
|
+
* memory and appends to a JSONL file on disk for crash recovery.
|
|
6
|
+
*/
|
|
7
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
8
|
+
import { dirname } from "node:path";
|
|
9
|
+
export class Transcript {
|
|
10
|
+
path;
|
|
11
|
+
messages = [];
|
|
12
|
+
nextSeq = 1;
|
|
13
|
+
constructor(path) {
|
|
14
|
+
this.path = path;
|
|
15
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
16
|
+
if (existsSync(path)) {
|
|
17
|
+
const raw = readFileSync(path, "utf8");
|
|
18
|
+
for (const line of raw.split("\n")) {
|
|
19
|
+
if (!line.trim())
|
|
20
|
+
continue;
|
|
21
|
+
try {
|
|
22
|
+
const msg = JSON.parse(line);
|
|
23
|
+
this.messages.push(msg);
|
|
24
|
+
if (msg.seq >= this.nextSeq)
|
|
25
|
+
this.nextSeq = msg.seq + 1;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
/* skip malformed */
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
append(msg) {
|
|
34
|
+
const full = {
|
|
35
|
+
seq: this.nextSeq++,
|
|
36
|
+
timestamp: Date.now(),
|
|
37
|
+
...msg,
|
|
38
|
+
};
|
|
39
|
+
this.messages.push(full);
|
|
40
|
+
appendFileSync(this.path, `${JSON.stringify(full)}\n`);
|
|
41
|
+
return full;
|
|
42
|
+
}
|
|
43
|
+
all() {
|
|
44
|
+
return this.messages;
|
|
45
|
+
}
|
|
46
|
+
/** Messages since (exclusive) the given seq. If seq is 0, returns all. */
|
|
47
|
+
since(seq) {
|
|
48
|
+
return this.messages.filter((m) => m.seq > seq);
|
|
49
|
+
}
|
|
50
|
+
lastSeq() {
|
|
51
|
+
return this.messages.length > 0 ? this.messages[this.messages.length - 1].seq : 0;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Render messages for a specific agent's perspective: its own messages are
|
|
55
|
+
* already in its pi session, so we only send messages from others since its
|
|
56
|
+
* last turn, formatted with attribution.
|
|
57
|
+
*/
|
|
58
|
+
renderForAgent(agentName, sinceSeq) {
|
|
59
|
+
const others = this.since(sinceSeq).filter((m) => m.from !== agentName);
|
|
60
|
+
if (others.length === 0)
|
|
61
|
+
return "";
|
|
62
|
+
return others
|
|
63
|
+
.map((m) => {
|
|
64
|
+
if (m.from === "human")
|
|
65
|
+
return `[human]: ${m.content}`;
|
|
66
|
+
return `[${m.from}]: ${m.content}`;
|
|
67
|
+
})
|
|
68
|
+
.join("\n\n---\n\n");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=transcript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for pi-team.
|
|
3
|
+
*
|
|
4
|
+
* The orchestrator maintains a single shared transcript. Each message has a
|
|
5
|
+
* `from` field identifying the speaker (agent name or "human"). Each agent has
|
|
6
|
+
* its own internal pi session; the orchestrator routes messages between them.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|