feat: add process registration protocol text builder

- Introduced a new function `buildProcessProtocolText` to generate context-free protocol text for background process registration.
- Updated the `protocols` interface to include the new function, promoting code reuse across member and lead prompts.
- Integrated the new protocol text into the team provisioning service for improved operational instructions.
- Enhanced type definitions to reflect the addition of the new protocol functionality.
This commit is contained in:
iliya 2026-03-18 09:36:16 +02:00
parent 6ace707653
commit 71fcf88a48
7 changed files with 42 additions and 4 deletions

View file

@ -38,6 +38,9 @@ module.exports = {
createController, createController,
createControllerContext, createControllerContext,
agentBlocks, agentBlocks,
protocols: {
buildProcessProtocolText: tasks.buildProcessProtocolText,
},
tasks, tasks,
kanban, kanban,
review, review,

View file

@ -443,8 +443,13 @@ function buildMemberTaskProtocol(teamName) {
Failure to follow this protocol means the task board will show incorrect status.`); Failure to follow this protocol means the task board will show incorrect status.`);
} }
function buildMemberProcessProtocol(teamName) { /**
return wrapAgentBlock(`BACKGROUND PROCESS REGISTRATION — when you start a background process (dev server, watcher, database, etc.): * Raw process-registration protocol text (no agent-block wrapping).
* Shared between member briefing and lead provisioning prompt (DRY).
* Context-free does NOT follow the (context, ...) convention.
*/
function buildProcessProtocolText(teamName) {
return `BACKGROUND PROCESS REGISTRATION — when you start a background process (dev server, watcher, database, etc.):
1. Launch with & to get PID: 1. Launch with & to get PID:
pnpm dev & pnpm dev &
2. Register immediately with MCP tool process_register (--port and --url are optional, use when the process listens on a port): 2. Register immediately with MCP tool process_register (--port and --url are optional, use when the process listens on a port):
@ -453,7 +458,11 @@ function buildMemberProcessProtocol(teamName) {
{ teamName: "${teamName}" } { teamName: "${teamName}" }
4. When stopping a process, use MCP tool process_stop: 4. When stopping a process, use MCP tool process_stop:
{ teamName: "${teamName}", pid: <PID> } { teamName: "${teamName}", pid: <PID> }
If verification in step 3 fails or the process is missing from the list, re-register it.`); If verification in step 3 fails or the process is missing from the list, re-register it.`;
}
function buildMemberProcessProtocol(teamName) {
return wrapAgentBlock(buildProcessProtocolText(teamName));
} }
function buildMemberFormattingProtocol() { function buildMemberFormattingProtocol() {
@ -593,6 +602,7 @@ module.exports = {
setTaskStatus, setTaskStatus,
softDeleteTask, softDeleteTask,
startTask, startTask,
buildProcessProtocolText,
memberBriefing, memberBriefing,
taskBriefing, taskBriefing,
unlinkTask, unlinkTask,

View file

@ -98,4 +98,11 @@ declare module 'agent-teams-controller' {
} }
export const agentBlocks: AgentBlocksApi; export const agentBlocks: AgentBlocksApi;
/** Context-free protocol text builders, shared across lead and member prompts. */
export interface ProtocolsApi {
buildProcessProtocolText(teamName: string): string;
}
export const protocols: ProtocolsApi;
} }

View file

@ -97,7 +97,7 @@ import type {
} from '@shared/types'; } from '@shared/types';
const logger = createLogger('Service:TeamProvisioning'); const logger = createLogger('Service:TeamProvisioning');
const { createController } = agentTeamsControllerModule; const { createController, protocols } = agentTeamsControllerModule;
const TEAM_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,127}$/; const TEAM_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,127}$/;
const RUN_TIMEOUT_MS = 300_000; const RUN_TIMEOUT_MS = 300_000;
const VERIFY_TIMEOUT_MS = 15_000; const VERIFY_TIMEOUT_MS = 15_000;
@ -569,6 +569,9 @@ function buildTeamCtlOpsInstructions(teamName: string, leadName: string): string
`- Request changes: review_request_changes { teamName: "${teamName}", taskId: "<id>", comment: "<what to fix>" }`, `- Request changes: review_request_changes { teamName: "${teamName}", taskId: "<id>", comment: "<what to fix>" }`,
`CRITICAL: Writing "approved" or "LGTM" as a task comment does NOT move the task on the kanban board. You MUST call the review_approve MCP tool. Without the tool call the task stays stuck in the REVIEW column.`, `CRITICAL: Writing "approved" or "LGTM" as a task comment does NOT move the task on the kanban board. You MUST call the review_approve MCP tool. Without the tool call the task stays stuck in the REVIEW column.`,
``, ``,
`Process operations — use MCP tools directly:`,
protocols.buildProcessProtocolText(teamName),
``,
`Attachment storage modes (IMPORTANT):`, `Attachment storage modes (IMPORTANT):`,
`- Default is copy (safe, robust).`, `- Default is copy (safe, robust).`,
`- Use mode: "link" to try a hardlink (no duplication). It may fall back to copy unless you disable fallback.`, `- Use mode: "link" to try a hardlink (no duplication). It may fall back to copy unless you disable fallback.`,

View file

@ -86,7 +86,14 @@ declare module 'agent-teams-controller' {
crossTeam: ControllerCrossTeamApi; crossTeam: ControllerCrossTeamApi;
} }
/** Context-free protocol text builders, shared across lead and member prompts. */
export interface ProtocolsApi {
buildProcessProtocolText(teamName: string): string;
}
export function createController(options: ControllerContextOptions): AgentTeamsController; export function createController(options: ControllerContextOptions): AgentTeamsController;
export const agentBlocks: AgentBlocksApi; export const agentBlocks: AgentBlocksApi;
export const protocols: ProtocolsApi;
} }

View file

@ -103,6 +103,10 @@ vi.mock('agent-teams-controller', () => ({
hoisted.sendInboxMessage(teamName, message), hoisted.sendInboxMessage(teamName, message),
}, },
}), }),
protocols: {
buildProcessProtocolText: (teamName: string) =>
`BACKGROUND PROCESS REGISTRATION (mock for ${teamName})`,
},
})); }));
import type { TeamChangeEvent } from '@shared/types/team'; import type { TeamChangeEvent } from '@shared/types/team';

View file

@ -121,6 +121,10 @@ vi.mock('agent-teams-controller', () => ({
hoisted.sendInboxMessage(teamName, message), hoisted.sendInboxMessage(teamName, message),
}, },
}), }),
protocols: {
buildProcessProtocolText: (teamName: string) =>
`BACKGROUND PROCESS REGISTRATION (mock for ${teamName})`,
},
})); }));
import { TeamProvisioningService } from '../../../../src/main/services/team/TeamProvisioningService'; import { TeamProvisioningService } from '../../../../src/main/services/team/TeamProvisioningService';