@grackle-ai/server 0.55.0 → 0.56.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.
@@ -1,12 +1,33 @@
1
1
  /**
2
2
  * Builds system prompts for agent sessions, assembling sections
3
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}).
4
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 ─────────────────────────────────────────────────
5
23
  /**
6
24
  * Assembles a system prompt from discrete sections based on session type.
7
25
  *
8
- * Task sessions get task context, completion contract, signal docs, and findings guidance.
26
+ * Orchestrator tasks get project state, task tree, persona roster, and
27
+ * decomposition guidelines. Leaf tasks get the existing completion contract.
9
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}).
10
31
  */
11
32
  export class SystemPromptBuilder {
12
33
  options;
@@ -21,16 +42,214 @@ export class SystemPromptBuilder {
21
42
  sections.push(this.options.personaPrompt);
22
43
  }
23
44
  if (this.options.task) {
24
- sections.push(this.buildTaskContext());
25
- sections.push(this.buildCompletionContract());
26
- sections.push(this.buildSubtaskSection());
27
- sections.push(this.buildSignalSection());
28
- sections.push(this.buildFindingsSection());
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
+ }
29
65
  }
30
66
  // MCP note (always included)
31
67
  sections.push(this.buildMcpNote());
32
68
  return sections.filter(Boolean).join("\n\n");
33
69
  }
70
+ // ─── Orchestrator Detection ──────────────────────────────
71
+ /**
72
+ * Determine whether this is an orchestrator task.
73
+ * Requires canDecompose, shallow depth, AND orchestrator data fields to be
74
+ * present. This ensures existing callers that pass canDecompose without
75
+ * the new fields still get the leaf template.
76
+ */
77
+ isOrchestrator() {
78
+ return this.options.canDecompose === true
79
+ && this.options.taskDepth !== undefined
80
+ && this.options.taskDepth <= 1
81
+ && this.options.taskTree !== undefined;
82
+ }
83
+ // ─── Orchestrator Sections ───────────────────────────────
84
+ /** Orchestrator task context with role framing. */
85
+ buildOrchestratorTaskContext() {
86
+ const { title, description, notes } = this.options.task;
87
+ const parts = [
88
+ `## Your Task: ${title}`,
89
+ `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.`,
90
+ ];
91
+ if (description) {
92
+ parts.push(description);
93
+ }
94
+ if (notes) {
95
+ parts.push(`### Notes (from previous attempt or user feedback)\n${notes}`);
96
+ }
97
+ return parts.join("\n\n");
98
+ }
99
+ /** Workspace metadata section. */
100
+ buildWorkspaceContext() {
101
+ const ws = this.options.workspace;
102
+ if (!ws) {
103
+ return "";
104
+ }
105
+ const lines = [`## Workspace Context`];
106
+ lines.push(`- **Name**: ${ws.name}`);
107
+ if (ws.description) {
108
+ lines.push(`- **Description**: ${ws.description}`);
109
+ }
110
+ if (ws.repoUrl) {
111
+ lines.push(`- **Repository**: ${ws.repoUrl}`);
112
+ }
113
+ return lines.join("\n");
114
+ }
115
+ /** Hierarchical task tree with statuses, personas, and dependencies. */
116
+ buildTaskTree() {
117
+ const nodes = this.options.taskTree;
118
+ if (!nodes || nodes.length === 0) {
119
+ return "";
120
+ }
121
+ // Build parent → children map
122
+ const childMap = new Map();
123
+ for (const node of nodes) {
124
+ const key = node.parentTaskId || "";
125
+ const children = childMap.get(key);
126
+ if (children) {
127
+ children.push(node);
128
+ }
129
+ else {
130
+ childMap.set(key, [node]);
131
+ }
132
+ }
133
+ // Status summary
134
+ const counts = new Map();
135
+ for (const node of nodes) {
136
+ counts.set(node.status, (counts.get(node.status) || 0) + 1);
137
+ }
138
+ const summary = [...counts.entries()]
139
+ .map(([status, count]) => `${count} ${status}`)
140
+ .join(", ");
141
+ // Recursive render
142
+ const lines = [];
143
+ const renderNode = (node, indent) => {
144
+ const pad = " ".repeat(indent);
145
+ const persona = node.personaName ? ` (persona: ${node.personaName})` : "";
146
+ const deps = node.dependsOn.length > 0
147
+ ? ` [depends on: ${node.dependsOn.join(", ")}]`
148
+ : "";
149
+ const branch = node.branch ? ` [branch: ${node.branch}]` : "";
150
+ const marker = node.id === this.options.taskId ? " <-- YOU" : "";
151
+ lines.push(`${pad}- [${node.status}] ${node.title}${persona}${deps}${branch}${marker}`);
152
+ const children = childMap.get(node.id);
153
+ if (children) {
154
+ for (const child of children) {
155
+ renderNode(child, indent + 1);
156
+ }
157
+ }
158
+ };
159
+ // Start from root nodes
160
+ const roots = childMap.get("") || [];
161
+ for (const root of roots) {
162
+ renderNode(root, 0);
163
+ }
164
+ return `## Task Tree\n\nStatus: ${summary}\n\n${lines.join("\n")}`;
165
+ }
166
+ /** Available personas table. */
167
+ buildAvailablePersonas() {
168
+ const personas = this.options.availablePersonas;
169
+ if (!personas || personas.length === 0) {
170
+ return "";
171
+ }
172
+ const rows = personas.map((p) => `| ${p.name} | ${p.description || "—"} | ${p.runtime || "—"} | ${p.model || "—"} |`);
173
+ return [
174
+ `## Available Personas`,
175
+ ``,
176
+ `| Name | Description | Runtime | Model |`,
177
+ `|------|-------------|---------|-------|`,
178
+ ...rows,
179
+ ].join("\n");
180
+ }
181
+ /** Available environments table. */
182
+ buildAvailableEnvironments() {
183
+ const envs = this.options.availableEnvironments;
184
+ if (!envs || envs.length === 0) {
185
+ return "";
186
+ }
187
+ const rows = envs.map((e) => `| ${e.displayName} | ${e.adapterType} | ${e.status} | ${e.defaultRuntime || "—"} |`);
188
+ return [
189
+ `## Available Environments`,
190
+ ``,
191
+ `| Name | Adapter | Status | Runtime |`,
192
+ `|------|---------|--------|---------|`,
193
+ ...rows,
194
+ ].join("\n");
195
+ }
196
+ /** Orchestrator findings section with actual findings data. */
197
+ buildOrchestratorFindingsSection() {
198
+ if (!this.options.findingsContext) {
199
+ return "";
200
+ }
201
+ return this.options.findingsContext;
202
+ }
203
+ /** Trigger context describing why this invocation happened. */
204
+ buildTriggerContext() {
205
+ if (this.options.triggerMode === "resume") {
206
+ return [
207
+ `## Trigger Context`,
208
+ `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.`,
209
+ ].join("\n");
210
+ }
211
+ return [
212
+ `## Trigger Context`,
213
+ `You are being invoked for the first time to orchestrate this workspace. Assess the current state, then decompose your task into subtasks.`,
214
+ ].join("\n");
215
+ }
216
+ /** Decomposition heuristics and guardrails. */
217
+ buildDecompositionGuidelines() {
218
+ return [
219
+ `## Decomposition Guidelines`,
220
+ ``,
221
+ `- Estimate task complexity before decomposing. Do not decompose simple tasks (under ~100 lines of code or touching fewer than 3 files).`,
222
+ `- Favor context-boundary decomposition over role-boundary decomposition: the agent implementing a feature should also write its tests.`,
223
+ `- Consider coordination cost vs. parallel benefit. Decomposition multiplies token usage by 3-10x.`,
224
+ `- Keep the number of direct subtasks reasonable (aim for 3-7 per parent).`,
225
+ `- Each subtask description must be self-contained — the child agent has no context beyond what you provide in the description.`,
226
+ `- Set dependencies between subtasks when ordering matters. Independent subtasks can run in parallel.`,
227
+ `- Grant decomposition rights (\`canDecompose: true\`) only to subtasks that genuinely need to coordinate further work.`,
228
+ ].join("\n");
229
+ }
230
+ /** Orchestrator-specific MCP tool documentation. */
231
+ buildOrchestratorTools() {
232
+ return [
233
+ `## Orchestrator Tools`,
234
+ ``,
235
+ `Use these tools on your \`grackle\` MCP server to coordinate work:`,
236
+ `- \`task_create\` — Create a subtask with title, description, dependencies, persona, and decomposition rights.`,
237
+ `- \`task_list\` — List all tasks in the workspace with their current statuses.`,
238
+ `- \`task_show\` — Show details of a specific task including its sessions and output.`,
239
+ `- \`task_start\` — Start a task (begins agent execution on the assigned environment).`,
240
+ `- \`task_complete\` — Signal that your task is complete.`,
241
+ `- \`finding_post\` — Share a discovery (architecture decisions, patterns, bugs) with other agents.`,
242
+ `- \`finding_list\` — List recent findings from all agents in this workspace.`,
243
+ ].join("\n");
244
+ }
245
+ /** Orchestrator-specific completion contract. */
246
+ buildOrchestratorCompletionContract() {
247
+ return [
248
+ `## Completion`,
249
+ `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.`,
250
+ ].join("\n");
251
+ }
252
+ // ─── Leaf Sections (unchanged) ───────────────────────────
34
253
  /** Task title, description, and notes. */
35
254
  buildTaskContext() {
36
255
  const { title, description, notes } = this.options.task;
@@ -73,7 +292,7 @@ export class SystemPromptBuilder {
73
292
  `3. If the child failed or was interrupted, decide whether to retry, reassign, or handle the failure yourself.`,
74
293
  ].join("\n");
75
294
  }
76
- /** Guidance on using findings. */
295
+ /** Guidance on using findings (leaf agents). */
77
296
  buildFindingsSection() {
78
297
  return [
79
298
  `## Findings`,
@@ -1 +1 @@
1
- {"version":3,"file":"system-prompt-builder.js","sourceRoot":"","sources":["../src/system-prompt-builder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH;;;;;GAKG;AACH,MAAM,OAAO,mBAAmB;IACb,OAAO,CAAsB;IAE9C,YAAmB,OAA4B;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,+CAA+C;IACxC,KAAK;QACV,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,6CAA6C;QAC7C,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACtB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC;YAC9C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;YAC1C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;YACzC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,6BAA6B;QAC7B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QAEnC,OAAO,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/C,CAAC;IAED,0CAA0C;IAClC,gBAAgB;QACtB,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,IAAK,CAAC;QACzD,MAAM,KAAK,GAAG;YACZ,YAAY,KAAK,EAAE;YACnB,WAAW;SACZ,CAAC;QACF,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,sDAAsD,KAAK,EAAE,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;IAED,8CAA8C;IACtC,uBAAuB;QAC7B,OAAO;YACL,eAAe;YACf,sMAAsM;SACvM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,8CAA8C;IACtC,mBAAmB;QACzB,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YAC9B,OAAO;gBACL,aAAa;gBACb,4OAA4O;aAC7O,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;QACD,OAAO;YACL,aAAa;YACb,yEAAyE;SAC1E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,oCAAoC;IAC5B,kBAAkB;QACxB,OAAO;YACL,YAAY;YACZ,oHAAoH;YACpH,wEAAwE;YACxE,mGAAmG;YACnG,+GAA+G;SAChH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,kCAAkC;IAC1B,oBAAoB;QAC1B,OAAO;YACL,aAAa;YACb,kKAAkK;SACnK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,kCAAkC;IAC1B,YAAY;QAClB,OAAO,gDAAgD,CAAC;IAC1D,CAAC;CACF"}
1
+ {"version":3,"file":"system-prompt-builder.js","sourceRoot":"","sources":["../src/system-prompt-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qFAAqF;AACrF,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,WAAmB,EAAE,KAAc;IAChF,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,IAAI,WAAW,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC1B,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAiFD,gEAAgE;AAEhE;;;;;;;;GAQG;AACH,MAAM,OAAO,mBAAmB;IACb,OAAO,CAAsB;IAE9C,YAAmB,OAA4B;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,+CAA+C;IACxC,KAAK;QACV,MAAM,QAAQ,GAAa,EAAE,CAAC;QAE9B,6CAA6C;QAC7C,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACtB,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC1B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC,CAAC;gBACnD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC,CAAC;gBAC5C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;gBACpC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC;gBAC7C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,CAAC,CAAC;gBACjD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,gCAAgC,EAAE,CAAC,CAAC;gBACvD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;gBAC1C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC,CAAC;gBACnD,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC,CAAC;gBAC7C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,mCAAmC,EAAE,CAAC,CAAC;gBAC1D,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACN,kFAAkF;gBAClF,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,CAAC;gBAC9C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;gBAC1C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;gBACzC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;QAED,6BAA6B;QAC7B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QAEnC,OAAO,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/C,CAAC;IAED,4DAA4D;IAE5D;;;;;OAKG;IACK,cAAc;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,KAAK,IAAI;eACpC,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS;eACpC,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC;eAC3B,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC;IAC3C,CAAC;IAED,4DAA4D;IAE5D,mDAAmD;IAC3C,4BAA4B;QAClC,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,IAAK,CAAC;QACzD,MAAM,KAAK,GAAG;YACZ,iBAAiB,KAAK,EAAE;YACxB,iNAAiN;SAClN,CAAC;QACF,IAAI,WAAW,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1B,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,uDAAuD,KAAK,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAED,kCAAkC;IAC1B,qBAAqB;QAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QACrC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,sBAAsB,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,wEAAwE;IAChE,aAAa;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACpC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,8BAA8B;QAC9B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;QACnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC;YACpC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACnC,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,iBAAiB;QACjB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;aAClC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC;aAC9C,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,mBAAmB;QACnB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,UAAU,GAAG,CAAC,IAAkB,EAAE,MAAc,EAAQ,EAAE;YAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAChC,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;gBACpC,CAAC,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAC/C,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,KAAK,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;YAExF,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACvC,IAAI,QAAQ,EAAE,CAAC;gBACb,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;oBAC7B,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,wBAAwB;QACxB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACtB,CAAC;QAED,OAAO,2BAA2B,OAAO,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACrE,CAAC;IAED,gCAAgC;IACxB,sBAAsB;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAChD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CACvB,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,WAAW,IAAI,GAAG,MAAM,CAAC,CAAC,OAAO,IAAI,GAAG,MAAM,CAAC,CAAC,KAAK,IAAI,GAAG,IAAI,CAC3F,CAAC;QACF,OAAO;YACL,uBAAuB;YACvB,EAAE;YACF,0CAA0C;YAC1C,0CAA0C;YAC1C,GAAG,IAAI;SACR,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,oCAAoC;IAC5B,0BAA0B;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC;QAChD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CACnB,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,WAAW,MAAM,CAAC,CAAC,WAAW,MAAM,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,cAAc,IAAI,GAAG,IAAI,CAC5F,CAAC;QACF,OAAO;YACL,2BAA2B;YAC3B,EAAE;YACF,uCAAuC;YACvC,uCAAuC;YACvC,GAAG,IAAI;SACR,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,+DAA+D;IACvD,gCAAgC;QACtC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAClC,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC;IACtC,CAAC;IAED,+DAA+D;IACvD,mBAAmB;QACzB,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;YAC1C,OAAO;gBACL,oBAAoB;gBACpB,+IAA+I;aAChJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;QACD,OAAO;YACL,oBAAoB;YACpB,2IAA2I;SAC5I,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,+CAA+C;IACvC,4BAA4B;QAClC,OAAO;YACL,6BAA6B;YAC7B,EAAE;YACF,yIAAyI;YACzI,wIAAwI;YACxI,mGAAmG;YACnG,2EAA2E;YAC3E,gIAAgI;YAChI,sGAAsG;YACtG,wHAAwH;SACzH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,oDAAoD;IAC5C,sBAAsB;QAC5B,OAAO;YACL,uBAAuB;YACvB,EAAE;YACF,oEAAoE;YACpE,gHAAgH;YAChH,gFAAgF;YAChF,sFAAsF;YACtF,uFAAuF;YACvF,0DAA0D;YAC1D,oGAAoG;YACpG,8EAA8E;SAC/E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,iDAAiD;IACzC,mCAAmC;QACzC,OAAO;YACL,eAAe;YACf,4NAA4N;SAC7N,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,4DAA4D;IAE5D,0CAA0C;IAClC,gBAAgB;QACtB,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,IAAK,CAAC;QACzD,MAAM,KAAK,GAAG;YACZ,YAAY,KAAK,EAAE;YACnB,WAAW;SACZ,CAAC;QACF,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,sDAAsD,KAAK,EAAE,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;IAED,8CAA8C;IACtC,uBAAuB;QAC7B,OAAO;YACL,eAAe;YACf,sMAAsM;SACvM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,8CAA8C;IACtC,mBAAmB;QACzB,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YAC9B,OAAO;gBACL,aAAa;gBACb,4OAA4O;aAC7O,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;QACD,OAAO;YACL,aAAa;YACb,yEAAyE;SAC1E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,oCAAoC;IAC5B,kBAAkB;QACxB,OAAO;YACL,YAAY;YACZ,oHAAoH;YACpH,wEAAwE;YACxE,mGAAmG;YACnG,+GAA+G;SAChH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,gDAAgD;IACxC,oBAAoB;QAC1B,OAAO;YACL,aAAa;YACb,kKAAkK;SACnK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,kCAAkC;IAC1B,YAAY;QAClB,OAAO,gDAAgD,CAAC;IAC1D,CAAC;CACF"}
@@ -1,5 +1,12 @@
1
- import { WebSocketServer } from "ws";
1
+ import { WebSocketServer, WebSocket } from "ws";
2
2
  import type { Server as HttpServer } from "node:http";
3
+ import * as taskStore from "./task-store.js";
3
4
  /** Create a WebSocket server on top of an HTTP server that bridges JSON messages to gRPC operations. */
4
5
  export declare function createWsBridge(httpServer: HttpServer, verifyApiKey: (token: string) => boolean, validateCookie?: (cookieHeader: string) => boolean): WebSocketServer;
6
+ /** Start a new agent session for a task. Returns an error message string on failure, undefined on success. */
7
+ export declare function startTaskSession(ws: WebSocket | undefined, task: taskStore.TaskRow, options?: {
8
+ personaId?: string;
9
+ environmentId?: string;
10
+ notes?: string;
11
+ }): Promise<string | undefined>;
5
12
  //# sourceMappingURL=ws-bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ws-bridge.d.ts","sourceRoot":"","sources":["../src/ws-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAa,MAAM,IAAI,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAC;AAiEtD,wGAAwG;AACxG,wBAAgB,cAAc,CAC5B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,EACxC,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,GACjD,eAAe,CA6CjB"}
1
+ {"version":3,"file":"ws-bridge.d.ts","sourceRoot":"","sources":["../src/ws-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,WAAW,CAAC;AAetD,OAAO,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAmD7C,wGAAwG;AACxG,wBAAgB,cAAc,CAC5B,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,EACxC,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,GACjD,eAAe,CA6CjB;AA0HD,8GAA8G;AAC9G,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,SAAS,GAAG,SAAS,EACzB,IAAI,EAAE,SAAS,CAAC,OAAO,EACvB,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACvE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA6I7B"}
package/dist/ws-bridge.js CHANGED
@@ -16,13 +16,14 @@ import { v4 as uuid } from "uuid";
16
16
  import { join } from "node:path";
17
17
  import { LOGS_DIR, SESSION_STATUS, TERMINAL_SESSION_STATUSES, TASK_STATUS, DEFAULT_MCP_PORT, ROOT_TASK_ID, eventTypeToString, } from "@grackle-ai/common";
18
18
  import { resolvePersona } from "./resolve-persona.js";
19
+ import { fetchOrchestratorContext } from "./orchestrator-context.js";
19
20
  import * as settingsStore from "./settings-store.js";
20
21
  import { isAllowedSettingKey } from "./settings-store.js";
21
22
  import { grackleHome } from "./paths.js";
22
23
  import * as logWriter from "./log-writer.js";
23
24
  import { safeParseJsonArray } from "./json-helpers.js";
24
25
  import { logger } from "./logger.js";
25
- import { SystemPromptBuilder } from "./system-prompt-builder.js";
26
+ import { SystemPromptBuilder, buildTaskPrompt } from "./system-prompt-builder.js";
26
27
  import { slugify } from "./utils/slugify.js";
27
28
  import { processEventStream } from "./event-processor.js";
28
29
  import * as processorRegistry from "./processor-registry.js";
@@ -119,6 +120,7 @@ async function autoProvisionEnvironment(ws, environmentId, env, logContext) {
119
120
  emit("environment.changed", {});
120
121
  try {
121
122
  const config = safeParseAdapterConfig(env.adapterConfig, environmentId);
123
+ config.defaultRuntime = env.defaultRuntime;
122
124
  const powerlineToken = env.powerlineToken || "";
123
125
  for await (const provEvent of reconnectOrProvision(environmentId, adapter, config, powerlineToken, !!env.bootstrapped)) {
124
126
  logger.info({ environmentId, stage: provEvent.stage, ...logContext }, "Auto-provision progress");
@@ -165,15 +167,8 @@ async function autoProvisionEnvironment(ws, environmentId, env, logContext) {
165
167
  return undefined;
166
168
  }
167
169
  }
168
- /**
169
- * Start a new agent session for a task. Handles environment lookup,
170
- * auto-provisioning, session creation, spawning, and completion wiring.
171
- *
172
- * Returns undefined on success (or if the failure was already reported
173
- * to the client via WS, e.g. provisioning errors), or an error message
174
- * string for failures that need the caller to surface to the client.
175
- */
176
- async function startTaskSession(ws, task, options) {
170
+ /** Start a new agent session for a task. Returns an error message string on failure, undefined on success. */
171
+ export async function startTaskSession(ws, task, options) {
177
172
  const workspace = task.workspaceId ? workspaceStore.getWorkspace(task.workspaceId) : undefined;
178
173
  if (task.workspaceId && !workspace) {
179
174
  logger.warn({ taskId: task.id }, "startTaskSession failed: workspace not found");
@@ -185,11 +180,17 @@ async function startTaskSession(ws, task, options) {
185
180
  logger.warn({ taskId: task.id, environmentId }, "startTaskSession failed: environment not found");
186
181
  return `Environment not found: ${environmentId}`;
187
182
  }
188
- const conn = await autoProvisionEnvironment(ws, environmentId, env, {
189
- taskId: task.id,
190
- });
183
+ let conn;
184
+ if (ws) {
185
+ conn = await autoProvisionEnvironment(ws, environmentId, env, {
186
+ taskId: task.id,
187
+ });
188
+ }
189
+ else {
190
+ conn = adapterManager.getConnection(environmentId) ?? undefined;
191
+ }
191
192
  if (!conn) {
192
- return undefined;
193
+ return ws ? undefined : `Environment not connected: ${environmentId}`;
193
194
  }
194
195
  // Resolve persona via cascade (request → task → workspace → app default)
195
196
  let resolved;
@@ -205,16 +206,19 @@ async function startTaskSession(ws, task, options) {
205
206
  const freshTask = taskStore.getTask(task.id) || task;
206
207
  // For the root/System task, use the user's chat message (passed as notes)
207
208
  // as the initial prompt instead of the task title "System".
208
- // For regular tasks, the task title IS the prompt.
209
- const initialPrompt = freshTask.id === ROOT_TASK_ID
209
+ // For regular tasks, build the prompt from title + description.
210
+ const taskPrompt = freshTask.id === ROOT_TASK_ID
210
211
  ? (options?.notes || "")
211
- : freshTask.title;
212
+ : buildTaskPrompt(freshTask.title, freshTask.description, options?.notes);
213
+ const orchestratorCtx = freshTask.canDecompose && freshTask.depth <= 1
214
+ ? fetchOrchestratorContext(freshTask.workspaceId || "") : undefined;
212
215
  const systemContext = new SystemPromptBuilder({
213
216
  task: { title: freshTask.title, description: freshTask.description, notes: options?.notes || "" },
214
- canDecompose: freshTask.canDecompose,
215
- personaPrompt: systemPrompt,
217
+ taskId: freshTask.id, canDecompose: freshTask.canDecompose, personaPrompt: systemPrompt,
218
+ taskDepth: freshTask.depth, ...orchestratorCtx,
219
+ ...(orchestratorCtx && { triggerMode: "fresh" }),
216
220
  }).build();
217
- sessionStore.createSession(sessionId, environmentId, runtime, initialPrompt || freshTask.title, model, logPath, freshTask.id, resolved.personaId);
221
+ sessionStore.createSession(sessionId, environmentId, runtime, freshTask.title, model, logPath, freshTask.id, resolved.personaId);
218
222
  emit("task.started", {
219
223
  taskId: freshTask.id,
220
224
  sessionId,
@@ -245,7 +249,7 @@ async function startTaskSession(ws, task, options) {
245
249
  const powerlineReq = create(powerline.SpawnRequestSchema, {
246
250
  sessionId,
247
251
  runtime,
248
- prompt: initialPrompt,
252
+ prompt: taskPrompt,
249
253
  model,
250
254
  maxTurns,
251
255
  branch: freshTask.branch,
@@ -266,7 +270,7 @@ async function startTaskSession(ws, task, options) {
266
270
  workspaceId: freshTask.workspaceId ?? undefined,
267
271
  taskId: freshTask.id,
268
272
  systemContext,
269
- prompt: initialPrompt,
273
+ prompt: taskPrompt,
270
274
  });
271
275
  return undefined;
272
276
  }
@@ -1285,6 +1289,7 @@ async function handleMessage(ws, msg, subscriptions) {
1285
1289
  (async () => {
1286
1290
  try {
1287
1291
  const config = safeParseAdapterConfig(env.adapterConfig, environmentId);
1292
+ config.defaultRuntime = env.defaultRuntime;
1288
1293
  const powerlineToken = env.powerlineToken || "";
1289
1294
  for await (const event of reconnectOrProvision(environmentId, adapter, config, powerlineToken, !!env.bootstrapped)) {
1290
1295
  logger.info({ environmentId, stage: event.stage, message: event.message }, "Provision progress");