@llblab/pi-actors 0.51.0 → 0.52.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/AGENTS.md +5 -3
- package/BACKLOG.md +1 -1
- package/CHANGELOG.md +8 -0
- package/README.md +3 -3
- package/dist/index.js +3 -0
- package/dist/lib/async-runs.d.ts +2 -2
- package/dist/lib/async-runs.js +42 -19
- package/dist/lib/extension-runtime.d.ts +1 -0
- package/dist/lib/extension-runtime.js +6 -1
- package/dist/lib/limits.d.ts +9 -0
- package/dist/lib/limits.js +9 -0
- package/dist/lib/observability.d.ts +10 -11
- package/dist/lib/observability.js +79 -56
- package/dist/lib/pi.d.ts +31 -0
- package/dist/lib/pi.js +180 -0
- package/dist/lib/run-delivery.d.ts +115 -0
- package/dist/lib/run-delivery.js +623 -0
- package/dist/lib/run-ui-runtime.d.ts +3 -0
- package/dist/lib/run-ui-runtime.js +341 -13
- package/dist/lib/runs-trace.d.ts +1 -1
- package/dist/lib/runs-trace.js +5 -3
- package/dist/lib/session-evidence.d.ts +16 -0
- package/dist/lib/session-evidence.js +143 -0
- package/dist/lib/temp.js +1 -1
- package/dist/lib/tools-inspect.js +3 -1
- package/dist/skills/actors/SKILL.md +2 -2
- package/dist/skills/actors/references/runs.md +1 -1
- package/dist/skills/swarm/SKILL.md +1 -1
- package/docs/README.md +1 -0
- package/docs/async-runs.md +4 -3
- package/docs/coordinator-delivery.md +207 -0
- package/index.ts +3 -0
- package/lib/async-runs.ts +42 -21
- package/lib/extension-runtime.ts +6 -1
- package/lib/limits.ts +9 -0
- package/lib/observability.ts +96 -78
- package/lib/pi.ts +210 -0
- package/lib/run-delivery.ts +800 -0
- package/lib/run-ui-runtime.ts +370 -18
- package/lib/runs-trace.ts +6 -4
- package/lib/session-evidence.ts +153 -0
- package/lib/temp.ts +1 -1
- package/lib/tools-inspect.ts +4 -1
- package/package.json +1 -1
- package/skills/actors/SKILL.md +2 -2
- package/skills/actors/references/runs.md +1 -1
- package/skills/swarm/SKILL.md +1 -1
package/lib/pi.ts
CHANGED
|
@@ -9,8 +9,13 @@ import type {
|
|
|
9
9
|
ExtensionContext,
|
|
10
10
|
} from "@earendil-works/pi-coding-agent";
|
|
11
11
|
|
|
12
|
+
import * as SessionEvidence from "./session-evidence.ts";
|
|
13
|
+
|
|
12
14
|
export type { ExtensionAPI, ExtensionContext };
|
|
13
15
|
|
|
16
|
+
export const RUN_COMPLETION_BATCH_CUSTOM_TYPE = "pi-actors-run-batch";
|
|
17
|
+
export const RUN_STEER_CUSTOM_TYPE = "pi-actors-run-steer";
|
|
18
|
+
|
|
14
19
|
export interface PiNotificationSink {
|
|
15
20
|
notify(message: string, level: "info" | "warning" | "error"): void;
|
|
16
21
|
sendFollowUp(message: {
|
|
@@ -39,6 +44,211 @@ export function createNotificationSink(
|
|
|
39
44
|
};
|
|
40
45
|
}
|
|
41
46
|
|
|
47
|
+
export function sendRunCompletionBatch(
|
|
48
|
+
pi: ExtensionAPI,
|
|
49
|
+
batchId: string,
|
|
50
|
+
content: string,
|
|
51
|
+
): void {
|
|
52
|
+
pi.sendMessage({
|
|
53
|
+
customType: RUN_COMPLETION_BATCH_CUSTOM_TYPE,
|
|
54
|
+
content,
|
|
55
|
+
display: false,
|
|
56
|
+
details: {
|
|
57
|
+
pi_actors_delivery: {
|
|
58
|
+
batch_id: batchId,
|
|
59
|
+
kind: "completion_batch",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
}, {
|
|
63
|
+
deliverAs: "followUp",
|
|
64
|
+
triggerTurn: true,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function sendRunSteer(
|
|
69
|
+
pi: ExtensionAPI,
|
|
70
|
+
input: { content: string; eventId: string; steerId: string },
|
|
71
|
+
): void {
|
|
72
|
+
pi.sendMessage({
|
|
73
|
+
customType: RUN_STEER_CUSTOM_TYPE,
|
|
74
|
+
content: input.content,
|
|
75
|
+
display: false,
|
|
76
|
+
details: {
|
|
77
|
+
pi_actors_delivery: {
|
|
78
|
+
event_id: input.eventId,
|
|
79
|
+
kind: "urgent_steer",
|
|
80
|
+
steer_id: input.steerId,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
}, {
|
|
84
|
+
deliverAs: "steer",
|
|
85
|
+
triggerTurn: true,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function completionBatchMessage(
|
|
90
|
+
message: unknown,
|
|
91
|
+
): { batchId: string; content: string } | undefined {
|
|
92
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) return undefined;
|
|
93
|
+
const record = message as Record<string, unknown>;
|
|
94
|
+
if (
|
|
95
|
+
record.customType !== RUN_COMPLETION_BATCH_CUSTOM_TYPE ||
|
|
96
|
+
typeof record.content !== "string"
|
|
97
|
+
) return undefined;
|
|
98
|
+
const details = record.details;
|
|
99
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) return undefined;
|
|
100
|
+
const delivery = (details as Record<string, unknown>).pi_actors_delivery;
|
|
101
|
+
if (!delivery || typeof delivery !== "object" || Array.isArray(delivery)) return undefined;
|
|
102
|
+
const envelope = delivery as Record<string, unknown>;
|
|
103
|
+
if (
|
|
104
|
+
envelope.kind !== "completion_batch" ||
|
|
105
|
+
typeof envelope.batch_id !== "string" ||
|
|
106
|
+
!envelope.batch_id ||
|
|
107
|
+
envelope.batch_id.length > 128
|
|
108
|
+
) return undefined;
|
|
109
|
+
return { batchId: envelope.batch_id, content: record.content };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Collapse exact retry duplicates and remove conflicting delivery envelopes. */
|
|
113
|
+
export function dedupeRunCompletionBatchContext(messages: unknown[]): {
|
|
114
|
+
batches: Map<string, string>;
|
|
115
|
+
conflicts: Set<string>;
|
|
116
|
+
messages: unknown[];
|
|
117
|
+
} {
|
|
118
|
+
const batches = new Map<string, string>();
|
|
119
|
+
const conflicts = new Set<string>();
|
|
120
|
+
for (const message of messages) {
|
|
121
|
+
const batch = completionBatchMessage(message);
|
|
122
|
+
if (!batch) continue;
|
|
123
|
+
const existing = batches.get(batch.batchId);
|
|
124
|
+
if (existing !== undefined && existing !== batch.content) {
|
|
125
|
+
conflicts.add(batch.batchId);
|
|
126
|
+
} else if (existing === undefined) {
|
|
127
|
+
batches.set(batch.batchId, batch.content);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const retained = new Set<string>();
|
|
131
|
+
const filtered = messages.filter((message) => {
|
|
132
|
+
const batch = completionBatchMessage(message);
|
|
133
|
+
if (!batch) return true;
|
|
134
|
+
if (conflicts.has(batch.batchId) || retained.has(batch.batchId)) return false;
|
|
135
|
+
retained.add(batch.batchId);
|
|
136
|
+
return true;
|
|
137
|
+
});
|
|
138
|
+
for (const batchId of conflicts) batches.delete(batchId);
|
|
139
|
+
return { batches, conflicts, messages: filtered };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function removeRunCompletionBatchFromContext(
|
|
143
|
+
messages: unknown[],
|
|
144
|
+
batchId: string,
|
|
145
|
+
): unknown[] {
|
|
146
|
+
return messages.filter((message) =>
|
|
147
|
+
completionBatchMessage(message)?.batchId !== batchId);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function steerMessage(
|
|
151
|
+
message: unknown,
|
|
152
|
+
): { content: string; eventId: string; steerId: string } | undefined {
|
|
153
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) return undefined;
|
|
154
|
+
const record = message as Record<string, unknown>;
|
|
155
|
+
if (record.customType !== RUN_STEER_CUSTOM_TYPE || typeof record.content !== "string") {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
const details = record.details;
|
|
159
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) return undefined;
|
|
160
|
+
const delivery = (details as Record<string, unknown>).pi_actors_delivery;
|
|
161
|
+
if (!delivery || typeof delivery !== "object" || Array.isArray(delivery)) return undefined;
|
|
162
|
+
const envelope = delivery as Record<string, unknown>;
|
|
163
|
+
if (
|
|
164
|
+
envelope.kind !== "urgent_steer" ||
|
|
165
|
+
typeof envelope.steer_id !== "string" ||
|
|
166
|
+
!envelope.steer_id ||
|
|
167
|
+
envelope.steer_id.length > 128 ||
|
|
168
|
+
typeof envelope.event_id !== "string" ||
|
|
169
|
+
!envelope.event_id ||
|
|
170
|
+
envelope.event_id.length > 256
|
|
171
|
+
) return undefined;
|
|
172
|
+
return {
|
|
173
|
+
content: record.content,
|
|
174
|
+
eventId: envelope.event_id,
|
|
175
|
+
steerId: envelope.steer_id,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function dedupeRunSteerContext(messages: unknown[]): {
|
|
180
|
+
conflicts: Set<string>;
|
|
181
|
+
messages: unknown[];
|
|
182
|
+
steers: Map<string, { content: string; eventId: string }>;
|
|
183
|
+
} {
|
|
184
|
+
const conflicts = new Set<string>();
|
|
185
|
+
const steers = new Map<string, { content: string; eventId: string }>();
|
|
186
|
+
for (const message of messages) {
|
|
187
|
+
const steer = steerMessage(message);
|
|
188
|
+
if (!steer) continue;
|
|
189
|
+
const existing = steers.get(steer.steerId);
|
|
190
|
+
if (existing &&
|
|
191
|
+
(existing.content !== steer.content || existing.eventId !== steer.eventId)) {
|
|
192
|
+
conflicts.add(steer.steerId);
|
|
193
|
+
} else if (!existing) {
|
|
194
|
+
steers.set(steer.steerId, {
|
|
195
|
+
content: steer.content,
|
|
196
|
+
eventId: steer.eventId,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const retained = new Set<string>();
|
|
201
|
+
const filtered = messages.filter((message) => {
|
|
202
|
+
const steer = steerMessage(message);
|
|
203
|
+
if (!steer) return true;
|
|
204
|
+
if (conflicts.has(steer.steerId) || retained.has(steer.steerId)) return false;
|
|
205
|
+
retained.add(steer.steerId);
|
|
206
|
+
return true;
|
|
207
|
+
});
|
|
208
|
+
for (const steerId of conflicts) steers.delete(steerId);
|
|
209
|
+
return { conflicts, messages: filtered, steers };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function removeRunSteerFromContext(
|
|
213
|
+
messages: unknown[],
|
|
214
|
+
steerId: string,
|
|
215
|
+
): unknown[] {
|
|
216
|
+
return messages.filter((message) => steerMessage(message)?.steerId !== steerId);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function inspectRunSteerSessionEvidence(
|
|
220
|
+
ctx: ExtensionContext,
|
|
221
|
+
input: { content: string; eventId: string; steerId: string },
|
|
222
|
+
): SessionEvidence.ActiveSessionEntryEvidence {
|
|
223
|
+
return SessionEvidence.inspectBoundedActiveSessionEntries({
|
|
224
|
+
getEntry: (id) => ctx.sessionManager.getEntry(id),
|
|
225
|
+
leaf: ctx.sessionManager.getLeafEntry(),
|
|
226
|
+
match: (entry) => {
|
|
227
|
+
const steer = steerMessage(entry);
|
|
228
|
+
if (!steer || steer.steerId !== input.steerId) return undefined;
|
|
229
|
+
return steer.content === input.content && steer.eventId === input.eventId
|
|
230
|
+
? "present"
|
|
231
|
+
: "conflict";
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function inspectRunCompletionBatchSessionEvidence(
|
|
237
|
+
ctx: ExtensionContext,
|
|
238
|
+
batchId: string,
|
|
239
|
+
content: string,
|
|
240
|
+
): SessionEvidence.ActiveSessionEntryEvidence {
|
|
241
|
+
return SessionEvidence.inspectBoundedActiveSessionEntries({
|
|
242
|
+
getEntry: (id) => ctx.sessionManager.getEntry(id),
|
|
243
|
+
leaf: ctx.sessionManager.getLeafEntry(),
|
|
244
|
+
match: (entry) => {
|
|
245
|
+
const batch = completionBatchMessage(entry);
|
|
246
|
+
if (!batch || batch.batchId !== batchId) return undefined;
|
|
247
|
+
return batch.content === content ? "present" : "conflict";
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
42
252
|
export function registerToolDefinitions(
|
|
43
253
|
pi: ExtensionAPI,
|
|
44
254
|
definitions: Iterable<unknown>,
|