@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schilderlabs/pitown-package",
3
- "version": "0.2.6",
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.6"
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("Allowed Pi Town tools: pitown_board, pitown_delegate"),
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 notifiedCompletions = new Set<string>()
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 {