@jwilger/pi-development-system 0.10.6 → 0.12.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/README.md +9 -1
- package/agents/advisor.md +40 -0
- package/agents/architect.md +66 -0
- package/agents/coder.md +57 -0
- package/agents/implementer.md +47 -0
- package/agents/lens-cagan.md +54 -0
- package/agents/lens-perri.md +54 -0
- package/agents/lens-pichler.md +54 -0
- package/agents/lens-rumelt.md +54 -0
- package/agents/lens-torres.md +54 -0
- package/agents/researcher.md +70 -0
- package/agents/reviewer.md +57 -0
- package/agents/tasker.md +58 -0
- package/agents/writer.md +39 -0
- package/extensions/development-system.ts +4 -0
- package/package.json +5 -3
- package/skills/delegation/SKILL.md +66 -0
- package/src/core/models.ts +18 -0
- package/src/core/routing.ts +67 -0
- package/src/core/spawn-overrides.ts +59 -0
- package/src/jev/questions/route.ts +93 -0
- package/src/state/config.ts +2 -4
- package/src/state/route-task-tool.ts +74 -0
- package/src/subagents/VENDORED.md +40 -0
- package/src/subagents/index.ts +544 -0
- package/src/subagents/orch/inherited-tools.ts +85 -0
- package/src/subagents/orch/mailbox.ts +262 -0
- package/src/subagents/orch/manager.ts +778 -0
- package/src/subagents/orch/paths.ts +74 -0
- package/src/subagents/orch/prompt.ts +25 -0
- package/src/subagents/orch/runtime.ts +791 -0
- package/src/subagents/orch/tools.ts +177 -0
- package/src/subagents/orch/transcript.ts +187 -0
- package/src/subagents/prefs/agent-import.ts +246 -0
- package/src/subagents/prefs/config.ts +1029 -0
- package/src/subagents/prefs/import-discovery.ts +644 -0
- package/src/subagents/prefs/import-instructions.ts +157 -0
- package/src/subagents/prefs/models.ts +85 -0
- package/src/subagents/prefs/settings.ts +371 -0
- package/src/subagents/types.ts +209 -0
- package/src/subagents/ui/agent-navigation-editor.ts +210 -0
- package/src/subagents/ui/dialog.ts +651 -0
- package/src/subagents/ui/import-picker.ts +232 -0
- package/src/subagents/ui/live-agent-view.ts +572 -0
- package/src/subagents/ui/model-picker.ts +452 -0
- package/src/subagents/ui/settings-ui.ts +329 -0
- package/src/subagents/ui/status-ui.ts +545 -0
- package/src/subagents/ui/thread-tree.ts +135 -0
- package/src/subagents/ui/ui.ts +1507 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// @ts-nocheck: vendored upstream pi-subagent-manager 0.14.0 compiled under looser options; see src/subagents/VENDORED.md
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import type { SessionEntry, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
|
|
5
|
+
export const LEGACY_QUEUE_TYPE = "pi-subagent:queue:v1";
|
|
6
|
+
const ACCEPTED_TYPE = "pi-subagent:mailbox:accepted:v2";
|
|
7
|
+
const CONSUMED_TYPE = "pi-subagent:mailbox:consumed:v2";
|
|
8
|
+
const MIGRATION_TYPE = "pi-subagent:mailbox:migration:v2";
|
|
9
|
+
export const MAILBOX_FIELD = "piSubagentMailbox";
|
|
10
|
+
export const BOOTSTRAP_MESSAGE = "Subagent session initialized; awaiting parent input.";
|
|
11
|
+
|
|
12
|
+
export type AcceptedKind = "steer" | "followUp" | "update";
|
|
13
|
+
|
|
14
|
+
export interface AcceptedLedgerEntry {
|
|
15
|
+
id: string;
|
|
16
|
+
kind: AcceptedKind;
|
|
17
|
+
content: string;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ConsumedLedgerEntry {
|
|
22
|
+
id: string;
|
|
23
|
+
messageEntryId: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface LegacyQueueSnapshot {
|
|
27
|
+
steering?: unknown;
|
|
28
|
+
followUp?: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface MigrationMarker {
|
|
32
|
+
sourceEntryId: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface MailboxMarker {
|
|
36
|
+
id: string;
|
|
37
|
+
kind: AcceptedKind | "bootstrap";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
41
|
+
typeof value === "object" && value !== null;
|
|
42
|
+
|
|
43
|
+
const isAcceptedEntry = (
|
|
44
|
+
entry: SessionEntry,
|
|
45
|
+
): entry is SessionEntry & { data: AcceptedLedgerEntry } =>
|
|
46
|
+
entry.type === "custom" &&
|
|
47
|
+
entry.customType === ACCEPTED_TYPE &&
|
|
48
|
+
isAcceptedLedgerEntry(entry.data);
|
|
49
|
+
|
|
50
|
+
const isConsumedEntry = (
|
|
51
|
+
entry: SessionEntry,
|
|
52
|
+
): entry is SessionEntry & { data: ConsumedLedgerEntry } =>
|
|
53
|
+
entry.type === "custom" &&
|
|
54
|
+
entry.customType === CONSUMED_TYPE &&
|
|
55
|
+
isConsumedLedgerEntry(entry.data);
|
|
56
|
+
|
|
57
|
+
const isMigrationEntry = (entry: SessionEntry, sourceEntryId: string): boolean =>
|
|
58
|
+
entry.type === "custom" &&
|
|
59
|
+
entry.customType === MIGRATION_TYPE &&
|
|
60
|
+
isObject(entry.data) &&
|
|
61
|
+
entry.data.sourceEntryId === sourceEntryId;
|
|
62
|
+
|
|
63
|
+
const isAcceptedKind = (value: unknown): value is AcceptedKind =>
|
|
64
|
+
value === "steer" || value === "followUp" || value === "update";
|
|
65
|
+
|
|
66
|
+
const isAcceptedLedgerEntry = (value: unknown): value is AcceptedLedgerEntry =>
|
|
67
|
+
isObject(value) &&
|
|
68
|
+
typeof value.id === "string" &&
|
|
69
|
+
isAcceptedKind(value.kind) &&
|
|
70
|
+
typeof value.content === "string" &&
|
|
71
|
+
typeof value.timestamp === "number";
|
|
72
|
+
|
|
73
|
+
const isConsumedLedgerEntry = (value: unknown): value is ConsumedLedgerEntry =>
|
|
74
|
+
isObject(value) && typeof value.id === "string" && typeof value.messageEntryId === "string";
|
|
75
|
+
|
|
76
|
+
const getMailboxMarker = (value: unknown): MailboxMarker | undefined => {
|
|
77
|
+
if (!isObject(value)) return undefined;
|
|
78
|
+
const marker = value[MAILBOX_FIELD];
|
|
79
|
+
if (!isObject(marker) || typeof marker.id !== "string") return undefined;
|
|
80
|
+
if (
|
|
81
|
+
marker.kind !== "steer" &&
|
|
82
|
+
marker.kind !== "followUp" &&
|
|
83
|
+
marker.kind !== "update" &&
|
|
84
|
+
marker.kind !== "bootstrap"
|
|
85
|
+
)
|
|
86
|
+
return undefined;
|
|
87
|
+
return { id: marker.id, kind: marker.kind };
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const normalizeLegacyQueue = (
|
|
91
|
+
value: unknown,
|
|
92
|
+
): { steering: string[]; followUp: string[] } | undefined => {
|
|
93
|
+
if (!isObject(value)) return undefined;
|
|
94
|
+
const steering = value.steering;
|
|
95
|
+
const followUp = value.followUp;
|
|
96
|
+
if (
|
|
97
|
+
!Array.isArray(steering) ||
|
|
98
|
+
!steering.every((text) => typeof text === "string") ||
|
|
99
|
+
!Array.isArray(followUp) ||
|
|
100
|
+
!followUp.every((text) => typeof text === "string")
|
|
101
|
+
) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
return { steering: [...steering], followUp: [...followUp] };
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const getAcceptedIdFromEntry = (entry: SessionEntry): string | undefined => {
|
|
108
|
+
if (entry.type === "message" && entry.message.role === "user") {
|
|
109
|
+
return getMailboxMarker(entry.message)?.id;
|
|
110
|
+
}
|
|
111
|
+
if (entry.type === "custom_message") {
|
|
112
|
+
return getMailboxMarker(entry.details)?.id;
|
|
113
|
+
}
|
|
114
|
+
return undefined;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const buildQueuedUserMessage = (entry: AcceptedLedgerEntry) => ({
|
|
118
|
+
role: "user" as const,
|
|
119
|
+
content: [{ type: "text" as const, text: entry.content }],
|
|
120
|
+
timestamp: entry.timestamp,
|
|
121
|
+
[MAILBOX_FIELD]: { id: entry.id, kind: entry.kind },
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export const buildUpdateDetails = (entry: AcceptedLedgerEntry) => ({
|
|
125
|
+
[MAILBOX_FIELD]: { id: entry.id, kind: entry.kind },
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
export const buildBootstrapMessage = (timestamp = Date.now()) => ({
|
|
129
|
+
role: "user" as const,
|
|
130
|
+
content: [{ type: "text" as const, text: BOOTSTRAP_MESSAGE }],
|
|
131
|
+
timestamp,
|
|
132
|
+
[MAILBOX_FIELD]: { id: `bootstrap:${timestamp}`, kind: "bootstrap" as const },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export class DurableMailbox {
|
|
136
|
+
private readonly accepted = new Map<string, AcceptedLedgerEntry>();
|
|
137
|
+
private readonly consumed = new Set<string>();
|
|
138
|
+
private readonly enqueued = new Set<string>();
|
|
139
|
+
|
|
140
|
+
constructor(private readonly sessionManager: SessionManager) {
|
|
141
|
+
this.migrateLegacyQueue();
|
|
142
|
+
this.restore();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
accept(kind: AcceptedKind, content: string, timestamp = Date.now()): AcceptedLedgerEntry {
|
|
146
|
+
return this.appendAccepted({ id: randomUUID(), kind, content, timestamp });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
pending(): AcceptedLedgerEntry[] {
|
|
150
|
+
return [...this.accepted.values()].filter((entry) => !this.consumed.has(entry.id));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
replayablePending(): AcceptedLedgerEntry[] {
|
|
154
|
+
return this.pending().filter((entry) => !this.enqueued.has(entry.id));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
markEnqueued(id: string): void {
|
|
158
|
+
this.enqueued.add(id);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
clearQueuedInputs(): void {
|
|
162
|
+
for (const entry of this.accepted.values()) {
|
|
163
|
+
if (entry.kind === "steer" || entry.kind === "followUp") this.enqueued.delete(entry.id);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private consumeAppended(messageEntryId: string, markerSource: unknown): void {
|
|
168
|
+
const id = getMailboxMarker(markerSource)?.id;
|
|
169
|
+
if (!id || !this.accepted.has(id) || this.consumed.has(id)) return;
|
|
170
|
+
this.sessionManager.appendCustomEntry(CONSUMED_TYPE, { id, messageEntryId });
|
|
171
|
+
this.consumed.add(id);
|
|
172
|
+
this.enqueued.delete(id);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
wrapAppends(onCheckpoint: () => void): () => void {
|
|
176
|
+
const originalAppendMessage = this.sessionManager.appendMessage.bind(this.sessionManager);
|
|
177
|
+
const originalAppendCustomMessageEntry = this.sessionManager.appendCustomMessageEntry.bind(
|
|
178
|
+
this.sessionManager,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
this.sessionManager.appendMessage = (message) => {
|
|
182
|
+
const entryId = originalAppendMessage(message);
|
|
183
|
+
this.consumeAppended(entryId, message);
|
|
184
|
+
onCheckpoint();
|
|
185
|
+
return entryId;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
this.sessionManager.appendCustomMessageEntry = (customType, content, display, details) => {
|
|
189
|
+
const entryId = originalAppendCustomMessageEntry(customType, content, display, details);
|
|
190
|
+
this.consumeAppended(entryId, details);
|
|
191
|
+
onCheckpoint();
|
|
192
|
+
return entryId;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return () => {
|
|
196
|
+
this.sessionManager.appendMessage = originalAppendMessage;
|
|
197
|
+
this.sessionManager.appendCustomMessageEntry = originalAppendCustomMessageEntry;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private appendAccepted(entry: AcceptedLedgerEntry): AcceptedLedgerEntry {
|
|
202
|
+
this.sessionManager.appendCustomEntry(ACCEPTED_TYPE, entry);
|
|
203
|
+
this.accepted.set(entry.id, entry);
|
|
204
|
+
return entry;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private migrateLegacyQueue(): void {
|
|
208
|
+
const branch = this.sessionManager.getBranch();
|
|
209
|
+
const legacy = [...branch]
|
|
210
|
+
.reverse()
|
|
211
|
+
.find((entry) => entry.type === "custom" && entry.customType === LEGACY_QUEUE_TYPE);
|
|
212
|
+
if (!legacy || branch.some((entry) => isMigrationEntry(entry, legacy.id))) return;
|
|
213
|
+
const queue = normalizeLegacyQueue(
|
|
214
|
+
(legacy as SessionEntry & { data?: LegacyQueueSnapshot }).data,
|
|
215
|
+
);
|
|
216
|
+
if (!queue) return;
|
|
217
|
+
const existingIds = new Set<string>();
|
|
218
|
+
for (const entry of branch) {
|
|
219
|
+
if (isAcceptedEntry(entry) || isConsumedEntry(entry)) existingIds.add(entry.data.id);
|
|
220
|
+
const acceptedId = getAcceptedIdFromEntry(entry);
|
|
221
|
+
if (acceptedId) existingIds.add(acceptedId);
|
|
222
|
+
}
|
|
223
|
+
const appendLegacyAccepted = (kind: AcceptedKind, content: string, occurrenceIndex: number) => {
|
|
224
|
+
const accepted = {
|
|
225
|
+
id: `legacy:${legacy.id}:${kind}:${occurrenceIndex}`,
|
|
226
|
+
kind,
|
|
227
|
+
content,
|
|
228
|
+
timestamp: Date.parse(legacy.timestamp),
|
|
229
|
+
} satisfies AcceptedLedgerEntry;
|
|
230
|
+
if (existingIds.has(accepted.id)) return;
|
|
231
|
+
this.appendAccepted(accepted);
|
|
232
|
+
existingIds.add(accepted.id);
|
|
233
|
+
};
|
|
234
|
+
queue.steering.forEach((content, occurrenceIndex) => {
|
|
235
|
+
appendLegacyAccepted("steer", content, occurrenceIndex);
|
|
236
|
+
});
|
|
237
|
+
queue.followUp.forEach((content, occurrenceIndex) => {
|
|
238
|
+
appendLegacyAccepted("followUp", content, occurrenceIndex);
|
|
239
|
+
});
|
|
240
|
+
this.sessionManager.appendCustomEntry(MIGRATION_TYPE, {
|
|
241
|
+
sourceEntryId: legacy.id,
|
|
242
|
+
} satisfies MigrationMarker);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private restore(): void {
|
|
246
|
+
this.accepted.clear();
|
|
247
|
+
this.consumed.clear();
|
|
248
|
+
this.enqueued.clear();
|
|
249
|
+
for (const entry of this.sessionManager.getBranch()) {
|
|
250
|
+
if (isAcceptedEntry(entry)) {
|
|
251
|
+
this.accepted.set(entry.data.id, entry.data);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (isConsumedEntry(entry)) {
|
|
255
|
+
this.consumed.add(entry.data.id);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const transcriptId = getAcceptedIdFromEntry(entry);
|
|
259
|
+
if (transcriptId) this.consumed.add(transcriptId);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|