@schilderlabs/pitown-package 0.2.6 → 0.2.7
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/package.json +2 -2
- package/pi/extensions/index.test.ts +41 -1
- package/pi/extensions/town-tools.ts +27 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schilderlabs/pitown-package",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.mts",
|
|
6
6
|
"exports": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@mariozechner/pi-ai": "0.58.3",
|
|
20
|
-
"@schilderlabs/pitown-core": "0.2.
|
|
20
|
+
"@schilderlabs/pitown-core": "0.2.7"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@mariozechner/pi-coding-agent": "0.58.3",
|
|
@@ -175,7 +175,7 @@ describe("pi town extension", () => {
|
|
|
175
175
|
message: expect.objectContaining({
|
|
176
176
|
customType: "pitown-context",
|
|
177
177
|
display: false,
|
|
178
|
-
content: expect.stringContaining("
|
|
178
|
+
content: expect.stringContaining("Recent inbox:\ninbox: empty"),
|
|
179
179
|
}),
|
|
180
180
|
}),
|
|
181
181
|
)
|
|
@@ -237,6 +237,46 @@ describe("pi town extension", () => {
|
|
|
237
237
|
)
|
|
238
238
|
})
|
|
239
239
|
|
|
240
|
+
it("shows queued mayor inbox updates as mayor UI notifications on the next turn", async () => {
|
|
241
|
+
const { artifactsDir, repoRoot, mayorSessionFile, workerSessionFile } = createRepoArtifacts()
|
|
242
|
+
const { handlers, tools } = setup()
|
|
243
|
+
const ctx = createToolContext(repoRoot, mayorSessionFile)
|
|
244
|
+
|
|
245
|
+
writeAgentState(
|
|
246
|
+
artifactsDir,
|
|
247
|
+
createAgentState({
|
|
248
|
+
agentId: "mayor",
|
|
249
|
+
role: "mayor",
|
|
250
|
+
status: "queued",
|
|
251
|
+
task: "coordinate town work",
|
|
252
|
+
lastMessage: "worker-001 completed task-123",
|
|
253
|
+
session: createAgentSessionRecord({
|
|
254
|
+
sessionDir: join(artifactsDir, "agents", "mayor", "sessions"),
|
|
255
|
+
sessionPath: mayorSessionFile,
|
|
256
|
+
sessionId: "mayor",
|
|
257
|
+
}),
|
|
258
|
+
}),
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
await tools.get("pitown_message_agent")?.execute(
|
|
262
|
+
"tool-call",
|
|
263
|
+
{ agentId: "mayor", body: "worker-001 completed task-123 (fix the failing auth flow): auth flow fixed" },
|
|
264
|
+
undefined,
|
|
265
|
+
() => {},
|
|
266
|
+
createToolContext(repoRoot, workerSessionFile),
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
await handlers["before_agent_start"]?.[1]?.(
|
|
270
|
+
{ systemPrompt: "base", prompt: "follow up", images: [] },
|
|
271
|
+
ctx,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
expect(ctx.ui.notify).toHaveBeenCalledWith(
|
|
275
|
+
"worker-001 completed task-123 (fix the failing auth flow): auth flow fixed",
|
|
276
|
+
"info",
|
|
277
|
+
)
|
|
278
|
+
})
|
|
279
|
+
|
|
240
280
|
it("enables mayor plan mode with a read-only tool set", async () => {
|
|
241
281
|
const { repoRoot, mayorSessionFile } = createRepoArtifacts()
|
|
242
282
|
const { commands, activeTools, appendedEntries } = setup()
|
|
@@ -121,6 +121,7 @@ function buildStartupContext(context: TownAgentContext): string {
|
|
|
121
121
|
|
|
122
122
|
const currentTask = state.task ?? "no active task"
|
|
123
123
|
const board = formatBoard(context.artifactsDir)
|
|
124
|
+
const inbox = formatMailbox(context.artifactsDir, context.agentId, "inbox")
|
|
124
125
|
const tools = getAllowedTools(context.role).join(", ")
|
|
125
126
|
|
|
126
127
|
return [
|
|
@@ -131,6 +132,9 @@ function buildStartupContext(context: TownAgentContext): string {
|
|
|
131
132
|
"",
|
|
132
133
|
"Current board:",
|
|
133
134
|
board,
|
|
135
|
+
"",
|
|
136
|
+
"Recent inbox:",
|
|
137
|
+
inbox,
|
|
134
138
|
].join("\n")
|
|
135
139
|
}
|
|
136
140
|
|
|
@@ -142,7 +146,7 @@ function requireTownContext(ctx: ExtensionContext): TownAgentContext {
|
|
|
142
146
|
return context
|
|
143
147
|
}
|
|
144
148
|
|
|
145
|
-
const
|
|
149
|
+
const notifiedMayorInboxMessages = new Set<string>()
|
|
146
150
|
|
|
147
151
|
export function registerTownTools(pi: ExtensionAPI) {
|
|
148
152
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
@@ -165,23 +169,25 @@ export function registerTownTools(pi: ExtensionAPI) {
|
|
|
165
169
|
|
|
166
170
|
const agents = listAgentStates(context.artifactsDir)
|
|
167
171
|
const workers = agents.filter((a) => a.agentId !== "mayor")
|
|
172
|
+
const recentMayorMessages = readAgentMessages(context.artifactsDir, context.agentId, "inbox").slice(-5)
|
|
173
|
+
|
|
174
|
+
for (const record of recentMayorMessages) {
|
|
175
|
+
if (record.from === "human" || record.from === "system") continue
|
|
176
|
+
|
|
177
|
+
const key = `${record.createdAt}:${record.from}:${record.body}`
|
|
178
|
+
if (notifiedMayorInboxMessages.has(key)) continue
|
|
179
|
+
|
|
180
|
+
notifiedMayorInboxMessages.add(key)
|
|
181
|
+
const level = /\bblocked\b|\bfailed\b|\bstopped\b/i.test(record.body) ? "warning" : "info"
|
|
182
|
+
ctx.ui.notify(record.body, level)
|
|
183
|
+
}
|
|
184
|
+
|
|
168
185
|
if (workers.length === 0) {
|
|
169
186
|
ctx.ui.setStatus("pitown-workers", undefined)
|
|
170
187
|
ctx.ui.setWidget("pitown-workers", undefined)
|
|
171
188
|
return
|
|
172
189
|
}
|
|
173
190
|
|
|
174
|
-
for (const w of workers) {
|
|
175
|
-
if ((w.status === "idle" || w.status === "completed") && !notifiedCompletions.has(w.agentId)) {
|
|
176
|
-
notifiedCompletions.add(w.agentId)
|
|
177
|
-
ctx.ui.notify(`✓ ${w.agentId} finished: ${w.lastMessage ?? w.task ?? "done"}`, "info")
|
|
178
|
-
}
|
|
179
|
-
if ((w.status === "blocked" || w.status === "failed" || w.status === "stopped") && !notifiedCompletions.has(w.agentId)) {
|
|
180
|
-
notifiedCompletions.add(w.agentId)
|
|
181
|
-
ctx.ui.notify(`✗ ${w.agentId} blocked: ${w.lastMessage ?? "needs attention"}`, "warning")
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
191
|
const running = workers.filter((a) => a.status === "running" || a.status === "starting").length
|
|
186
192
|
const done = workers.filter((a) => a.status === "idle" || a.status === "completed").length
|
|
187
193
|
const blocked = workers.filter((a) => a.status === "blocked" || a.status === "failed" || a.status === "stopped").length
|
|
@@ -241,6 +247,15 @@ export function registerTownTools(pi: ExtensionAPI) {
|
|
|
241
247
|
agentId: input.agentId ?? null,
|
|
242
248
|
extensionPath: resolvePiTownExtensionPath(),
|
|
243
249
|
appendedSystemPrompt: input.role === "mayor" ? readPiTownMayorPrompt() : null,
|
|
250
|
+
completionAutoResumeTarget:
|
|
251
|
+
context.agentId === "mayor"
|
|
252
|
+
? {
|
|
253
|
+
agentId: "mayor",
|
|
254
|
+
message: "New agent check-ins arrived. Review the latest board and inbox updates, then decide the next bounded action.",
|
|
255
|
+
extensionPath: resolvePiTownExtensionPath(),
|
|
256
|
+
appendedSystemPrompt: readPiTownMayorPrompt(),
|
|
257
|
+
}
|
|
258
|
+
: null,
|
|
244
259
|
})
|
|
245
260
|
|
|
246
261
|
return {
|