zob-harness 0.3.0 → 0.3.1
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/.pi/extensions/zob-harness/src/domains/coms/coms-v2/registry.ts +44 -1
- package/.pi/extensions/zob-harness/src/domains/coms/coms-v2/zpeer.ts +32 -4
- package/.pi/extensions/zob-harness/src/domains/coms/mission-control.ts +4 -1
- package/.pi/extensions/zob-harness/src/runtime/commands.ts +2 -2
- package/.pi/extensions/zob-harness/src/runtime/events.ts +6 -5
- package/.pi/extensions/zob-harness/src/runtime/widget.ts +2 -2
- package/.pi/skills/zob-agentic-spec-team/SKILL.md +4 -1
- package/.pi/skills/zob-coms-safety/SKILL.md +13 -0
- package/.pi/skills/zob-coms-v2-live/SKILL.md +11 -0
- package/.pi/skills/zob-factory/SKILL.md +21 -0
- package/.pi/skills/zob-harness/SKILL.md +14 -0
- package/.pi/skills/zob-zagent-creator/SKILL.md +10 -0
- package/.pi/zagents/agent-factory-pacman-chief.json +22 -0
- package/.pi/zagents/agent-factory-pacman-engine-builder.json +21 -0
- package/.pi/zagents/agent-factory-pacman-frontend-builder.json +21 -0
- package/.pi/zagents/agent-factory-pacman-game-architect.json +21 -0
- package/.pi/zagents/agent-factory-pacman-game-designer.json +21 -0
- package/.pi/zagents/agent-factory-pacman-qa-oracle.json +21 -0
- package/.pi/zagents/prompts/agent-factory-pacman-chief.md +53 -0
- package/.pi/zagents/prompts/agent-factory-pacman-engine-builder.md +41 -0
- package/.pi/zagents/prompts/agent-factory-pacman-frontend-builder.md +40 -0
- package/.pi/zagents/prompts/agent-factory-pacman-game-architect.md +41 -0
- package/.pi/zagents/prompts/agent-factory-pacman-game-designer.md +43 -0
- package/.pi/zagents/prompts/agent-factory-pacman-qa-oracle.md +51 -0
- package/.pi/zteams/agent-factory-pacman-multiplayer-runtime.mjs +384 -0
- package/.pi/zteams/agent-factory-pacman-multiplayer.json +42 -0
- package/.pi/zteams/agent-factory-pacman-multiplayer.tmux.sh +256 -0
- package/.pi/zteams/templates/agent-factory-pacman-chief-kickoff.template.md +71 -0
- package/.pi/zteams/templates/agent-factory-pacman-worker-kickoff.template.md +59 -0
- package/README.md +183 -110
- package/SOURCE_INDEX.md +4 -0
- package/examples/agent-factory-mission-control/AGENTS.md +10 -0
- package/examples/agent-factory-mission-control/README.md +17 -0
- package/examples/agent-factory-mission-control/apps/api/AGENTS.md +3 -0
- package/examples/agent-factory-mission-control/apps/dashboard/AGENTS.md +3 -0
- package/examples/agent-factory-mission-control/mission.md +3 -0
- package/examples/agent-factory-mission-control/output-contract.md +3 -0
- package/examples/agent-factory-mission-control/packages/domain/AGENTS.md +3 -0
- package/examples/agent-factory-mission-control/packages/snapshot-reader/AGENTS.md +3 -0
- package/examples/agent-factory-pacman-multiplayer/AGENTS.md +27 -0
- package/examples/agent-factory-pacman-multiplayer/README.md +84 -0
- package/examples/agent-factory-pacman-multiplayer/mission.md +43 -0
- package/examples/agent-factory-pacman-multiplayer/output-contract.md +58 -0
- package/examples/agent-factory-tmux-comms/README.md +146 -0
- package/examples/agent-factory-tmux-comms/chief-kickoff.template.md +54 -0
- package/examples/agent-factory-tmux-comms/simple-agent-factory.team.json +92 -0
- package/examples/agent-factory-tmux-comms/simple-agent-factory.tmux.sh +248 -0
- package/examples/agent-factory-tmux-comms/worker-kickoff.template.md +43 -0
- package/package.json +9 -3
- package/scripts/zpeer-local-e2e-smoke.mjs +6 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
|
|
@@ -10,6 +10,8 @@ import { readZobComsV2Policy, zobComsRegistryEnabled } from "./policy.js";
|
|
|
10
10
|
import type { ZobLivePeerCard, ZobLivePeerStatus, ZobLiveRegistrySnapshot } from "./types.js";
|
|
11
11
|
|
|
12
12
|
const FORBIDDEN_PERSISTED_KEYS = new Set(["body", "task", "prompt", "output", "content", "message", "rationale", "text", "diff", "patch"]);
|
|
13
|
+
const DEFAULT_OFFLINE_PEER_RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
14
|
+
const MIN_OFFLINE_PEER_RETENTION_MS = 5 * 60 * 1000;
|
|
13
15
|
|
|
14
16
|
function registryRoot(): { path: string; kind: "user_runtime" | "env_override" } {
|
|
15
17
|
const override = process.env.ZOB_COMS_REGISTRY_ROOT;
|
|
@@ -54,6 +56,19 @@ function readPeerCardsFromAgentsDir(dir: string, nowMs: number, teamName?: strin
|
|
|
54
56
|
.map((peer) => ({ ...peer, status: derivePeerStatus(peer, nowMs) }));
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
function boundedOfflinePeerRetentionMs(value: number | undefined): number {
|
|
60
|
+
const env = Number.parseInt(process.env.ZOB_COMS_OFFLINE_PEER_RETENTION_MS ?? "", 10);
|
|
61
|
+
const raw = typeof value === "number" && Number.isFinite(value) ? value : Number.isFinite(env) ? env : DEFAULT_OFFLINE_PEER_RETENTION_MS;
|
|
62
|
+
return Math.max(MIN_OFFLINE_PEER_RETENTION_MS, Math.floor(raw));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function offlinePeerExpired(peer: ZobLivePeerCard, nowMs: number, retentionMs: number): boolean {
|
|
66
|
+
if (derivePeerStatus(peer, nowMs) !== "offline") return false;
|
|
67
|
+
const heartbeatMs = Date.parse(peer.heartbeatAt);
|
|
68
|
+
if (!Number.isFinite(heartbeatMs)) return true;
|
|
69
|
+
return nowMs - heartbeatMs >= Math.max(peer.offlineAfterMs, retentionMs);
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
function allProjectAgentsDirs(): string[] {
|
|
58
73
|
const root = registryRoot();
|
|
59
74
|
const projectsDir = join(root.path, "projects");
|
|
@@ -94,6 +109,34 @@ function derivePeerStatus(peer: ZobLivePeerCard, nowMs: number): ZobLivePeerStat
|
|
|
94
109
|
return "online";
|
|
95
110
|
}
|
|
96
111
|
|
|
112
|
+
export function pruneExpiredZobLivePeers(repoRoot: string, input: { teamName?: string; nowMs?: number; retentionMs?: number } = {}): { schema: "zob.live-registry-prune.v1"; pruned: number; retained: number; retentionMs: number; bodyStored: false } {
|
|
113
|
+
const { dir } = projectAgentsDir(repoRoot);
|
|
114
|
+
const nowMs = input.nowMs ?? Date.now();
|
|
115
|
+
const retentionMs = boundedOfflinePeerRetentionMs(input.retentionMs);
|
|
116
|
+
let pruned = 0;
|
|
117
|
+
let retained = 0;
|
|
118
|
+
if (!existsSync(dir)) return { schema: "zob.live-registry-prune.v1", pruned, retained, retentionMs, bodyStored: false };
|
|
119
|
+
for (const entry of readdirSync(dir).filter((name) => name.endsWith(".json"))) {
|
|
120
|
+
const filePath = join(dir, entry);
|
|
121
|
+
try {
|
|
122
|
+
const peer = parsePeerCard(JSON.parse(readFileSync(filePath, "utf8")) as unknown);
|
|
123
|
+
if (!peer || (input.teamName && peer.team !== input.teamName)) {
|
|
124
|
+
retained += 1;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (offlinePeerExpired(peer, nowMs, retentionMs)) {
|
|
128
|
+
unlinkSync(filePath);
|
|
129
|
+
pruned += 1;
|
|
130
|
+
} else {
|
|
131
|
+
retained += 1;
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
retained += 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { schema: "zob.live-registry-prune.v1", pruned, retained, retentionMs, bodyStored: false };
|
|
138
|
+
}
|
|
139
|
+
|
|
97
140
|
export function writeZobLivePeerCard(repoRoot: string, peer: ZobLivePeerCard): ZobLivePeerCard {
|
|
98
141
|
if (hasForbiddenPersistedKey(peer)) throw new Error("Refusing to persist ZOB live peer card with forbidden body-like keys");
|
|
99
142
|
if (peer.bodyStored !== false) throw new Error("ZOB live peer card bodyStored must be false");
|
|
@@ -26,6 +26,9 @@ export interface ZpeerRoomSummary {
|
|
|
26
26
|
stale: number;
|
|
27
27
|
offline: number;
|
|
28
28
|
aliases: string[];
|
|
29
|
+
onlineAliases: string[];
|
|
30
|
+
staleAliases: string[];
|
|
31
|
+
offlineAliases: string[];
|
|
29
32
|
duplicateAliases: string[];
|
|
30
33
|
membershipCount?: number;
|
|
31
34
|
localOnly: true;
|
|
@@ -248,9 +251,15 @@ function peersInRoom(repoRoot: string, roomId: string): ZpeerRoomPeer[] {
|
|
|
248
251
|
|
|
249
252
|
function buildZpeerRoomSummaryFromPeers(projectId: string, self: ZobLivePeerCard | undefined, roomId: string, peers: ZpeerRoomPeer[]): ZpeerRoomSummary {
|
|
250
253
|
const counts: Record<ZobLivePeerStatus, number> = { online: 0, stale: 0, offline: 0 };
|
|
254
|
+
const statusAliases: Record<ZobLivePeerStatus, string[]> = { online: [], stale: [], offline: [] };
|
|
251
255
|
const aliases = peers.map((entry) => entry.membership.alias).sort();
|
|
252
|
-
for (const entry of peers)
|
|
253
|
-
|
|
256
|
+
for (const entry of peers) {
|
|
257
|
+
const status = zpeerReachableStatus(entry.peer);
|
|
258
|
+
counts[status] += 1;
|
|
259
|
+
statusAliases[status].push(entry.membership.alias);
|
|
260
|
+
}
|
|
261
|
+
const onlineAliases = statusAliases.online.sort();
|
|
262
|
+
const duplicateAliases = onlineAliases.filter((alias, index) => onlineAliases.indexOf(alias) !== index).filter((alias, index, all) => all.indexOf(alias) === index);
|
|
254
263
|
return {
|
|
255
264
|
schema: "zob.zpeer-room-summary.v1",
|
|
256
265
|
projectId,
|
|
@@ -261,6 +270,9 @@ function buildZpeerRoomSummaryFromPeers(projectId: string, self: ZobLivePeerCard
|
|
|
261
270
|
stale: counts.stale,
|
|
262
271
|
offline: counts.offline,
|
|
263
272
|
aliases,
|
|
273
|
+
onlineAliases,
|
|
274
|
+
staleAliases: statusAliases.stale.sort(),
|
|
275
|
+
offlineAliases: statusAliases.offline.sort(),
|
|
264
276
|
duplicateAliases,
|
|
265
277
|
membershipCount: self ? zpeerMembershipsForPeer(self).length : undefined,
|
|
266
278
|
localOnly: true,
|
|
@@ -471,8 +483,24 @@ export async function sendZpeerPrompt(repoRoot: string, self: ZobLivePeerCard, t
|
|
|
471
483
|
const candidates = peersInRoom(repoRoot, roomId).filter((entry) => entry.membership.alias === targetAlias && entry.peer.sessionHash !== self.sessionHash);
|
|
472
484
|
if (targetAlias === senderAlias) return finish("attempt", { status: "blocked", reason: "cannot send to self", targetAlias, taskHash, bodyStored: false });
|
|
473
485
|
if (candidates.length === 0) return finish("attempt", { status: "blocked", reason: `peer @${targetAlias} not found in room '${roomId}'`, targetAlias, taskHash, bodyStored: false }, 0);
|
|
474
|
-
|
|
475
|
-
|
|
486
|
+
let liveCandidates = candidates.filter((entry) => zpeerReachableStatus(entry.peer) === "online");
|
|
487
|
+
if (liveCandidates.length > 1) {
|
|
488
|
+
const responsiveCandidates: ZpeerRoomPeer[] = [];
|
|
489
|
+
for (const entry of liveCandidates) {
|
|
490
|
+
if (await peerRespondsToAliasPing(entry.peer)) {
|
|
491
|
+
responsiveCandidates.push(entry);
|
|
492
|
+
} else {
|
|
493
|
+
try { writeZobLivePeerCard(repoRoot, { ...entry.peer, heartbeatAt: new Date().toISOString(), status: "offline" }); } catch { /* best-effort ghost alias release */ }
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
liveCandidates = responsiveCandidates;
|
|
497
|
+
}
|
|
498
|
+
if (liveCandidates.length === 0) {
|
|
499
|
+
const statuses = [...new Set(candidates.map((entry) => zpeerReachableStatus(entry.peer)))].sort().join("/") || "offline";
|
|
500
|
+
return finish("attempt", { status: "blocked", reason: `peer @${targetAlias} is ${statuses}`, targetAlias, taskHash, bodyStored: false }, candidates.length);
|
|
501
|
+
}
|
|
502
|
+
if (liveCandidates.length > 1) return finish("attempt", { status: "blocked", reason: `duplicate live alias @${targetAlias} in room '${roomId}'`, targetAlias, taskHash, bodyStored: false }, liveCandidates.length);
|
|
503
|
+
const target = liveCandidates[0];
|
|
476
504
|
const targetReachableStatus = zpeerReachableStatus(target.peer);
|
|
477
505
|
if (targetReachableStatus !== "online") return finish("attempt", { status: "blocked", reason: `peer @${targetAlias} is ${targetReachableStatus}`, targetAlias, taskHash, bodyStored: false }, 1);
|
|
478
506
|
const topologyBlocker = validateZpeerTopology(repoRoot, self, target.peer, roomId, senderAlias, target.membership.alias);
|
|
@@ -63,6 +63,7 @@ function summarizeZpeerRooms(peers: Array<Record<string, unknown>>): Array<Recor
|
|
|
63
63
|
}
|
|
64
64
|
return [...rooms.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([roomId, roomPeers]) => {
|
|
65
65
|
const aliases = roomPeers.map((entry) => entry.alias).filter((alias): alias is string => Boolean(alias)).sort();
|
|
66
|
+
const onlineAliases = roomPeers.filter((entry) => entry.peer.status === "online").map((entry) => entry.alias).filter((alias): alias is string => Boolean(alias)).sort();
|
|
66
67
|
const sessionHashes = roomPeers.map((entry) => typeof entry.peer.sessionHash === "string" ? entry.peer.sessionHash : undefined).filter((sessionHash): sessionHash is string => Boolean(sessionHash));
|
|
67
68
|
return {
|
|
68
69
|
schema: "zob.zpeer-room-summary.v1",
|
|
@@ -73,7 +74,9 @@ function summarizeZpeerRooms(peers: Array<Record<string, unknown>>): Array<Recor
|
|
|
73
74
|
stale: roomPeers.filter((entry) => entry.peer.status === "stale").length,
|
|
74
75
|
offline: roomPeers.filter((entry) => entry.peer.status === "offline").length,
|
|
75
76
|
aliasHashes: aliases.map((alias) => sha256(alias)),
|
|
76
|
-
|
|
77
|
+
onlineAliasHashes: onlineAliases.map((alias) => sha256(alias)),
|
|
78
|
+
duplicateAliasHashes: onlineAliases.filter((alias, index) => onlineAliases.indexOf(alias) !== index).filter((alias, index, all) => all.indexOf(alias) === index).map((alias) => sha256(alias)),
|
|
79
|
+
duplicateAliasScope: "online_only",
|
|
77
80
|
localOnly: true,
|
|
78
81
|
networkEnabled: false,
|
|
79
82
|
bodyStored: false,
|
|
@@ -1032,9 +1032,9 @@ export function registerHarnessCommands(pi: ExtensionAPI, state: HarnessRuntimeS
|
|
|
1032
1032
|
});
|
|
1033
1033
|
emitZpeerEvent({ kind: "status", roomId: summary.roomId, fromAlias: summary.selfAlias, status: `online=${summary.online}/${summary.peerCount}`, reason: `stale=${summary.stale} offline=${summary.offline}` });
|
|
1034
1034
|
renderHarnessWidget(pi, state, ctx);
|
|
1035
|
-
const availableAliases = summary.
|
|
1035
|
+
const availableAliases = summary.onlineAliases.filter((alias) => alias !== summary.selfAlias).map((alias) => `@${alias}`).join(", ") || "none";
|
|
1036
1036
|
const unavailable = summary.stale + summary.offline;
|
|
1037
|
-
ctx.ui.notify(`zpeer room=${summary.roomId} memberships=${summary.membershipCount ?? zpeerMembershipsForPeer(self).length} self=@${summary.selfAlias ?? "?"} onlinePeers=${Math.max(0, summary.online - 1)} unavailable=${unavailable}
|
|
1037
|
+
ctx.ui.notify(`zpeer room=${summary.roomId} memberships=${summary.membershipCount ?? zpeerMembershipsForPeer(self).length} self=@${summary.selfAlias ?? "?"} onlinePeers=${Math.max(0, summary.online - 1)} unavailable=${unavailable} livePeers=${availableAliases} · usage: /zpeer @alias <prompt> | /zpeer in <room> @alias <prompt> · safety: local-only/hash-only/bodyStored=false`, "info");
|
|
1038
1038
|
return;
|
|
1039
1039
|
}
|
|
1040
1040
|
const parts = trimmed.split(/\s+/);
|
|
@@ -8,7 +8,7 @@ import { buildZobLiveAckEnvelope, buildZobLiveErrorEnvelope, buildZobLivePongEnv
|
|
|
8
8
|
import { appendLiveCompletedRef } from "../domains/coms/coms-v2/ledger-bridge.js";
|
|
9
9
|
import { bindZobLocalEndpoint, makeZobLocalEndpoint, sendZobLocalEnvelope } from "../domains/coms/coms-v2/local-transport.js";
|
|
10
10
|
import { readZobComsV2Policy } from "../domains/coms/coms-v2/policy.js";
|
|
11
|
-
import { registerCurrentZobLivePeer, touchCurrentZobLivePeer, unregisterCurrentZobLivePeer, writeZobLivePeerCard } from "../domains/coms/coms-v2/registry.js";
|
|
11
|
+
import { pruneExpiredZobLivePeers, registerCurrentZobLivePeer, touchCurrentZobLivePeer, unregisterCurrentZobLivePeer, writeZobLivePeerCard } from "../domains/coms/coms-v2/registry.js";
|
|
12
12
|
import { clearZpeerNewCarryoverProfile, readZpeerLocalProfile, readZpeerNewCarryoverProfile, writeZpeerLocalProfileFromPeer, writeZpeerNewCarryoverProfile, zpeerProfileIdIsSharedFallback } from "../domains/coms/coms-v2/zpeer-profile.js";
|
|
13
13
|
import { buildZpeerPeerRoomSummaries, ensureZpeerFields, refreshZpeerSelf } from "../domains/coms/coms-v2/zpeer.js";
|
|
14
14
|
import type { ZpeerRoomMembership } from "../domains/coms/coms-v2/types.js";
|
|
@@ -317,16 +317,16 @@ function buildZpeerAwarenessPrompt(state: HarnessRuntimeState, repoRoot: string)
|
|
|
317
317
|
const memberships = (state.zobLive.peerCard.zpeerMemberships?.length ?? summaries.length) || 1;
|
|
318
318
|
const roomLines = summaries.slice(0, 6).map((summary) => {
|
|
319
319
|
const selfAlias = summary.selfAlias ?? "?";
|
|
320
|
-
const peerAliases = summary.
|
|
320
|
+
const peerAliases = summary.onlineAliases.filter((alias) => alias !== selfAlias).slice(0, 6).map((alias) => `@${alias}`);
|
|
321
321
|
const unavailable = summary.stale + summary.offline;
|
|
322
|
-
const duplicateText = summary.duplicateAliases.length > 0 ? `
|
|
322
|
+
const duplicateText = summary.duplicateAliases.length > 0 ? ` liveDuplicates=${summary.duplicateAliases.map((alias) => `@${alias}`).join(",")}` : "";
|
|
323
323
|
return ` - ${summary.active ? "*" : " "} ${summary.roomId}: self=@${selfAlias}; online=${peerAliases.join(",") || "none"}; unavailable=${unavailable} (stale=${summary.stale}, offline=${summary.offline})${duplicateText}`;
|
|
324
324
|
});
|
|
325
325
|
if (summaries.length > 6) roomLines.push(` - +${summaries.length - 6} more room${summaries.length - 6 === 1 ? "" : "s"}`);
|
|
326
326
|
const activeSelfAlias = activeSummary?.selfAlias ?? "?";
|
|
327
|
-
const activePeerAliases = (activeSummary?.
|
|
327
|
+
const activePeerAliases = (activeSummary?.onlineAliases ?? []).filter((alias) => alias !== activeSelfAlias).slice(0, 8).map((alias) => `@${alias}`);
|
|
328
328
|
const activeUnavailable = (activeSummary?.stale ?? 0) + (activeSummary?.offline ?? 0);
|
|
329
|
-
const activeDuplicateLine = activeSummary && activeSummary.duplicateAliases.length > 0 ? `\n- duplicate aliases: ${activeSummary.duplicateAliases.map((alias) => `@${alias}`).join(", ")}` : "";
|
|
329
|
+
const activeDuplicateLine = activeSummary && activeSummary.duplicateAliases.length > 0 ? `\n- duplicate live aliases: ${activeSummary.duplicateAliases.map((alias) => `@${alias}`).join(", ")}` : "";
|
|
330
330
|
return `\n\nZPEER AWARENESS (transient, rebuilt each turn)\n- active room: ${activeSummary?.roomId ?? "default"}\n- memberships: ${memberships}\n- self: @${activeSelfAlias}\n- online peers: ${activePeerAliases.join(", ") || "none"}\n- unavailable peers: ${activeUnavailable} (stale=${activeSummary?.stale ?? 0}, offline=${activeSummary?.offline ?? 0})${activeDuplicateLine}\n- rooms:\n${roomLines.join("\n") || " - none"}\n- Use zpeer_ask with explicit roomId when targeting a non-active room.\n- posture: local_socket-only, room-scoped, hash-only durable ledgers, bodyStored=false, networkEnabled=false\n- For non-trivial review/debug/planning peer coordination, agents may use zpeer_ask with mode=\"async\" so the request is visible, governed, and non-blocking; /zpeer remains the interactive command path.\n- Passive wait rule: if the only remaining action is waiting for ZPeer/coms replies, stop the turn and remain idle; do not poll, call tools, or continue just to wait.\n- Use ZPeer only when useful or user-requested; avoid spam, duplicate asks, and reply loops; do not use it for hidden free chat or to bypass topology/safety gates.\n- Raw ZPeer bodies are transient; durable records must remain hash-only/bodyStored=false.\n- last ZPeer event: ${formatZpeerLastEvent(state.zobLive.lastEvent)}`;
|
|
331
331
|
}
|
|
332
332
|
|
|
@@ -389,6 +389,7 @@ function handleSameAgentModeIntent(pi: ExtensionAPI, state: HarnessRuntimeState,
|
|
|
389
389
|
async function startOrRefreshZobLiveRuntime(pi: ExtensionAPI, state: HarnessRuntimeState, ctx: ExtensionContext): Promise<void> {
|
|
390
390
|
const repoRoot = ctx.cwd;
|
|
391
391
|
const profileId = zpeerRuntimeProfileId(ctx);
|
|
392
|
+
try { pruneExpiredZobLivePeers(repoRoot); } catch { /* best-effort ghost peer cleanup; live runtime must remain available */ }
|
|
392
393
|
const policy = readZobComsV2Policy(repoRoot);
|
|
393
394
|
if (policy.mode === "off" || policy.mode === "required_network") {
|
|
394
395
|
safelyUpdateZobLivePeer(repoRoot, state.zobLive.peerCard ? "touch" : "register");
|
|
@@ -353,8 +353,8 @@ export function renderHarnessWidget(pi: ExtensionAPI, state: HarnessRuntimeState
|
|
|
353
353
|
const marker = summary.active ? "*" : " ";
|
|
354
354
|
const selfAlias = `@${summary.selfAlias ?? "?"}`;
|
|
355
355
|
const peerState = `${summary.online}/${summary.peerCount}${summary.stale > 0 ? ` s${summary.stale}` : ""}${summary.offline > 0 ? ` off${summary.offline}` : ""}`;
|
|
356
|
-
const peerAliases = summary.
|
|
357
|
-
const aliasOverflow = Math.max(0, summary.
|
|
356
|
+
const peerAliases = summary.onlineAliases.filter((alias) => alias !== summary.selfAlias).slice(0, 2).map((alias) => `@${alias}`);
|
|
357
|
+
const aliasOverflow = Math.max(0, summary.onlineAliases.length - (summary.selfAlias && summary.onlineAliases.includes(summary.selfAlias) ? 1 : 0) - peerAliases.length);
|
|
358
358
|
const aliasText = peerAliases.length > 0 ? `${peerAliases.join(" ")}${aliasOverflow > 0 ? ` +${aliasOverflow}` : ""}` : "no peers";
|
|
359
359
|
return theme.fg("muted", truncateToWidth(`${marker} ${summary.roomId} ${selfAlias} ${peerState} ${aliasText}`, 52, "…"));
|
|
360
360
|
});
|
|
@@ -8,15 +8,18 @@ description: Use when launching, running, reviewing, or extending the Agentic Sp
|
|
|
8
8
|
|
|
9
9
|
Use this skill to run a reusable ZOB-native spec production workflow. It ingests a mission plus explicit source paths, coordinates a run-scoped ZTeam, asks the human only through `spec-chief`, and produces a detailed, testable, traceable implementation spec.
|
|
10
10
|
|
|
11
|
-
V1 packaging is composite and deliberately **not** a runtime extension:
|
|
11
|
+
V1 packaging is composite and deliberately **not** a runtime extension. It is a specialized Agent Factory pattern:
|
|
12
12
|
|
|
13
13
|
```text
|
|
14
14
|
Skill = rules and operating contract
|
|
15
15
|
Factory = repeatable checkpoints, sentinels, validators
|
|
16
16
|
ZTeam/ZAgents = run-scoped human-facing team
|
|
17
17
|
CLI = scriptable entrypoint and tmux automation
|
|
18
|
+
Coms = parent-visible async asks, blockers, evidence refs, and oracle/no-ship routing
|
|
18
19
|
```
|
|
19
20
|
|
|
21
|
+
Use the generic `examples/agent-factory-tmux-comms/` pattern for a smaller teaching version of the same shape.
|
|
22
|
+
|
|
20
23
|
## Entry points
|
|
21
24
|
|
|
22
25
|
```bash
|
|
@@ -30,6 +30,17 @@ ZOB coms live transport may be transient, but ZOB audit must stay metadata-only.
|
|
|
30
30
|
- No silent fallback from required live delivery to append-only success.
|
|
31
31
|
- No token/secret logging.
|
|
32
32
|
|
|
33
|
+
## Tmux Agent Factory safety
|
|
34
|
+
|
|
35
|
+
For tmux-backed ZAgent teams:
|
|
36
|
+
|
|
37
|
+
- Tmux is a local launch/observation surface, not the durable source of truth.
|
|
38
|
+
- Startup kickoff files are allowed when they stay bounded and avoid secrets/raw private dumps; post-start pane paste is not reliable proof of delivery.
|
|
39
|
+
- Pane capture may be useful for live diagnosis, but do not persist captured prompt/output bodies into ledgers, reports, Mission Control, or skills.
|
|
40
|
+
- Supervisors/watchdogs must be bounded and local-only; they may detect stale windows or nudge a specific owed-response agent only with hash/body-free durable records.
|
|
41
|
+
- Team communication must remain parent-visible. Prefer one `control` room with lanes/tags/artifact sections unless a bounded owner-approved route exists.
|
|
42
|
+
- Completion still requires artifacts, validation evidence, and oracle/no-ship review when applicable; an active tmux session or chat reply is not completion evidence.
|
|
43
|
+
|
|
33
44
|
## Goal Room and owner-pool safety
|
|
34
45
|
|
|
35
46
|
For parallel owner micro-worker pools:
|
|
@@ -48,6 +59,8 @@ No-ship if:
|
|
|
48
59
|
- live send succeeds while receiver is absent/stale/offline;
|
|
49
60
|
- await treats timeout/stale/offline as success;
|
|
50
61
|
- hidden worker-to-worker free chat works outside a typed parent-visible Goal Room;
|
|
62
|
+
- tmux pane paste/capture is treated as reliable durable communication or completion evidence;
|
|
63
|
+
- a supervisor/watchdog nudges indefinitely, starts broad work, or stores raw pane bodies;
|
|
51
64
|
- a non-owner writes owned paths instead of sending an owner request;
|
|
52
65
|
- a worker applies or merges directly into the main workspace;
|
|
53
66
|
- network starts without explicit auth/locality policy;
|
|
@@ -35,6 +35,17 @@ Use this skill when:
|
|
|
35
35
|
- Do not bypass topology guards.
|
|
36
36
|
- Do not enable network transport without explicit auth/locality policy.
|
|
37
37
|
|
|
38
|
+
## Tmux/ZAgent communication pattern
|
|
39
|
+
|
|
40
|
+
For tmux-backed Agent Factory teams:
|
|
41
|
+
|
|
42
|
+
- Treat each tmux window as a local Pi/ZAgent session with its own identity and room membership.
|
|
43
|
+
- Use tmux only to start, attach, inspect status, or close the local session; do not use pane paste as the primary communication transport.
|
|
44
|
+
- Prefer startup files (`pi @chief-kickoff.md`, `pi @worker-kickoff.md`) for initial instructions so Pi receives one bounded message block.
|
|
45
|
+
- Use async ZPeer/Goal Room-style asks for normal coordination; do not block in polling loops after sending a non-blocking ask.
|
|
46
|
+
- Keep messages short and actionable with `CONTEXT`, `ASK`, `EVIDENCE`, `URGENCY`, and `BLOCKER` fields.
|
|
47
|
+
- When a reply affects scope, ownership, merge readiness, oracle status, or completion, mirror only body-free metadata/artifact refs into durable records.
|
|
48
|
+
|
|
38
49
|
## Expected pattern
|
|
39
50
|
|
|
40
51
|
```text
|
|
@@ -17,6 +17,19 @@ Use when the task is repeated, batchable, or should become a reusable system rat
|
|
|
17
17
|
5. Pilot: 10 items max with bounded concurrency.
|
|
18
18
|
6. Batch: only after pilot sentinel and oracle gate.
|
|
19
19
|
|
|
20
|
+
## Agent Factory shape
|
|
21
|
+
|
|
22
|
+
Some factories produce not only code or reports, but a supervised team workflow. Treat these as **Agent Factories**. A safe Agent Factory defines:
|
|
23
|
+
|
|
24
|
+
- ZAgent roles, prompts, default modes, and allowed authority;
|
|
25
|
+
- ZTeam topology, aliases, rooms, entry agent, and `parentVisible` communication policy;
|
|
26
|
+
- optional manual tmux launcher with start/attach/status/close only;
|
|
27
|
+
- startup kickoff templates or rendered kickoff files passed as `pi @kickoff.md`;
|
|
28
|
+
- run artifacts such as `run-manifest.json`, workgraph/status/iteration logs, readiness or kickoff-dispatch records;
|
|
29
|
+
- validation and oracle/no-ship gates before completion claims.
|
|
30
|
+
|
|
31
|
+
Do not treat launching a tmux team as factory success. Success comes from evidence artifacts, validators, and oracle review.
|
|
32
|
+
|
|
20
33
|
## Required artifacts
|
|
21
34
|
|
|
22
35
|
- `manifest.json`
|
|
@@ -26,3 +39,11 @@ Use when the task is repeated, batchable, or should become a reusable system rat
|
|
|
26
39
|
- `validation.json`
|
|
27
40
|
- phase sentinel (`SMOKE_PASSED.sentinel`, `PILOT_PASSED.sentinel`, or `BATCH_PASSED.sentinel`)
|
|
28
41
|
- `DONE.sentinel` only after validation passes
|
|
42
|
+
|
|
43
|
+
For Agent Factories, also prefer:
|
|
44
|
+
|
|
45
|
+
- team manifest or generated run-scoped team manifest
|
|
46
|
+
- kickoff templates or rendered startup kickoff files
|
|
47
|
+
- `autonomous-workgraph.md` / `autonomous-status.md` / `iteration-log.md`
|
|
48
|
+
- `kickoff-dispatch.json` or equivalent body-free startup proof
|
|
49
|
+
- communication protocol and no-ship policy
|
|
@@ -9,12 +9,26 @@ description: Use when working inside the ZOB Pi harness, designing agentic workf
|
|
|
9
9
|
Use this skill for any task involving:
|
|
10
10
|
- Pi extensions, prompt templates, skills, or agent definitions.
|
|
11
11
|
- Multi-agent delegation workflows.
|
|
12
|
+
- Agent Factory design: ZAgents, ZTeams, tmux launchers, kickoff files, visible coms, workgraphs, and oracle gates.
|
|
12
13
|
- Safety gates and damage-control policy.
|
|
13
14
|
- Software-factory design from repeated manual workflows.
|
|
14
15
|
- Runtime tool/command routing via `.pi/capabilities/zob-public-runtime-capabilities.json`.
|
|
15
16
|
|
|
16
17
|
For routing behavior, load `zob-tool-router` before non-trivial or tool-ambiguous work. For compaction/recovery behavior, load `zob-compaction-policy` before changing compaction hooks or resuming from a compacted long-running goal. For domain behavior, load the domain skill named by the registry instead of inlining details here: `zob-goal-todo-tree`, `zob-coms-v2-live`, `zob-coms-safety`, `zob-mission-control-coms`, `zob-autonomous-runtime`, `zob-factory`, `zob-sandbox`, `zob-oracle`, or `zob-spec` as applicable.
|
|
17
18
|
|
|
19
|
+
## Agent Factory posture
|
|
20
|
+
|
|
21
|
+
Treat ZOB as a governed Agent Factory when the owner wants persistent local agent roles rather than one transient assistant. An Agent Factory may include:
|
|
22
|
+
|
|
23
|
+
- ZAgent role definitions and prompts;
|
|
24
|
+
- ZTeam topology, rooms, aliases, entry agent, and communication policy;
|
|
25
|
+
- optional tmux launchers for manual local startup/attach/status/close;
|
|
26
|
+
- startup kickoff files passed as `pi @kickoff.md`, not post-start pane paste as the primary transport;
|
|
27
|
+
- run artifacts such as `run-manifest.json`, workgraph/status/iteration logs, validation reports, and `kickoff-dispatch.json`;
|
|
28
|
+
- oracle/no-ship gates before completion claims.
|
|
29
|
+
|
|
30
|
+
Communication is a core deliverable. Prefer one parent-visible control room by default; use lanes/tags/artifact sections instead of hidden worker rooms unless the owner explicitly approves a bounded route. The generic teaching example lives in `examples/agent-factory-tmux-comms/`.
|
|
31
|
+
|
|
18
32
|
## Operating model
|
|
19
33
|
|
|
20
34
|
1. Classify the task as one of: `explore`, `plan`, `implement`, `oracle`, `factory`, `orchestrator`.
|
|
@@ -40,10 +40,18 @@ Generated definitions must stay project-local and are not harness-global:
|
|
|
40
40
|
|
|
41
41
|
Never write generated ZAgent, ZTeam, prompt, or tmux launcher artifacts outside those directories unless the owner explicitly provides a different project-local allowed path.
|
|
42
42
|
|
|
43
|
+
## Documentation examples vs active project teams
|
|
44
|
+
|
|
45
|
+
When creating documentation-only Agent Factory examples, write them under an explicit example path such as `examples/agent-factory-tmux-comms/` and mark them as inert/example-only. Do not place example manifests under `.pi/zagents/` or `.pi/zteams/` unless the owner is asking to create an active project-local team.
|
|
46
|
+
|
|
47
|
+
Example files may illustrate a team manifest, manual tmux launcher, and kickoff templates, but they must state that real activation requires owner review and adaptation into `.pi/zagents/`, `.pi/zagents/prompts/`, and `.pi/zteams/`.
|
|
48
|
+
|
|
43
49
|
## Optional tmux launcher mode
|
|
44
50
|
|
|
45
51
|
When the owner starts or qualifies the natural-language request with `tmux`, generate a project-local tmux launcher script alongside the generated ZTeam manifests. This is a convenience artifact only: the assistant must write the script and report manual commands, but must not run tmux, start Pi sessions, attach to tmux, or close tmux sessions automatically.
|
|
46
52
|
|
|
53
|
+
For Agent Factory launchers, prefer startup kickoff files (`pi @chief-kickoff.md`, `pi @worker-kickoff.md`) over post-start tmux pane paste. Tmux is a local launch/observation wrapper; communication and durable evidence still belong to ZPeer/Goal Room-style visible coordination and run artifacts.
|
|
54
|
+
|
|
47
55
|
Accepted owner request patterns include:
|
|
48
56
|
|
|
49
57
|
- `/skill:zob-zagent-creator tmux ...`
|
|
@@ -292,6 +300,7 @@ ZAgent manifest mode shape:
|
|
|
292
300
|
- When the ask mentions model choice or cost/quality tradeoffs, read the model catalog/routing files and record a justified per-ZAgent `model` plus metadata instead of guessing.
|
|
293
301
|
- Keep definitions minimal, auditable, and project-local.
|
|
294
302
|
- When generating tmux launchers, treat multi-team requests as bundles, deduplicate shared agents by `zagentId`, and document included teams, unique agents, and bridge/shared agents.
|
|
303
|
+
- For Agent Factory teams, include or reference the communication policy: `parentVisible: true`, `hiddenPeerChat: false`, `bodyStored: false`, `networkEnabled: false`, and owner/oracle gates for completion.
|
|
295
304
|
- Preserve existing runtime code and safety policy unless the owner explicitly asks for a separate implementation task.
|
|
296
305
|
- Ask for clarification when authority, launch conditions, write permissions, or external access are ambiguous.
|
|
297
306
|
|
|
@@ -303,6 +312,7 @@ ZAgent manifest mode shape:
|
|
|
303
312
|
- Do not create manifests, prompts, or tmux launchers outside `.pi/zagents/`, `.pi/zagents/prompts/`, or `.pi/zteams/`.
|
|
304
313
|
- Do not grant broad filesystem, network, browser, secret, commit, push, or destructive-command authority by default.
|
|
305
314
|
- Do not generate tmux launchers that duplicate shared ZAgents per team, use `killall`, broad process kills, install daemons, access credentials, or perform global cleanup.
|
|
315
|
+
- Do not present a tmux launcher or kickoff template as proof that agents launched, communicated, validated, or completed work.
|
|
306
316
|
- Do not enable live/global model routing or store provider credentials/API keys while selecting ZAgent models.
|
|
307
317
|
- Do not choose `vanilla` as a default mode unless the owner explicitly requested vanilla/base Pi/direct unrestricted behavior.
|
|
308
318
|
- Do not treat ZAgent creation as delivery success for live communication or mission execution.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "zob.zagent.v1",
|
|
3
|
+
"id": "agent-factory-pacman-chief",
|
|
4
|
+
"team": "agent-factory-pacman-multiplayer",
|
|
5
|
+
"role": "orchestrator",
|
|
6
|
+
"alias": "pacman_chief",
|
|
7
|
+
"description": "Owner-facing chief for the generative Pac-Man multiplayer Agent Factory demo; coordinates workers, workgraph, communication, and final synthesis.",
|
|
8
|
+
"promptRef": ".pi/zagents/prompts/agent-factory-pacman-chief.md",
|
|
9
|
+
"defaultMode": "orchestrator",
|
|
10
|
+
"defaultRoom": "pacman-factory",
|
|
11
|
+
"activeRoom": "pacman-factory",
|
|
12
|
+
"rooms": [{ "id": "pacman-factory", "alias": "pacman_chief", "role": "lead", "active": true }],
|
|
13
|
+
"allowedTools": ["read", "bash", "write", "edit", "zpeer_ask"],
|
|
14
|
+
"allowedPaths": ["examples/agent-factory-pacman-multiplayer/", "reports/agent-factory-pacman-runs/", ".pi/zteams/agent-factory-pacman-multiplayer.json", ".pi/zteams/agent-factory-pacman-multiplayer.tmux.sh", ".pi/zteams/agent-factory-pacman-multiplayer-runtime.mjs", ".pi/zteams/templates/agent-factory-pacman-*.template.md"],
|
|
15
|
+
"forbiddenPaths": [".env", ".env.*", "node_modules/", "dist/", "build/", "coverage/", ".git/", ".pi/sessions/", ".pi/agent-sessions/", ".pi/coms/"],
|
|
16
|
+
"approvalGates": { "externalAccess": "human", "externalWrite": "human+oracle", "commitOrPush": "human" },
|
|
17
|
+
"communicationPolicy": { "zpeerContact": true, "allowedRooms": ["pacman-factory"], "parentVisible": true, "hiddenPeerChat": false, "bodyStored": false },
|
|
18
|
+
"localOnly": true,
|
|
19
|
+
"networkEnabled": false,
|
|
20
|
+
"bodyStored": false,
|
|
21
|
+
"metadata": { "modeSelection": { "reason": "Chief coordinates the autonomous team and workgraph.", "authorityNote": "defaultMode sets initial posture only and does not expand permissions." } }
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "zob.zagent.v1",
|
|
3
|
+
"id": "agent-factory-pacman-engine-builder",
|
|
4
|
+
"team": "agent-factory-pacman-multiplayer",
|
|
5
|
+
"role": "engine-builder",
|
|
6
|
+
"alias": "engine_builder",
|
|
7
|
+
"description": "Generates the deterministic TypeScript Pac-Man game engine, movement/collision/scoring logic, and tests under the run project directory.",
|
|
8
|
+
"promptRef": ".pi/zagents/prompts/agent-factory-pacman-engine-builder.md",
|
|
9
|
+
"defaultMode": "implement",
|
|
10
|
+
"defaultRoom": "pacman-factory",
|
|
11
|
+
"activeRoom": "pacman-factory",
|
|
12
|
+
"rooms": [{ "id": "pacman-factory", "alias": "engine_builder", "role": "engine", "active": true }],
|
|
13
|
+
"allowedTools": ["read", "bash", "write", "edit", "zpeer_ask"],
|
|
14
|
+
"allowedPaths": ["examples/agent-factory-pacman-multiplayer/", "reports/agent-factory-pacman-runs/"],
|
|
15
|
+
"forbiddenPaths": [".env", ".env.*", "node_modules/", "dist/", "build/", "coverage/", ".git/", ".pi/sessions/", ".pi/agent-sessions/", ".pi/coms/"],
|
|
16
|
+
"approvalGates": { "externalAccess": "human", "externalWrite": "human+oracle", "commitOrPush": "human" },
|
|
17
|
+
"communicationPolicy": { "zpeerContact": true, "allowedRooms": ["pacman-factory"], "parentVisible": true, "hiddenPeerChat": false, "bodyStored": false },
|
|
18
|
+
"localOnly": true,
|
|
19
|
+
"networkEnabled": false,
|
|
20
|
+
"bodyStored": false
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "zob.zagent.v1",
|
|
3
|
+
"id": "agent-factory-pacman-frontend-builder",
|
|
4
|
+
"team": "agent-factory-pacman-multiplayer",
|
|
5
|
+
"role": "frontend-builder",
|
|
6
|
+
"alias": "frontend_builder",
|
|
7
|
+
"description": "Generates the browser game UI, rendering, local multiplayer controls, HUD, and launcher under the run project directory.",
|
|
8
|
+
"promptRef": ".pi/zagents/prompts/agent-factory-pacman-frontend-builder.md",
|
|
9
|
+
"defaultMode": "implement",
|
|
10
|
+
"defaultRoom": "pacman-factory",
|
|
11
|
+
"activeRoom": "pacman-factory",
|
|
12
|
+
"rooms": [{ "id": "pacman-factory", "alias": "frontend_builder", "role": "frontend", "active": true }],
|
|
13
|
+
"allowedTools": ["read", "bash", "write", "edit", "zpeer_ask"],
|
|
14
|
+
"allowedPaths": ["examples/agent-factory-pacman-multiplayer/", "reports/agent-factory-pacman-runs/"],
|
|
15
|
+
"forbiddenPaths": [".env", ".env.*", "node_modules/", "dist/", "build/", "coverage/", ".git/", ".pi/sessions/", ".pi/agent-sessions/", ".pi/coms/"],
|
|
16
|
+
"approvalGates": { "externalAccess": "human", "externalWrite": "human+oracle", "commitOrPush": "human" },
|
|
17
|
+
"communicationPolicy": { "zpeerContact": true, "allowedRooms": ["pacman-factory"], "parentVisible": true, "hiddenPeerChat": false, "bodyStored": false },
|
|
18
|
+
"localOnly": true,
|
|
19
|
+
"networkEnabled": false,
|
|
20
|
+
"bodyStored": false
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "zob.zagent.v1",
|
|
3
|
+
"id": "agent-factory-pacman-game-architect",
|
|
4
|
+
"team": "agent-factory-pacman-multiplayer",
|
|
5
|
+
"role": "game-architect",
|
|
6
|
+
"alias": "game_architect",
|
|
7
|
+
"description": "Designs the generated Pac-Man game architecture, engine/UI boundaries, state contracts, validation ladder, and implementation order.",
|
|
8
|
+
"promptRef": ".pi/zagents/prompts/agent-factory-pacman-game-architect.md",
|
|
9
|
+
"defaultMode": "plan",
|
|
10
|
+
"defaultRoom": "pacman-factory",
|
|
11
|
+
"activeRoom": "pacman-factory",
|
|
12
|
+
"rooms": [{ "id": "pacman-factory", "alias": "game_architect", "role": "architecture", "active": true }],
|
|
13
|
+
"allowedTools": ["read", "bash", "write", "edit", "zpeer_ask"],
|
|
14
|
+
"allowedPaths": ["examples/agent-factory-pacman-multiplayer/", "reports/agent-factory-pacman-runs/"],
|
|
15
|
+
"forbiddenPaths": [".env", ".env.*", "node_modules/", "dist/", "build/", "coverage/", ".git/", ".pi/sessions/", ".pi/agent-sessions/", ".pi/coms/"],
|
|
16
|
+
"approvalGates": { "externalAccess": "human", "externalWrite": "human+oracle", "commitOrPush": "human" },
|
|
17
|
+
"communicationPolicy": { "zpeerContact": true, "allowedRooms": ["pacman-factory"], "parentVisible": true, "hiddenPeerChat": false, "bodyStored": false },
|
|
18
|
+
"localOnly": true,
|
|
19
|
+
"networkEnabled": false,
|
|
20
|
+
"bodyStored": false
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "zob.zagent.v1",
|
|
3
|
+
"id": "agent-factory-pacman-game-designer",
|
|
4
|
+
"team": "agent-factory-pacman-multiplayer",
|
|
5
|
+
"role": "game-designer",
|
|
6
|
+
"alias": "game_designer",
|
|
7
|
+
"description": "Defines Pac-Man multiplayer gameplay rules, controls, scoring, round flow, and playability acceptance criteria.",
|
|
8
|
+
"promptRef": ".pi/zagents/prompts/agent-factory-pacman-game-designer.md",
|
|
9
|
+
"defaultMode": "plan",
|
|
10
|
+
"defaultRoom": "pacman-factory",
|
|
11
|
+
"activeRoom": "pacman-factory",
|
|
12
|
+
"rooms": [{ "id": "pacman-factory", "alias": "game_designer", "role": "game_design", "active": true }],
|
|
13
|
+
"allowedTools": ["read", "bash", "write", "edit", "zpeer_ask"],
|
|
14
|
+
"allowedPaths": ["examples/agent-factory-pacman-multiplayer/", "reports/agent-factory-pacman-runs/"],
|
|
15
|
+
"forbiddenPaths": [".env", ".env.*", "node_modules/", "dist/", "build/", "coverage/", ".git/", ".pi/sessions/", ".pi/agent-sessions/", ".pi/coms/"],
|
|
16
|
+
"approvalGates": { "externalAccess": "human", "externalWrite": "human+oracle", "commitOrPush": "human" },
|
|
17
|
+
"communicationPolicy": { "zpeerContact": true, "allowedRooms": ["pacman-factory"], "parentVisible": true, "hiddenPeerChat": false, "bodyStored": false },
|
|
18
|
+
"localOnly": true,
|
|
19
|
+
"networkEnabled": false,
|
|
20
|
+
"bodyStored": false
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "zob.zagent.v1",
|
|
3
|
+
"id": "agent-factory-pacman-qa-oracle",
|
|
4
|
+
"team": "agent-factory-pacman-multiplayer",
|
|
5
|
+
"role": "qa-oracle",
|
|
6
|
+
"alias": "qa_oracle",
|
|
7
|
+
"description": "Skeptically validates generated Pac-Man multiplayer project playability, tests, structure, safety, and no-ship posture.",
|
|
8
|
+
"promptRef": ".pi/zagents/prompts/agent-factory-pacman-qa-oracle.md",
|
|
9
|
+
"defaultMode": "oracle",
|
|
10
|
+
"defaultRoom": "pacman-factory",
|
|
11
|
+
"activeRoom": "pacman-factory",
|
|
12
|
+
"rooms": [{ "id": "pacman-factory", "alias": "qa_oracle", "role": "oracle", "active": true }],
|
|
13
|
+
"allowedTools": ["read", "bash", "zpeer_ask"],
|
|
14
|
+
"allowedPaths": ["examples/agent-factory-pacman-multiplayer/", "reports/agent-factory-pacman-runs/", ".pi/zagents/agent-factory-pacman-*.json", ".pi/zteams/agent-factory-pacman-multiplayer.json", ".pi/zteams/agent-factory-pacman-multiplayer-runtime.mjs", ".pi/zteams/agent-factory-pacman-multiplayer.tmux.sh"],
|
|
15
|
+
"forbiddenPaths": [".env", ".env.*", "node_modules/", "dist/", "build/", "coverage/", ".git/", ".pi/sessions/", ".pi/agent-sessions/", ".pi/coms/"],
|
|
16
|
+
"approvalGates": { "externalAccess": "human", "externalWrite": "human+oracle", "commitOrPush": "human" },
|
|
17
|
+
"communicationPolicy": { "zpeerContact": true, "allowedRooms": ["pacman-factory"], "parentVisible": true, "hiddenPeerChat": false, "bodyStored": false },
|
|
18
|
+
"localOnly": true,
|
|
19
|
+
"networkEnabled": false,
|
|
20
|
+
"bodyStored": false
|
|
21
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Agent Factory Pac-Man Chief
|
|
2
|
+
|
|
3
|
+
You are `agent-factory-pacman-chief`, alias `pacman_chief`, the owner-facing coordinator for the Pac-Man multiplayer generative demo.
|
|
4
|
+
|
|
5
|
+
## Mission
|
|
6
|
+
|
|
7
|
+
Coordinate a local ZTeam that **generates** a playable Pac-Man-inspired multiplayer browser game under the run target:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
reports/agent-factory-pacman-runs/<run_id>/project/
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Do not build the game inside `examples/agent-factory-pacman-multiplayer/`; that folder is only the source brief.
|
|
14
|
+
|
|
15
|
+
## Required communication posture
|
|
16
|
+
|
|
17
|
+
Use only the parent-visible `pacman-factory` room. Communicate proactively. Do not wait silently.
|
|
18
|
+
|
|
19
|
+
Message shape:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
CONTEXT:
|
|
23
|
+
ASK:
|
|
24
|
+
EVIDENCE:
|
|
25
|
+
URGENCY:
|
|
26
|
+
BLOCKER:
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You must dispatch and track:
|
|
30
|
+
|
|
31
|
+
- gameplay questions to `@game_designer`;
|
|
32
|
+
- architecture decisions to `@game_architect`;
|
|
33
|
+
- engine/state/collision tasks to `@engine_builder`;
|
|
34
|
+
- rendering/input/HUD tasks to `@frontend_builder`;
|
|
35
|
+
- playability/no-ship/validation review to `@qa_oracle`.
|
|
36
|
+
|
|
37
|
+
## First turn
|
|
38
|
+
|
|
39
|
+
1. Read the kickoff file, mission, output contract, and run manifest.
|
|
40
|
+
2. Confirm `READY` in `pacman-factory`.
|
|
41
|
+
3. Update the run workgraph/status/iteration log.
|
|
42
|
+
4. Ask `@game_designer` for rules, controls, scoring, and acceptance criteria.
|
|
43
|
+
5. Ask `@game_architect` for engine/UI/state architecture and validation ladder.
|
|
44
|
+
6. Ask builders to wait for gameplay + architecture handoff before creating broad module boundaries.
|
|
45
|
+
7. Ask `@qa_oracle` for validation/no-ship criteria.
|
|
46
|
+
|
|
47
|
+
## Must not
|
|
48
|
+
|
|
49
|
+
- Do not commit/push/tag.
|
|
50
|
+
- Do not read secrets.
|
|
51
|
+
- Do not launch hidden rooms.
|
|
52
|
+
- Do not write generated game code outside the run `project/` directory.
|
|
53
|
+
- Do not claim completion without validation evidence and oracle review.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Agent Factory Pac-Man Engine Builder
|
|
2
|
+
|
|
3
|
+
You are `agent-factory-pacman-engine-builder`, alias `engine_builder`.
|
|
4
|
+
|
|
5
|
+
## Mission
|
|
6
|
+
|
|
7
|
+
Generate the deterministic TypeScript game engine for Pac-Man multiplayer under the run project directory:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
reports/agent-factory-pacman-runs/<run_id>/project/
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Deliverables
|
|
14
|
+
|
|
15
|
+
- Game state/types.
|
|
16
|
+
- Maze/grid movement and wall collision.
|
|
17
|
+
- Pellet/score handling.
|
|
18
|
+
- Player/ghost/obstacle collision rules.
|
|
19
|
+
- Round end/restart logic.
|
|
20
|
+
- Engine tests.
|
|
21
|
+
|
|
22
|
+
## Proactive communication
|
|
23
|
+
|
|
24
|
+
Ask `@game_architect` when the state model or contracts are unclear. Ask `@game_designer` when scoring/rules are unclear. Ask `@frontend_builder` before changing UI-facing state shapes. Ask `@qa_oracle` when validation coverage is ready or blocked.
|
|
25
|
+
|
|
26
|
+
Use:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
CONTEXT:
|
|
30
|
+
ASK:
|
|
31
|
+
EVIDENCE:
|
|
32
|
+
URGENCY:
|
|
33
|
+
BLOCKER:
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Must not
|
|
37
|
+
|
|
38
|
+
- Do not read secrets or raw session/coms bodies.
|
|
39
|
+
- Do not add external service requirements.
|
|
40
|
+
- Do not write generated game code outside the run `project/` directory.
|
|
41
|
+
- Do not claim completion without tests or explicit blocker evidence.
|