pi-extended-teams 2.1.16
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/README.md +105 -0
- package/assets/pi-extended-teams-agent-navigation.png +0 -0
- package/assets/pi-extended-teams-in-action.png +0 -0
- package/extensions/agents/read-agent-report.ts +181 -0
- package/extensions/agents/read-agent-session-lifecycle.test.ts +605 -0
- package/extensions/agents/read-agent-session-lifecycle.ts +676 -0
- package/extensions/agents/read-agent.test.ts +3077 -0
- package/extensions/agents/read-agent.ts +1119 -0
- package/extensions/agents/write-agent.test.ts +513 -0
- package/extensions/agents/write-agent.ts +392 -0
- package/extensions/events/register-events.test.ts +465 -0
- package/extensions/events/register-events.ts +409 -0
- package/extensions/index.test.ts +1659 -0
- package/extensions/index.ts +1228 -0
- package/extensions/internal/agent-session-files.test.ts +164 -0
- package/extensions/internal/agent-session-files.ts +320 -0
- package/extensions/internal/debug.ts +44 -0
- package/extensions/internal/model-selection.ts +82 -0
- package/extensions/internal/pi-command.test.ts +191 -0
- package/extensions/internal/pi-command.ts +224 -0
- package/extensions/internal/pi-runtime-api.test.ts +43 -0
- package/extensions/internal/pi-runtime-api.ts +78 -0
- package/extensions/internal/schema.ts +19 -0
- package/extensions/internal/session-context-reference.test.ts +290 -0
- package/extensions/internal/session-context-reference.ts +430 -0
- package/extensions/internal/session-files.test.ts +225 -0
- package/extensions/internal/session-files.ts +273 -0
- package/extensions/internal/session-usage.ts +59 -0
- package/extensions/resources/spawn-resource-plan.test.ts +233 -0
- package/extensions/resources/spawn-resource-plan.ts +247 -0
- package/extensions/runtime/active-agent-sleep.test.ts +157 -0
- package/extensions/runtime/active-agent-sleep.ts +117 -0
- package/extensions/runtime/nested-read-agents.ts +22 -0
- package/extensions/runtime/pending-child-controller.test.ts +169 -0
- package/extensions/runtime/pending-child-controller.ts +280 -0
- package/extensions/runtime/types.ts +68 -0
- package/extensions/team/contracts.test.ts +111 -0
- package/extensions/team/lifecycle.test.ts +1095 -0
- package/extensions/team/lifecycle.ts +502 -0
- package/extensions/team/recipient-closure.test.ts +97 -0
- package/extensions/team/recipient-closure.ts +120 -0
- package/extensions/team/roster.test.ts +129 -0
- package/extensions/team/roster.ts +165 -0
- package/extensions/team/team-contracts.json +43 -0
- package/extensions/team/writer-screens.test.ts +50 -0
- package/extensions/team/writer-screens.ts +156 -0
- package/extensions/tools/agent-communication-tools.test.ts +306 -0
- package/extensions/tools/agent-communication-tools.ts +183 -0
- package/extensions/tools/coordination-tools.test.ts +670 -0
- package/extensions/tools/coordination-tools.ts +304 -0
- package/extensions/tools/delegation-guard.test.ts +95 -0
- package/extensions/tools/delegation-guard.ts +65 -0
- package/extensions/tools/file-claim-tools.test.ts +104 -0
- package/extensions/tools/file-claim-tools.ts +68 -0
- package/extensions/tools/model-tools.ts +53 -0
- package/extensions/tools/predefined-tools.test.ts +647 -0
- package/extensions/tools/predefined-tools.ts +331 -0
- package/extensions/tools/read-helper.test.ts +35 -0
- package/extensions/tools/task-runtime-tools.test.ts +334 -0
- package/extensions/tools/task-runtime-tools.ts +227 -0
- package/extensions/tools/team-tools.read-agent.test.ts +1924 -0
- package/extensions/tools/team-tools.ts +1270 -0
- package/extensions/ui/agent-follow-view.test.ts +520 -0
- package/extensions/ui/agent-follow-view.ts +779 -0
- package/extensions/ui/agent-navigation.test.ts +52 -0
- package/extensions/ui/agent-navigation.ts +55 -0
- package/extensions/ui/ansi.ts +16 -0
- package/extensions/ui/extensions-command.test.ts +336 -0
- package/extensions/ui/extensions-command.ts +282 -0
- package/extensions/ui/favorite-models-command.test.ts +280 -0
- package/extensions/ui/favorite-models-command.ts +423 -0
- package/extensions/ui/frame.ts +77 -0
- package/extensions/ui/input.ts +17 -0
- package/extensions/ui/read-agent-status.test.ts +117 -0
- package/extensions/ui/read-agent-status.ts +125 -0
- package/extensions/ui/renderers.ts +243 -0
- package/extensions/ui/status-widget.test.ts +143 -0
- package/extensions/ui/status-widget.ts +440 -0
- package/extensions/ui-frame.test.ts +69 -0
- package/package.json +75 -0
- package/skills/teams.md +256 -0
- package/src/adapters/terminal-registry.ts +78 -0
- package/src/adapters/tmux-adapter.test.ts +276 -0
- package/src/adapters/tmux-adapter.ts +200 -0
- package/src/orchestration/index.ts +437 -0
- package/src/orchestration/orchestrator.test.ts +396 -0
- package/src/orchestration/types.ts +125 -0
- package/src/utils/atomic-json.ts +21 -0
- package/src/utils/claims.test.ts +137 -0
- package/src/utils/claims.ts +169 -0
- package/src/utils/hooks.test.ts +75 -0
- package/src/utils/hooks.ts +35 -0
- package/src/utils/lifecycle-tombstone.test.ts +105 -0
- package/src/utils/lifecycle-tombstone.ts +268 -0
- package/src/utils/lock.race.child.ts +44 -0
- package/src/utils/lock.race.test.ts +198 -0
- package/src/utils/lock.test.ts +90 -0
- package/src/utils/lock.ts +186 -0
- package/src/utils/messaging.test.ts +337 -0
- package/src/utils/messaging.ts +441 -0
- package/src/utils/model-resolution.test.ts +231 -0
- package/src/utils/model-resolution.ts +322 -0
- package/src/utils/models.test.ts +8 -0
- package/src/utils/models.ts +115 -0
- package/src/utils/paths.ts +81 -0
- package/src/utils/predefined-teams/types.ts +48 -0
- package/src/utils/predefined-teams.save-template.test.ts +74 -0
- package/src/utils/predefined-teams.test.ts +441 -0
- package/src/utils/predefined-teams.ts +471 -0
- package/src/utils/read-helper-queue.ts +99 -0
- package/src/utils/report-events.test.ts +154 -0
- package/src/utils/report-events.ts +222 -0
- package/src/utils/runtime.test.ts +314 -0
- package/src/utils/runtime.ts +261 -0
- package/src/utils/security.test.ts +43 -0
- package/src/utils/settings.test.ts +480 -0
- package/src/utils/settings.ts +645 -0
- package/src/utils/shared-memory.test.ts +80 -0
- package/src/utils/shared-memory.ts +81 -0
- package/src/utils/tasks.race.test.ts +44 -0
- package/src/utils/tasks.test.ts +229 -0
- package/src/utils/tasks.ts +396 -0
- package/src/utils/teams.ts +231 -0
- package/src/utils/terminal-adapter.ts +103 -0
- package/src/utils/thinking-levels.test.ts +56 -0
- package/src/utils/thinking-levels.ts +47 -0
- package/src/utils/workflow-metadata.test.ts +11 -0
- package/src/utils/workflow-metadata.ts +88 -0
- package/src/utils/write-queue.test.ts +251 -0
- package/src/utils/write-queue.ts +202 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { Member } from "./models";
|
|
5
|
+
import { withLock } from "./lock";
|
|
6
|
+
import { writeQueuePath } from "./paths";
|
|
7
|
+
import { loadSettings, requireFavoriteModelLevel, type FavoriteModelSlot } from "./settings";
|
|
8
|
+
|
|
9
|
+
export interface QueuedWriteSpawn {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
prompt: string;
|
|
13
|
+
cwd: string;
|
|
14
|
+
category?: string;
|
|
15
|
+
modelSlot: FavoriteModelSlot;
|
|
16
|
+
planModeRequired?: boolean;
|
|
17
|
+
color?: string;
|
|
18
|
+
operationId?: string;
|
|
19
|
+
workflowRunId?: string;
|
|
20
|
+
metadata?: Record<string, any>;
|
|
21
|
+
requestedAt: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function ensureQueueDir(teamName: string): string {
|
|
25
|
+
const queuePath = writeQueuePath(teamName);
|
|
26
|
+
const dir = path.dirname(queuePath);
|
|
27
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
28
|
+
return queuePath;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readQueueRaw(queuePath: string): QueuedWriteSpawn[] {
|
|
32
|
+
if (!fs.existsSync(queuePath)) return [];
|
|
33
|
+
const raw = JSON.parse(fs.readFileSync(queuePath, "utf-8"));
|
|
34
|
+
return Array.isArray(raw) ? raw : [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function writeQueueRaw(queuePath: string, queue: QueuedWriteSpawn[]): void {
|
|
38
|
+
const dir = path.dirname(queuePath);
|
|
39
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
40
|
+
const tmpPath = path.join(dir, `.${path.basename(queuePath)}.${process.pid}.${Date.now()}.${crypto.randomUUID()}.tmp`);
|
|
41
|
+
try {
|
|
42
|
+
fs.writeFileSync(tmpPath, JSON.stringify(queue, null, 2));
|
|
43
|
+
fs.renameSync(tmpPath, queuePath);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
try {
|
|
46
|
+
if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
|
|
47
|
+
} catch {
|
|
48
|
+
// Ignore cleanup errors; preserve the original write/rename failure.
|
|
49
|
+
}
|
|
50
|
+
throw e;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function operationIdFor(item: Pick<QueuedWriteSpawn, "operationId" | "metadata">): string | undefined {
|
|
55
|
+
return item.operationId || item.metadata?.operationId;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function workflowRunIdFor(item: Pick<QueuedWriteSpawn, "workflowRunId" | "metadata">): string | undefined {
|
|
59
|
+
return item.workflowRunId || item.metadata?.workflowRunId;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function matchesQueuedIdentity(
|
|
63
|
+
item: QueuedWriteSpawn,
|
|
64
|
+
request: Partial<QueuedWriteSpawn> & Pick<QueuedWriteSpawn, "name">
|
|
65
|
+
): boolean {
|
|
66
|
+
if (request.id && item.id === request.id) return true;
|
|
67
|
+
if (item.name === request.name) return true;
|
|
68
|
+
|
|
69
|
+
const operationId = operationIdFor(request);
|
|
70
|
+
if (!operationId || operationIdFor(item) !== operationId) return false;
|
|
71
|
+
|
|
72
|
+
const workflowRunId = workflowRunIdFor(request);
|
|
73
|
+
return workflowRunId === undefined || workflowRunIdFor(item) === workflowRunId;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function withWriteQueueCapacityLock<T>(teamName: string, fn: () => Promise<T>): Promise<T> {
|
|
77
|
+
const queuePath = ensureQueueDir(teamName);
|
|
78
|
+
return await withLock(`${queuePath}.capacity`, fn);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function listWriteQueue(teamName: string): Promise<QueuedWriteSpawn[]> {
|
|
82
|
+
const queuePath = ensureQueueDir(teamName);
|
|
83
|
+
return await withLock(queuePath, async () => readQueueRaw(queuePath));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function enqueueWriteSpawn(
|
|
87
|
+
teamName: string,
|
|
88
|
+
request: Omit<QueuedWriteSpawn, "id" | "requestedAt"> & Partial<Pick<QueuedWriteSpawn, "id" | "requestedAt">>
|
|
89
|
+
): Promise<QueuedWriteSpawn> {
|
|
90
|
+
const queuePath = ensureQueueDir(teamName);
|
|
91
|
+
return await withLock(queuePath, async () => {
|
|
92
|
+
const queue = readQueueRaw(queuePath);
|
|
93
|
+
const existing = queue.find(item => matchesQueuedIdentity(item, request));
|
|
94
|
+
if (existing) return existing;
|
|
95
|
+
|
|
96
|
+
const queued: QueuedWriteSpawn = {
|
|
97
|
+
id: request.id || crypto.randomUUID(),
|
|
98
|
+
name: request.name,
|
|
99
|
+
prompt: request.prompt,
|
|
100
|
+
cwd: request.cwd,
|
|
101
|
+
category: request.category,
|
|
102
|
+
modelSlot: request.modelSlot,
|
|
103
|
+
planModeRequired: request.planModeRequired,
|
|
104
|
+
color: request.color,
|
|
105
|
+
operationId: request.operationId,
|
|
106
|
+
workflowRunId: request.workflowRunId,
|
|
107
|
+
metadata: request.metadata,
|
|
108
|
+
requestedAt: request.requestedAt || Date.now(),
|
|
109
|
+
};
|
|
110
|
+
queue.push(queued);
|
|
111
|
+
queue.sort((a, b) => a.requestedAt - b.requestedAt);
|
|
112
|
+
writeQueueRaw(queuePath, queue);
|
|
113
|
+
return queued;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function dequeueWriteSpawns(teamName: string, count: number): Promise<QueuedWriteSpawn[]> {
|
|
118
|
+
const take = Math.max(0, Math.floor(count));
|
|
119
|
+
if (take < 1) return [];
|
|
120
|
+
|
|
121
|
+
const queuePath = ensureQueueDir(teamName);
|
|
122
|
+
return await withLock(queuePath, async () => {
|
|
123
|
+
const queue = readQueueRaw(queuePath);
|
|
124
|
+
const next = queue.splice(0, take);
|
|
125
|
+
if (next.length > 0) writeQueueRaw(queuePath, queue);
|
|
126
|
+
return next;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function dequeueWriteSpawn(teamName: string): Promise<QueuedWriteSpawn | null> {
|
|
131
|
+
const [queued] = await dequeueWriteSpawns(teamName, 1);
|
|
132
|
+
return queued || null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function removeQueuedWriteSpawnsByName(teamName: string, name: string): Promise<QueuedWriteSpawn[]> {
|
|
136
|
+
const queuePath = ensureQueueDir(teamName);
|
|
137
|
+
return await withLock(queuePath, async () => {
|
|
138
|
+
const queue = readQueueRaw(queuePath);
|
|
139
|
+
const removed = queue.filter(item => item.name === name);
|
|
140
|
+
const kept = queue.filter(item => item.name !== name);
|
|
141
|
+
if (removed.length > 0) writeQueueRaw(queuePath, kept);
|
|
142
|
+
return removed;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function cancelQueuedWriteSpawn(teamName: string, id: string): Promise<QueuedWriteSpawn | null> {
|
|
147
|
+
const queuePath = ensureQueueDir(teamName);
|
|
148
|
+
return await withLock(queuePath, async () => {
|
|
149
|
+
const queue = readQueueRaw(queuePath);
|
|
150
|
+
const index = queue.findIndex(item => item.id === id);
|
|
151
|
+
if (index === -1) return null;
|
|
152
|
+
const [removed] = queue.splice(index, 1);
|
|
153
|
+
writeQueueRaw(queuePath, queue);
|
|
154
|
+
return removed;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function findQueuedWriteSpawn(
|
|
159
|
+
teamName: string,
|
|
160
|
+
criteria: { name?: string; operationId?: string; workflowRunId?: string }
|
|
161
|
+
): Promise<QueuedWriteSpawn | null> {
|
|
162
|
+
const queuePath = ensureQueueDir(teamName);
|
|
163
|
+
return await withLock(queuePath, async () => {
|
|
164
|
+
const queue = readQueueRaw(queuePath);
|
|
165
|
+
return queue.find(item => {
|
|
166
|
+
const nameMatches = criteria.name !== undefined && item.name === criteria.name;
|
|
167
|
+
const operationMatches = criteria.operationId !== undefined
|
|
168
|
+
&& (item.operationId || item.metadata?.operationId) === criteria.operationId
|
|
169
|
+
&& (criteria.workflowRunId === undefined || (item.workflowRunId || item.metadata?.workflowRunId) === criteria.workflowRunId);
|
|
170
|
+
return nameMatches || operationMatches;
|
|
171
|
+
}) || null;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function queuedWriteSpawnToMember(teamName: string, queued: QueuedWriteSpawn): Member {
|
|
176
|
+
const level = requireFavoriteModelLevel(loadSettings({ projectDir: queued.cwd }), queued.modelSlot);
|
|
177
|
+
if (level.role !== "write") {
|
|
178
|
+
throw new Error(`Queued writer ${queued.name} requires a write-* intent tier configured via /agents-favorite-models, got ${level.slot}.`);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
agentId: `${queued.name}@${teamName}`,
|
|
182
|
+
name: queued.name,
|
|
183
|
+
agentType: "teammate",
|
|
184
|
+
role: "write",
|
|
185
|
+
category: queued.category,
|
|
186
|
+
model: level.model,
|
|
187
|
+
joinedAt: Date.now(),
|
|
188
|
+
tmuxPaneId: "",
|
|
189
|
+
cwd: queued.cwd,
|
|
190
|
+
subscriptions: [],
|
|
191
|
+
prompt: queued.prompt,
|
|
192
|
+
color: queued.color || "blue",
|
|
193
|
+
thinking: level.thinking,
|
|
194
|
+
modelSlot: level.slot,
|
|
195
|
+
planModeRequired: queued.planModeRequired,
|
|
196
|
+
metadata: {
|
|
197
|
+
...(queued.metadata || {}),
|
|
198
|
+
...(queued.operationId ? { operationId: queued.operationId } : {}),
|
|
199
|
+
...(queued.workflowRunId ? { workflowRunId: queued.workflowRunId } : {}),
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|