@grackle-ai/prompt 0.73.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/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # @grackle-ai/prompt
2
+
3
+ System prompt assembly and persona resolution for [Grackle](https://github.com/nick-pape/grackle) agent sessions.
4
+
5
+ ## Overview
6
+
7
+ This package prepares everything an agent session needs before it starts:
8
+
9
+ - **System prompt building** — `SystemPromptBuilder` assembles prompts from discrete sections based on session type (orchestrator, leaf task, or ad-hoc). Orchestrator tasks get project context, task trees, persona rosters, and decomposition guidelines. Leaf tasks get a completion contract.
10
+ - **Orchestrator context** — `fetchOrchestratorContext()` gathers all data needed for orchestrator prompts from the database: workspace metadata, task hierarchy, available personas/environments, and recent findings.
11
+ - **Persona resolution** — `resolvePersona()` resolves which persona to use via a cascade: request persona → task default → workspace default → app default.
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { SystemPromptBuilder, fetchOrchestratorContext, resolvePersona } from "@grackle-ai/prompt";
17
+
18
+ // Resolve the persona for a session
19
+ const resolved = resolvePersona(requestPersonaId, taskDefaultPersonaId, workspaceDefaultPersonaId);
20
+
21
+ // Fetch orchestrator context (for orchestrator tasks)
22
+ const context = fetchOrchestratorContext(workspaceId);
23
+
24
+ // Build the system prompt
25
+ const builder = new SystemPromptBuilder({
26
+ task: { title, description, notes },
27
+ personaPrompt: resolved.systemPrompt,
28
+ canDecompose: task.canDecompose,
29
+ ...context,
30
+ });
31
+ const systemPrompt = builder.build();
32
+ ```
@@ -0,0 +1,7 @@
1
+ export { SystemPromptBuilder, buildTaskPrompt } from "./system-prompt-builder.js";
2
+ export type { SystemPromptOptions, TaskTreeNode, PersonaSummary, EnvironmentSummary } from "./system-prompt-builder.js";
3
+ export { fetchOrchestratorContext } from "./orchestrator-context.js";
4
+ export type { OrchestratorContext } from "./orchestrator-context.js";
5
+ export { resolvePersona } from "./resolve-persona.js";
6
+ export type { ResolvedPersona } from "./resolve-persona.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClF,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGxH,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAGrE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // ─── System Prompt Builder ──────────────────────────────────
2
+ export { SystemPromptBuilder, buildTaskPrompt } from "./system-prompt-builder.js";
3
+ // ─── Orchestrator Context ───────────────────────────────────
4
+ export { fetchOrchestratorContext } from "./orchestrator-context.js";
5
+ // ─── Persona Resolution ────────────────────────────────────
6
+ export { resolvePersona } from "./resolve-persona.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAGlF,+DAA+D;AAC/D,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAGrE,8DAA8D;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Fetches and assembles all data needed for the orchestrator system prompt.
3
+ * Centralizes store reads so both gRPC and WebSocket call sites share
4
+ * the same data-fetching logic.
5
+ */
6
+ import type { TaskTreeNode, PersonaSummary, EnvironmentSummary } from "./system-prompt-builder.js";
7
+ /** Pre-fetched orchestrator data matching SystemPromptOptions fields. */
8
+ export interface OrchestratorContext {
9
+ /** Workspace metadata (undefined when workspace not found). */
10
+ workspace?: {
11
+ name: string;
12
+ description: string;
13
+ repoUrl: string;
14
+ };
15
+ /** All tasks in the workspace mapped to tree nodes. */
16
+ taskTree: TaskTreeNode[];
17
+ /** All available personas. */
18
+ availablePersonas: PersonaSummary[];
19
+ /** All available environments. */
20
+ availableEnvironments: EnvironmentSummary[];
21
+ /** Pre-built findings context string. */
22
+ findingsContext: string;
23
+ }
24
+ /**
25
+ * Fetch all data needed for an orchestrator system prompt.
26
+ *
27
+ * @param workspaceId - The workspace to scope task/findings queries to.
28
+ * @returns Data ready to spread into SystemPromptOptions.
29
+ */
30
+ export declare function fetchOrchestratorContext(workspaceId: string): OrchestratorContext | undefined;
31
+ //# sourceMappingURL=orchestrator-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator-context.d.ts","sourceRoot":"","sources":["../src/orchestrator-context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGnG,yEAAyE;AACzE,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,uDAAuD;IACvD,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,8BAA8B;IAC9B,iBAAiB,EAAE,cAAc,EAAE,CAAC;IACpC,kCAAkC;IAClC,qBAAqB,EAAE,kBAAkB,EAAE,CAAC;IAC5C,yCAAyC;IACzC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CA8D7F"}
@@ -0,0 +1,90 @@
1
+ import { taskStore, personaStore, envRegistry, findingStore, workspaceStore, safeParseJsonArray } from "@grackle-ai/database";
2
+ /**
3
+ * Fetch all data needed for an orchestrator system prompt.
4
+ *
5
+ * @param workspaceId - The workspace to scope task/findings queries to.
6
+ * @returns Data ready to spread into SystemPromptOptions.
7
+ */
8
+ export function fetchOrchestratorContext(workspaceId) {
9
+ // No workspace → no orchestrator context (root/System task)
10
+ if (!workspaceId) {
11
+ return undefined;
12
+ }
13
+ // Workspace metadata
14
+ const ws = workspaceStore.getWorkspace(workspaceId);
15
+ // All personas (used for both the roster and persona name resolution)
16
+ const allPersonas = personaStore.listPersonas();
17
+ const personaNameMap = new Map();
18
+ for (const p of allPersonas) {
19
+ personaNameMap.set(p.id, p.name);
20
+ }
21
+ // All tasks in this workspace → TaskTreeNode[]
22
+ const allTasks = taskStore.listTasks(workspaceId || undefined);
23
+ const taskTree = allTasks.map((t) => ({
24
+ id: t.id,
25
+ title: t.title,
26
+ status: t.status,
27
+ depth: t.depth,
28
+ parentTaskId: t.parentTaskId,
29
+ dependsOn: safeParseJsonArray(t.dependsOn),
30
+ personaName: personaNameMap.get(t.defaultPersonaId) || "",
31
+ branch: t.branch,
32
+ canDecompose: t.canDecompose,
33
+ }));
34
+ // Available personas
35
+ const availablePersonas = allPersonas.map((p) => ({
36
+ name: p.name,
37
+ description: p.description,
38
+ runtime: p.runtime,
39
+ model: p.model,
40
+ }));
41
+ // Available environments
42
+ const availableEnvironments = envRegistry.listEnvironments().map((e) => ({
43
+ displayName: e.displayName,
44
+ adapterType: e.adapterType,
45
+ status: e.status,
46
+ defaultRuntime: e.defaultRuntime,
47
+ }));
48
+ // Findings context (pre-formatted markdown with 8K char budget)
49
+ const findingsContext = workspaceId
50
+ ? buildFindingsContext(workspaceId)
51
+ : "";
52
+ return {
53
+ workspace: ws ? {
54
+ name: ws.name,
55
+ description: ws.description,
56
+ repoUrl: ws.repoUrl,
57
+ } : undefined,
58
+ taskTree,
59
+ availablePersonas,
60
+ availableEnvironments,
61
+ findingsContext,
62
+ };
63
+ }
64
+ // ─── Findings Context Builder ──────────────────────────────
65
+ /** Maximum total characters for the findings context block. */
66
+ const FINDINGS_MAX_CHARS = 8000;
67
+ /** Maximum characters per individual finding's content. */
68
+ const FINDINGS_MAX_PER_FINDING = 500;
69
+ /** Build a summarized text context of recent findings for a workspace. */
70
+ function buildFindingsContext(workspaceId) {
71
+ const allFindings = findingStore.queryFindings(workspaceId, undefined, undefined, 20);
72
+ if (allFindings.length === 0) {
73
+ return "";
74
+ }
75
+ const lines = ["## Workspace Findings (shared knowledge from other agents)\n"];
76
+ let totalChars = lines[0].length;
77
+ for (const f of allFindings) {
78
+ const content = f.content.length > FINDINGS_MAX_PER_FINDING
79
+ ? f.content.slice(0, FINDINGS_MAX_PER_FINDING) + "..."
80
+ : f.content;
81
+ const entry = `### [${f.category}] ${f.title}\n${content}\n`;
82
+ if (totalChars + entry.length > FINDINGS_MAX_CHARS) {
83
+ break;
84
+ }
85
+ lines.push(entry);
86
+ totalChars += entry.length;
87
+ }
88
+ return lines.join("\n");
89
+ }
90
+ //# sourceMappingURL=orchestrator-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator-context.js","sourceRoot":"","sources":["../src/orchestrator-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { PersonaRow } from "@grackle-ai/database";
2
+ /** Resolved persona fields needed to start a session. */
3
+ export interface ResolvedPersona {
4
+ /** The persona ID that was resolved. */
5
+ personaId: string;
6
+ /** Agent runtime implementation (e.g. "claude-code", "codex", "genaiscript"). */
7
+ runtime: string;
8
+ /** LLM model identifier (e.g. "sonnet", "gpt-4.1"). */
9
+ model: string;
10
+ /** Maximum turns for the agent session (0 = unlimited). */
11
+ maxTurns: number;
12
+ /** System prompt to prepend. */
13
+ systemPrompt: string;
14
+ /** JSON tool configuration. */
15
+ toolConfig: string;
16
+ /** JSON array of MCP server configs. */
17
+ mcpServers: string;
18
+ /** Persona type: "agent" (interactive LLM session) or "script" (run-to-completion). */
19
+ type: string;
20
+ /** Script source code (non-empty for script personas). */
21
+ script: string;
22
+ /** The full persona row for additional fields. */
23
+ persona: PersonaRow;
24
+ }
25
+ /**
26
+ * Resolve a persona using the cascade:
27
+ * request persona → task default → workspace default → app default → error
28
+ *
29
+ * The first non-empty persona ID in the cascade is used to look up the persona.
30
+ * Throws if no persona ID is found at any level, or if the resolved ID does not exist.
31
+ */
32
+ export declare function resolvePersona(requestPersonaId: string, taskDefaultPersonaId?: string, workspaceDefaultPersonaId?: string): ResolvedPersona;
33
+ //# sourceMappingURL=resolve-persona.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-persona.d.ts","sourceRoot":"","sources":["../src/resolve-persona.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,yDAAyD;AACzD,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,OAAO,EAAE,UAAU,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,gBAAgB,EAAE,MAAM,EACxB,oBAAoB,CAAC,EAAE,MAAM,EAC7B,yBAAyB,CAAC,EAAE,MAAM,GACjC,eAAe,CAyCjB"}
@@ -0,0 +1,43 @@
1
+ import { personaStore, settingsStore } from "@grackle-ai/database";
2
+ /**
3
+ * Resolve a persona using the cascade:
4
+ * request persona → task default → workspace default → app default → error
5
+ *
6
+ * The first non-empty persona ID in the cascade is used to look up the persona.
7
+ * Throws if no persona ID is found at any level, or if the resolved ID does not exist.
8
+ */
9
+ export function resolvePersona(requestPersonaId, taskDefaultPersonaId, workspaceDefaultPersonaId) {
10
+ const personaId = requestPersonaId ||
11
+ taskDefaultPersonaId ||
12
+ workspaceDefaultPersonaId ||
13
+ settingsStore.getSetting("default_persona_id") ||
14
+ "";
15
+ if (!personaId) {
16
+ throw new Error("No persona configured. Set a default persona at the app, workspace, or task level, or specify one explicitly.");
17
+ }
18
+ const persona = personaStore.getPersona(personaId);
19
+ if (!persona) {
20
+ throw new Error(`Persona not found: ${personaId}`);
21
+ }
22
+ const personaType = persona.type || "agent";
23
+ if (!persona.runtime) {
24
+ throw new Error(`Persona "${persona.name}" has no runtime configured`);
25
+ }
26
+ // Model is required for agent personas but optional for script personas
27
+ if (personaType !== "script" && !persona.model) {
28
+ throw new Error(`Persona "${persona.name}" has no model configured`);
29
+ }
30
+ return {
31
+ personaId: persona.id,
32
+ runtime: persona.runtime,
33
+ model: persona.model,
34
+ maxTurns: persona.maxTurns,
35
+ systemPrompt: persona.systemPrompt,
36
+ toolConfig: persona.toolConfig,
37
+ mcpServers: persona.mcpServers,
38
+ type: personaType,
39
+ script: persona.script,
40
+ persona,
41
+ };
42
+ }
43
+ //# sourceMappingURL=resolve-persona.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-persona.js","sourceRoot":"","sources":["../src/resolve-persona.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA2BnE;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,gBAAwB,EACxB,oBAA6B,EAC7B,yBAAkC;IAElC,MAAM,SAAS,GACb,gBAAgB;QAChB,oBAAoB;QACpB,yBAAyB;QACzB,aAAa,CAAC,UAAU,CAAC,oBAAoB,CAAC;QAC9C,EAAE,CAAC;IAEL,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,+GAA+G,CAChH,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACnD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,sBAAsB,SAAS,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC;IAE5C,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,CAAC,IAAI,6BAA6B,CAAC,CAAC;IACzE,CAAC;IACD,wEAAwE;IACxE,IAAI,WAAW,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,CAAC,IAAI,2BAA2B,CAAC,CAAC;IACvE,CAAC;IAED,OAAO;QACL,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Builds system prompts for agent sessions, assembling sections
3
+ * dynamically based on the session type (task vs ad-hoc).
4
+ *
5
+ * Orchestrator tasks (canDecompose + depth <= 1) receive rich project
6
+ * context (task tree, persona roster, environments, findings).
7
+ * Leaf tasks receive the existing completion-contract template.
8
+ * Task title and description are NOT included in the system prompt — they
9
+ * belong in the user prompt (see {@link buildTaskPrompt}).
10
+ */
11
+ /** Build the user-facing prompt from task title, description, and optional notes. */
12
+ export declare function buildTaskPrompt(title: string, description: string, notes?: string): string;
13
+ /** Lightweight task node for rendering the task tree in orchestrator prompts. */
14
+ export interface TaskTreeNode {
15
+ /** Task ID. */
16
+ id: string;
17
+ /** Task title. */
18
+ title: string;
19
+ /** Current lifecycle status (not_started, working, paused, complete, failed). */
20
+ status: string;
21
+ /** Nesting depth in the hierarchy (0 = root). */
22
+ depth: number;
23
+ /** Parent task ID (empty string for root-level tasks). */
24
+ parentTaskId: string;
25
+ /** IDs of tasks this task depends on. */
26
+ dependsOn: string[];
27
+ /** Resolved persona display name (empty if none assigned). */
28
+ personaName: string;
29
+ /** Git branch name (empty if none). */
30
+ branch: string;
31
+ /** Whether this task can create subtasks. */
32
+ canDecompose: boolean;
33
+ }
34
+ /** Persona summary for the available-personas prompt section. */
35
+ export interface PersonaSummary {
36
+ /** Display name. */
37
+ name: string;
38
+ /** Short description. */
39
+ description: string;
40
+ /** Runtime backend (claude-code, copilot, codex, etc.). */
41
+ runtime: string;
42
+ /** Default model (e.g. "opus", "sonnet"). */
43
+ model: string;
44
+ }
45
+ /** Environment summary for the available-environments prompt section. */
46
+ export interface EnvironmentSummary {
47
+ /** Human-readable name. */
48
+ displayName: string;
49
+ /** Adapter backend (local, ssh, codespace, docker). */
50
+ adapterType: string;
51
+ /** Connection status (connected, disconnected, etc.). */
52
+ status: string;
53
+ /** Default runtime for this environment. */
54
+ defaultRuntime: string;
55
+ }
56
+ /** Options for building a system prompt. */
57
+ export interface SystemPromptOptions {
58
+ /** Task metadata. When absent, this is an ad-hoc session. */
59
+ task?: {
60
+ title: string;
61
+ description: string;
62
+ notes: string;
63
+ };
64
+ /** ID of the current task (used to mark it in the task tree). */
65
+ taskId?: string;
66
+ /** Whether the agent is allowed to create subtasks. */
67
+ canDecompose?: boolean;
68
+ /** Persona behavioral instructions (prepended when non-empty). */
69
+ personaPrompt?: string;
70
+ /** Task depth in the hierarchy (0 = root, 1 = first child, etc.). */
71
+ taskDepth?: number;
72
+ /** Workspace metadata for the orchestrator context section. */
73
+ workspace?: {
74
+ name: string;
75
+ description: string;
76
+ repoUrl: string;
77
+ };
78
+ /** All tasks in the workspace, for rendering the task tree. */
79
+ taskTree?: TaskTreeNode[];
80
+ /** Available personas for the orchestrator to assign. */
81
+ availablePersonas?: PersonaSummary[];
82
+ /** Available environments for the orchestrator to route work to. */
83
+ availableEnvironments?: EnvironmentSummary[];
84
+ /** Pre-built findings context string (from buildFindingsContext). */
85
+ findingsContext?: string;
86
+ /** Invocation mode: fresh (first time) or resume (re-invoked after child completion). */
87
+ triggerMode?: "fresh" | "resume";
88
+ }
89
+ /**
90
+ * Assembles a system prompt from discrete sections based on session type.
91
+ *
92
+ * Orchestrator tasks get project state, task tree, persona roster, and
93
+ * decomposition guidelines. Leaf tasks get the existing completion contract.
94
+ * Ad-hoc sessions get only the MCP note and persona prompt.
95
+ * Task title and description are NOT included here — they belong in the user prompt
96
+ * (see {@link buildTaskPrompt}).
97
+ */
98
+ export declare class SystemPromptBuilder {
99
+ private readonly options;
100
+ constructor(options: SystemPromptOptions);
101
+ /** Build the complete system prompt string. */
102
+ build(): string;
103
+ /**
104
+ * Determine whether this is an orchestrator task.
105
+ * Requires canDecompose, shallow depth, AND orchestrator data fields to be
106
+ * present. This ensures existing callers that pass canDecompose without
107
+ * the new fields still get the leaf template.
108
+ */
109
+ private isOrchestrator;
110
+ /** Orchestrator task context with role framing. */
111
+ private buildOrchestratorTaskContext;
112
+ /** Workspace metadata section. */
113
+ private buildWorkspaceContext;
114
+ /** Hierarchical task tree with statuses, personas, and dependencies. */
115
+ private buildTaskTree;
116
+ /** Available personas table. */
117
+ private buildAvailablePersonas;
118
+ /** Available environments table. */
119
+ private buildAvailableEnvironments;
120
+ /** Orchestrator findings section with actual findings data. */
121
+ private buildOrchestratorFindingsSection;
122
+ /** Trigger context describing why this invocation happened. */
123
+ private buildTriggerContext;
124
+ /** Decomposition heuristics and guardrails. */
125
+ private buildDecompositionGuidelines;
126
+ /** Orchestrator-specific MCP tool documentation. */
127
+ private buildOrchestratorTools;
128
+ /** Orchestrator-specific completion contract. */
129
+ private buildOrchestratorCompletionContract;
130
+ /** Task title, description, and notes. */
131
+ private buildTaskContext;
132
+ /** Contract for signaling task completion. */
133
+ private buildCompletionContract;
134
+ /** Subtask guidance based on canDecompose. */
135
+ private buildSubtaskSection;
136
+ /** SIGCHLD signal documentation. */
137
+ private buildSignalSection;
138
+ /** Guidance on using findings (leaf agents). */
139
+ private buildFindingsSection;
140
+ /** IPC fd cleanup instructions — advisory enforcement for closing child fds before exit. */
141
+ private buildIpcFdSection;
142
+ /** MCP note (always included). */
143
+ private buildMcpNote;
144
+ }
145
+ //# sourceMappingURL=system-prompt-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-prompt-builder.d.ts","sourceRoot":"","sources":["../src/system-prompt-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Builds system prompts for agent sessions, assembling sections
3
+ * dynamically based on the session type (task vs ad-hoc).
4
+ *
5
+ * Orchestrator tasks (canDecompose + depth <= 1) receive rich project
6
+ * context (task tree, persona roster, environments, findings).
7
+ * Leaf tasks receive the existing completion-contract template.
8
+ * Task title and description are NOT included in the system prompt — they
9
+ * belong in the user prompt (see {@link buildTaskPrompt}).
10
+ */
11
+ /** Build the user-facing prompt from task title, description, and optional notes. */
12
+ export function buildTaskPrompt(title, description, notes) {
13
+ const parts = [title];
14
+ if (description) {
15
+ parts.push(description);
16
+ }
17
+ if (notes) {
18
+ parts.push(`## Notes\n${notes}`);
19
+ }
20
+ return parts.join("\n\n");
21
+ }
22
+ // ─── Builder ─────────────────────────────────────────────────
23
+ /**
24
+ * Assembles a system prompt from discrete sections based on session type.
25
+ *
26
+ * Orchestrator tasks get project state, task tree, persona roster, and
27
+ * decomposition guidelines. Leaf tasks get the existing completion contract.
28
+ * Ad-hoc sessions get only the MCP note and persona prompt.
29
+ * Task title and description are NOT included here — they belong in the user prompt
30
+ * (see {@link buildTaskPrompt}).
31
+ */
32
+ export class SystemPromptBuilder {
33
+ options;
34
+ constructor(options) {
35
+ this.options = options;
36
+ }
37
+ /** Build the complete system prompt string. */
38
+ build() {
39
+ const sections = [];
40
+ // Persona prompt (always first when present)
41
+ if (this.options.personaPrompt) {
42
+ sections.push(this.options.personaPrompt);
43
+ }
44
+ if (this.options.task) {
45
+ if (this.isOrchestrator()) {
46
+ sections.push(this.buildOrchestratorTaskContext());
47
+ sections.push(this.buildWorkspaceContext());
48
+ sections.push(this.buildTaskTree());
49
+ sections.push(this.buildAvailablePersonas());
50
+ sections.push(this.buildAvailableEnvironments());
51
+ sections.push(this.buildOrchestratorFindingsSection());
52
+ sections.push(this.buildTriggerContext());
53
+ sections.push(this.buildDecompositionGuidelines());
54
+ sections.push(this.buildOrchestratorTools());
55
+ sections.push(this.buildOrchestratorCompletionContract());
56
+ sections.push(this.buildSignalSection());
57
+ }
58
+ else {
59
+ // Leaf task: title/description go in the user prompt (buildTaskPrompt), not here.
60
+ sections.push(this.buildCompletionContract());
61
+ sections.push(this.buildSubtaskSection());
62
+ sections.push(this.buildSignalSection());
63
+ sections.push(this.buildFindingsSection());
64
+ }
65
+ }
66
+ // IPC fd cleanup instructions (always included — harmless if agent doesn't use IPC)
67
+ sections.push(this.buildIpcFdSection());
68
+ // MCP note (always included)
69
+ sections.push(this.buildMcpNote());
70
+ return sections.filter(Boolean).join("\n\n");
71
+ }
72
+ // ─── Orchestrator Detection ──────────────────────────────
73
+ /**
74
+ * Determine whether this is an orchestrator task.
75
+ * Requires canDecompose, shallow depth, AND orchestrator data fields to be
76
+ * present. This ensures existing callers that pass canDecompose without
77
+ * the new fields still get the leaf template.
78
+ */
79
+ isOrchestrator() {
80
+ return this.options.canDecompose === true
81
+ && this.options.taskDepth !== undefined
82
+ && this.options.taskDepth <= 1
83
+ && this.options.taskTree !== undefined;
84
+ }
85
+ // ─── Orchestrator Sections ───────────────────────────────
86
+ /** Orchestrator task context with role framing. */
87
+ buildOrchestratorTaskContext() {
88
+ const { title, description, notes } = this.options.task;
89
+ const parts = [
90
+ `## Your Task: ${title}`,
91
+ `You are an **orchestrator agent** responsible for decomposing and coordinating work. You do not write code directly — you break work into subtasks, assign personas, manage dependencies, and monitor progress.`,
92
+ ];
93
+ if (description) {
94
+ parts.push(description);
95
+ }
96
+ if (notes) {
97
+ parts.push(`### Notes (from previous attempt or user feedback)\n${notes}`);
98
+ }
99
+ return parts.join("\n\n");
100
+ }
101
+ /** Workspace metadata section. */
102
+ buildWorkspaceContext() {
103
+ const ws = this.options.workspace;
104
+ if (!ws) {
105
+ return "";
106
+ }
107
+ const lines = [`## Workspace Context`];
108
+ lines.push(`- **Name**: ${ws.name}`);
109
+ if (ws.description) {
110
+ lines.push(`- **Description**: ${ws.description}`);
111
+ }
112
+ if (ws.repoUrl) {
113
+ lines.push(`- **Repository**: ${ws.repoUrl}`);
114
+ }
115
+ return lines.join("\n");
116
+ }
117
+ /** Hierarchical task tree with statuses, personas, and dependencies. */
118
+ buildTaskTree() {
119
+ const nodes = this.options.taskTree;
120
+ if (!nodes || nodes.length === 0) {
121
+ return "";
122
+ }
123
+ // Build parent → children map
124
+ const childMap = new Map();
125
+ for (const node of nodes) {
126
+ const key = node.parentTaskId || "";
127
+ const children = childMap.get(key);
128
+ if (children) {
129
+ children.push(node);
130
+ }
131
+ else {
132
+ childMap.set(key, [node]);
133
+ }
134
+ }
135
+ // Status summary
136
+ const counts = new Map();
137
+ for (const node of nodes) {
138
+ counts.set(node.status, (counts.get(node.status) || 0) + 1);
139
+ }
140
+ const summary = [...counts.entries()]
141
+ .map(([status, count]) => `${count} ${status}`)
142
+ .join(", ");
143
+ // Recursive render
144
+ const lines = [];
145
+ const renderNode = (node, indent) => {
146
+ const pad = " ".repeat(indent);
147
+ const persona = node.personaName ? ` (persona: ${node.personaName})` : "";
148
+ const deps = node.dependsOn.length > 0
149
+ ? ` [depends on: ${node.dependsOn.join(", ")}]`
150
+ : "";
151
+ const branch = node.branch ? ` [branch: ${node.branch}]` : "";
152
+ const marker = node.id === this.options.taskId ? " <-- YOU" : "";
153
+ lines.push(`${pad}- [${node.status}] ${node.title}${persona}${deps}${branch}${marker}`);
154
+ const children = childMap.get(node.id);
155
+ if (children) {
156
+ for (const child of children) {
157
+ renderNode(child, indent + 1);
158
+ }
159
+ }
160
+ };
161
+ // Start from root nodes
162
+ const roots = childMap.get("") || [];
163
+ for (const root of roots) {
164
+ renderNode(root, 0);
165
+ }
166
+ return `## Task Tree\n\nStatus: ${summary}\n\n${lines.join("\n")}`;
167
+ }
168
+ /** Available personas table. */
169
+ buildAvailablePersonas() {
170
+ const personas = this.options.availablePersonas;
171
+ if (!personas || personas.length === 0) {
172
+ return "";
173
+ }
174
+ const rows = personas.map((p) => `| ${p.name} | ${p.description || "—"} | ${p.runtime || "—"} | ${p.model || "—"} |`);
175
+ return [
176
+ `## Available Personas`,
177
+ ``,
178
+ `| Name | Description | Runtime | Model |`,
179
+ `|------|-------------|---------|-------|`,
180
+ ...rows,
181
+ ].join("\n");
182
+ }
183
+ /** Available environments table. */
184
+ buildAvailableEnvironments() {
185
+ const envs = this.options.availableEnvironments;
186
+ if (!envs || envs.length === 0) {
187
+ return "";
188
+ }
189
+ const rows = envs.map((e) => `| ${e.displayName} | ${e.adapterType} | ${e.status} | ${e.defaultRuntime || "—"} |`);
190
+ return [
191
+ `## Available Environments`,
192
+ ``,
193
+ `| Name | Adapter | Status | Runtime |`,
194
+ `|------|---------|--------|---------|`,
195
+ ...rows,
196
+ ].join("\n");
197
+ }
198
+ /** Orchestrator findings section with actual findings data. */
199
+ buildOrchestratorFindingsSection() {
200
+ if (!this.options.findingsContext) {
201
+ return "";
202
+ }
203
+ return this.options.findingsContext;
204
+ }
205
+ /** Trigger context describing why this invocation happened. */
206
+ buildTriggerContext() {
207
+ if (this.options.triggerMode === "resume") {
208
+ return [
209
+ `## Trigger Context`,
210
+ `You are being re-invoked after one or more child tasks completed. Review the task tree above for current statuses and decide what to do next.`,
211
+ ].join("\n");
212
+ }
213
+ return [
214
+ `## Trigger Context`,
215
+ `You are being invoked for the first time to orchestrate this workspace. Assess the current state, then decompose your task into subtasks.`,
216
+ ].join("\n");
217
+ }
218
+ /** Decomposition heuristics and guardrails. */
219
+ buildDecompositionGuidelines() {
220
+ return [
221
+ `## Decomposition Guidelines`,
222
+ ``,
223
+ `- Estimate task complexity before decomposing. Do not decompose simple tasks (under ~100 lines of code or touching fewer than 3 files).`,
224
+ `- Favor context-boundary decomposition over role-boundary decomposition: the agent implementing a feature should also write its tests.`,
225
+ `- Consider coordination cost vs. parallel benefit. Decomposition multiplies token usage by 3-10x.`,
226
+ `- Keep the number of direct subtasks reasonable (aim for 3-7 per parent).`,
227
+ `- Each subtask description must be self-contained — the child agent has no context beyond what you provide in the description.`,
228
+ `- Set dependencies between subtasks when ordering matters. Independent subtasks can run in parallel.`,
229
+ `- Grant decomposition rights (\`canDecompose: true\`) only to subtasks that genuinely need to coordinate further work.`,
230
+ ].join("\n");
231
+ }
232
+ /** Orchestrator-specific MCP tool documentation. */
233
+ buildOrchestratorTools() {
234
+ return [
235
+ `## Orchestrator Tools`,
236
+ ``,
237
+ `Use these tools on your \`grackle\` MCP server to coordinate work:`,
238
+ `- \`task_create\` — Create a subtask with title, description, dependencies, persona, and decomposition rights.`,
239
+ `- \`task_list\` — List all tasks in the workspace with their current statuses.`,
240
+ `- \`task_show\` — Show details of a specific task including its sessions and output.`,
241
+ `- \`task_start\` — Start a task (begins agent execution on the assigned environment).`,
242
+ `- \`task_complete\` — Signal that your task is complete.`,
243
+ `- \`finding_post\` — Share a discovery (architecture decisions, patterns, bugs) with other agents.`,
244
+ `- \`finding_list\` — List recent findings from all agents in this workspace.`,
245
+ ].join("\n");
246
+ }
247
+ /** Orchestrator-specific completion contract. */
248
+ buildOrchestratorCompletionContract() {
249
+ return [
250
+ `## Completion`,
251
+ `When all subtasks are complete and you have verified the results, use \`task_complete\` to signal your own completion. If any subtasks failed, decide whether to retry, reassign, or handle the failure before completing.`,
252
+ ].join("\n");
253
+ }
254
+ // ─── Leaf Sections (unchanged) ───────────────────────────
255
+ /** Task title, description, and notes. */
256
+ buildTaskContext() {
257
+ const { title, description, notes } = this.options.task;
258
+ const parts = [
259
+ `## Task: ${title}`,
260
+ description,
261
+ ];
262
+ if (notes) {
263
+ parts.push(`## Notes (from previous attempt or user feedback)\n${notes}`);
264
+ }
265
+ return parts.filter(Boolean).join("\n\n");
266
+ }
267
+ /** Contract for signaling task completion. */
268
+ buildCompletionContract() {
269
+ return [
270
+ `## Completion`,
271
+ `When you are done with your task, use \`task_complete\` to signal completion. Do not go idle without signaling — the orchestrator depends on an explicit completion signal to know you are finished.`,
272
+ ].join("\n");
273
+ }
274
+ /** Subtask guidance based on canDecompose. */
275
+ buildSubtaskSection() {
276
+ if (this.options.canDecompose) {
277
+ return [
278
+ `## Subtasks`,
279
+ `If this task is too large or complex to complete alone, use \`task_create\` to decompose it into subtasks. Monitor their progress with \`task_list\` and \`task_show\`. Wait for subtasks to finish before marking your own task complete.`,
280
+ ].join("\n");
281
+ }
282
+ return [
283
+ `## Subtasks`,
284
+ `Subtask creation is disabled for this task. Complete the work yourself.`,
285
+ ].join("\n");
286
+ }
287
+ /** SIGCHLD signal documentation. */
288
+ buildSignalSection() {
289
+ return [
290
+ `## Signals`,
291
+ `You may receive \`[SIGCHLD]\` messages when a child task finishes, fails, or is interrupted. When you receive one:`,
292
+ `1. Review the child's status and last output (included in the signal).`,
293
+ `2. If the child succeeded, check whether all subtasks are done and mark your task complete if so.`,
294
+ `3. If the child failed or was interrupted, decide whether to retry, reassign, or handle the failure yourself.`,
295
+ ].join("\n");
296
+ }
297
+ /** Guidance on using findings (leaf agents). */
298
+ buildFindingsSection() {
299
+ return [
300
+ `## Findings`,
301
+ `Use \`finding_post\` to share discoveries (architecture decisions, bugs, patterns) with other agents. Check \`finding_list\` before posting to avoid duplicates.`,
302
+ ].join("\n");
303
+ }
304
+ /** IPC fd cleanup instructions — advisory enforcement for closing child fds before exit. */
305
+ buildIpcFdSection() {
306
+ return [
307
+ "## IPC File Descriptors",
308
+ "",
309
+ "If you spawn child sessions using `ipc_spawn`, you receive file descriptors (fds).",
310
+ "Before finishing your work, you MUST:",
311
+ "1. Call `ipc_list_fds` to check for open fds",
312
+ "2. For each owned fd (`owned: true`), call `ipc_close` to close it",
313
+ "3. Only then should you stop working",
314
+ "",
315
+ "Failing to close your fds will leave child sessions running indefinitely.",
316
+ ].join("\n");
317
+ }
318
+ /** MCP note (always included). */
319
+ buildMcpNote() {
320
+ return `You have tools on your \`grackle\` MCP server.`;
321
+ }
322
+ }
323
+ //# sourceMappingURL=system-prompt-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-prompt-builder.js","sourceRoot":"","sources":["../src/system-prompt-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.57.7"
9
+ }
10
+ ]
11
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@grackle-ai/prompt",
3
+ "version": "0.73.1",
4
+ "description": "System prompt assembly and persona resolution for Grackle agent sessions",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/nick-pape/grackle.git",
9
+ "directory": "packages/prompt"
10
+ },
11
+ "keywords": [
12
+ "grackle",
13
+ "prompt",
14
+ "system-prompt",
15
+ "persona",
16
+ "orchestrator"
17
+ ],
18
+ "engines": {
19
+ "node": ">=22.0.0"
20
+ },
21
+ "type": "module",
22
+ "main": "dist/index.js",
23
+ "types": "dist/index.d.ts",
24
+ "files": [
25
+ "dist/"
26
+ ],
27
+ "dependencies": {
28
+ "@grackle-ai/database": "0.73.1"
29
+ },
30
+ "devDependencies": {
31
+ "@rushstack/heft": "1.2.7",
32
+ "@types/node": "^22.0.0",
33
+ "vitest": "^3.1.1",
34
+ "@grackle-ai/heft-rig": "0.0.1"
35
+ },
36
+ "scripts": {
37
+ "build": "heft build --clean",
38
+ "test": "vitest run",
39
+ "clean": "heft clean",
40
+ "_phase:build": "heft run --only build -- --clean",
41
+ "_phase:test": "vitest run"
42
+ }
43
+ }