funolio-agent 1.0.7 → 1.0.48

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.
Files changed (239) hide show
  1. package/dist/agent-config.d.ts +9 -1
  2. package/dist/agent-config.d.ts.map +1 -1
  3. package/dist/agent-config.js +4 -1
  4. package/dist/agent-config.js.map +1 -1
  5. package/dist/approval.d.ts +1 -0
  6. package/dist/approval.d.ts.map +1 -1
  7. package/dist/approval.js +12 -0
  8. package/dist/approval.js.map +1 -1
  9. package/dist/auth/auto-detect.d.ts +11 -3
  10. package/dist/auth/auto-detect.d.ts.map +1 -1
  11. package/dist/auth/auto-detect.js +136 -168
  12. package/dist/auth/auto-detect.js.map +1 -1
  13. package/dist/auth/subscription-runtime.js +1 -1
  14. package/dist/auth/subscription-runtime.js.map +1 -1
  15. package/dist/auto-organizer.d.ts.map +1 -1
  16. package/dist/auto-organizer.js +4 -3
  17. package/dist/auto-organizer.js.map +1 -1
  18. package/dist/backfill.d.ts.map +1 -1
  19. package/dist/backfill.js +34 -30
  20. package/dist/backfill.js.map +1 -1
  21. package/dist/bot-manager.d.ts +4 -8
  22. package/dist/bot-manager.d.ts.map +1 -1
  23. package/dist/bot-manager.js +31 -160
  24. package/dist/bot-manager.js.map +1 -1
  25. package/dist/clerk-model.d.ts +15 -7
  26. package/dist/clerk-model.d.ts.map +1 -1
  27. package/dist/clerk-model.js +78 -43
  28. package/dist/clerk-model.js.map +1 -1
  29. package/dist/cli-session-epoch.d.ts +10 -0
  30. package/dist/cli-session-epoch.d.ts.map +1 -0
  31. package/dist/cli-session-epoch.js +61 -0
  32. package/dist/cli-session-epoch.js.map +1 -0
  33. package/dist/cli.js +7 -2
  34. package/dist/cli.js.map +1 -1
  35. package/dist/commands/import-history.js +5 -1
  36. package/dist/commands/import-history.js.map +1 -1
  37. package/dist/commands/init.d.ts.map +1 -1
  38. package/dist/commands/init.js +30 -1
  39. package/dist/commands/init.js.map +1 -1
  40. package/dist/commands/pool.js +1 -1
  41. package/dist/commands/pool.js.map +1 -1
  42. package/dist/commands/setup.d.ts +37 -0
  43. package/dist/commands/setup.d.ts.map +1 -1
  44. package/dist/commands/setup.js +146 -43
  45. package/dist/commands/setup.js.map +1 -1
  46. package/dist/commands/start.d.ts.map +1 -1
  47. package/dist/commands/start.js +117 -255
  48. package/dist/commands/start.js.map +1 -1
  49. package/dist/config-cleanup.d.ts.map +1 -1
  50. package/dist/config-cleanup.js +2 -1
  51. package/dist/config-cleanup.js.map +1 -1
  52. package/dist/config.d.ts +6 -9
  53. package/dist/config.d.ts.map +1 -1
  54. package/dist/config.js +7 -18
  55. package/dist/config.js.map +1 -1
  56. package/dist/context-window.d.ts +33 -5
  57. package/dist/context-window.d.ts.map +1 -1
  58. package/dist/context-window.js +122 -21
  59. package/dist/context-window.js.map +1 -1
  60. package/dist/eval/orchestrator-front-door-replay.js +1 -1
  61. package/dist/eval/orchestrator-front-door-replay.js.map +1 -1
  62. package/dist/eval/policy-detection-replay.js +1 -1
  63. package/dist/eval/policy-detection-replay.js.map +1 -1
  64. package/dist/import-parser-core.d.ts.map +1 -1
  65. package/dist/import-parser-core.js +74 -8
  66. package/dist/import-parser-core.js.map +1 -1
  67. package/dist/integration-tokens.d.ts +1 -6
  68. package/dist/integration-tokens.d.ts.map +1 -1
  69. package/dist/integration-tokens.js +38 -40
  70. package/dist/integration-tokens.js.map +1 -1
  71. package/dist/local-cli-pty-manager.d.ts +50 -0
  72. package/dist/local-cli-pty-manager.d.ts.map +1 -0
  73. package/dist/local-cli-pty-manager.js +645 -0
  74. package/dist/local-cli-pty-manager.js.map +1 -0
  75. package/dist/local-data.d.ts +89 -6
  76. package/dist/local-data.d.ts.map +1 -1
  77. package/dist/local-data.js +600 -63
  78. package/dist/local-data.js.map +1 -1
  79. package/dist/local-db.d.ts.map +1 -1
  80. package/dist/local-db.js +74 -1
  81. package/dist/local-db.js.map +1 -1
  82. package/dist/local-funnel.d.ts +0 -7
  83. package/dist/local-funnel.d.ts.map +1 -1
  84. package/dist/local-funnel.js +22 -30
  85. package/dist/local-funnel.js.map +1 -1
  86. package/dist/local-import-worker.d.ts.map +1 -1
  87. package/dist/local-import-worker.js +49 -4
  88. package/dist/local-import-worker.js.map +1 -1
  89. package/dist/local-memory-search.d.ts +1 -0
  90. package/dist/local-memory-search.d.ts.map +1 -1
  91. package/dist/local-memory-search.js +107 -21
  92. package/dist/local-memory-search.js.map +1 -1
  93. package/dist/local-server.d.ts +21 -0
  94. package/dist/local-server.d.ts.map +1 -1
  95. package/dist/local-server.js +1057 -501
  96. package/dist/local-server.js.map +1 -1
  97. package/dist/mcp/bridge-server.d.ts.map +1 -1
  98. package/dist/mcp/bridge-server.js +2 -1
  99. package/dist/mcp/bridge-server.js.map +1 -1
  100. package/dist/mcp/local-memory-server.d.ts +6 -1
  101. package/dist/mcp/local-memory-server.d.ts.map +1 -1
  102. package/dist/mcp/local-memory-server.js +38 -13
  103. package/dist/mcp/local-memory-server.js.map +1 -1
  104. package/dist/mcp/manager.d.ts +3 -22
  105. package/dist/mcp/manager.d.ts.map +1 -1
  106. package/dist/mcp/manager.js +66 -320
  107. package/dist/mcp/manager.js.map +1 -1
  108. package/dist/memory-extraction.d.ts +2 -0
  109. package/dist/memory-extraction.d.ts.map +1 -1
  110. package/dist/memory-extraction.js +3 -1
  111. package/dist/memory-extraction.js.map +1 -1
  112. package/dist/message-loop.d.ts +1 -3
  113. package/dist/message-loop.d.ts.map +1 -1
  114. package/dist/message-loop.js +220 -437
  115. package/dist/message-loop.js.map +1 -1
  116. package/dist/mqtt-client.d.ts +2 -28
  117. package/dist/mqtt-client.d.ts.map +1 -1
  118. package/dist/mqtt-client.js +2 -2
  119. package/dist/mqtt-client.js.map +1 -1
  120. package/dist/oauth.d.ts +6 -0
  121. package/dist/oauth.d.ts.map +1 -1
  122. package/dist/oauth.js +91 -0
  123. package/dist/oauth.js.map +1 -1
  124. package/dist/orchestration/front-door-policy.d.ts +5 -2
  125. package/dist/orchestration/front-door-policy.d.ts.map +1 -1
  126. package/dist/orchestration/front-door-policy.js +25 -28
  127. package/dist/orchestration/front-door-policy.js.map +1 -1
  128. package/dist/orchestration/orchestrator-blocked-prompt.d.ts +2 -1
  129. package/dist/orchestration/orchestrator-blocked-prompt.d.ts.map +1 -1
  130. package/dist/orchestration/orchestrator-blocked-prompt.js +12 -1
  131. package/dist/orchestration/orchestrator-blocked-prompt.js.map +1 -1
  132. package/dist/orchestration/orchestrator-final-response-prompt.d.ts +4 -1
  133. package/dist/orchestration/orchestrator-final-response-prompt.d.ts.map +1 -1
  134. package/dist/orchestration/orchestrator-final-response-prompt.js +9 -7
  135. package/dist/orchestration/orchestrator-final-response-prompt.js.map +1 -1
  136. package/dist/orchestration/orchestrator-operating-prompt.d.ts +11 -0
  137. package/dist/orchestration/orchestrator-operating-prompt.d.ts.map +1 -1
  138. package/dist/orchestration/orchestrator-operating-prompt.js +67 -44
  139. package/dist/orchestration/orchestrator-operating-prompt.js.map +1 -1
  140. package/dist/orchestration/worker-operating-prompt.d.ts +2 -0
  141. package/dist/orchestration/worker-operating-prompt.d.ts.map +1 -1
  142. package/dist/orchestration/worker-operating-prompt.js +41 -2
  143. package/dist/orchestration/worker-operating-prompt.js.map +1 -1
  144. package/dist/orchestrator.d.ts +17 -0
  145. package/dist/orchestrator.d.ts.map +1 -1
  146. package/dist/orchestrator.js +328 -166
  147. package/dist/orchestrator.js.map +1 -1
  148. package/dist/prompt-template.js +3 -3
  149. package/dist/prompt-template.js.map +1 -1
  150. package/dist/providers/anthropic.d.ts +0 -5
  151. package/dist/providers/anthropic.d.ts.map +1 -1
  152. package/dist/providers/anthropic.js +29 -75
  153. package/dist/providers/anthropic.js.map +1 -1
  154. package/dist/providers/claude-cli-prompt.d.ts.map +1 -1
  155. package/dist/providers/claude-cli-prompt.js +22 -6
  156. package/dist/providers/claude-cli-prompt.js.map +1 -1
  157. package/dist/providers/claude-cli.d.ts.map +1 -1
  158. package/dist/providers/claude-cli.js +36 -142
  159. package/dist/providers/claude-cli.js.map +1 -1
  160. package/dist/providers/codex-cli.d.ts.map +1 -1
  161. package/dist/providers/codex-cli.js +148 -74
  162. package/dist/providers/codex-cli.js.map +1 -1
  163. package/dist/providers/google.d.ts.map +1 -1
  164. package/dist/providers/google.js +4 -2
  165. package/dist/providers/google.js.map +1 -1
  166. package/dist/providers/index.d.ts +13 -0
  167. package/dist/providers/index.d.ts.map +1 -1
  168. package/dist/providers/index.js.map +1 -1
  169. package/dist/providers/openai.d.ts.map +1 -1
  170. package/dist/providers/openai.js +27 -2
  171. package/dist/providers/openai.js.map +1 -1
  172. package/dist/runtime-context.d.ts +10 -0
  173. package/dist/runtime-context.d.ts.map +1 -0
  174. package/dist/runtime-context.js +30 -0
  175. package/dist/runtime-context.js.map +1 -0
  176. package/dist/storage-mode.d.ts +5 -0
  177. package/dist/storage-mode.d.ts.map +1 -0
  178. package/dist/storage-mode.js +21 -0
  179. package/dist/storage-mode.js.map +1 -0
  180. package/dist/subagent/queue.d.ts.map +1 -1
  181. package/dist/subagent/queue.js +1 -0
  182. package/dist/subagent/queue.js.map +1 -1
  183. package/dist/summarization-pipeline.d.ts +10 -0
  184. package/dist/summarization-pipeline.d.ts.map +1 -1
  185. package/dist/summarization-pipeline.js +147 -34
  186. package/dist/summarization-pipeline.js.map +1 -1
  187. package/dist/tool-permissions.d.ts +2 -0
  188. package/dist/tool-permissions.d.ts.map +1 -0
  189. package/dist/tool-permissions.js +25 -0
  190. package/dist/tool-permissions.js.map +1 -0
  191. package/dist/tools/analyze-image.js +2 -2
  192. package/dist/tools/analyze-image.js.map +1 -1
  193. package/dist/tools/edit-file.js +3 -3
  194. package/dist/tools/edit-file.js.map +1 -1
  195. package/dist/tools/index.d.ts +7 -8
  196. package/dist/tools/index.d.ts.map +1 -1
  197. package/dist/tools/index.js +106 -60
  198. package/dist/tools/index.js.map +1 -1
  199. package/dist/tools/list-directory.js +7 -4
  200. package/dist/tools/list-directory.js.map +1 -1
  201. package/dist/tools/read-file.js +3 -3
  202. package/dist/tools/read-file.js.map +1 -1
  203. package/dist/tools/run-command.js +3 -3
  204. package/dist/tools/run-command.js.map +1 -1
  205. package/dist/tools/sandbox.d.ts +10 -5
  206. package/dist/tools/sandbox.d.ts.map +1 -1
  207. package/dist/tools/sandbox.js +41 -13
  208. package/dist/tools/sandbox.js.map +1 -1
  209. package/dist/tools/search-codebase.js +2 -2
  210. package/dist/tools/search-codebase.js.map +1 -1
  211. package/dist/tools/search-local-memory.d.ts.map +1 -1
  212. package/dist/tools/search-local-memory.js +19 -8
  213. package/dist/tools/search-local-memory.js.map +1 -1
  214. package/dist/tools/search-memory.d.ts.map +1 -1
  215. package/dist/tools/search-memory.js +9 -3
  216. package/dist/tools/search-memory.js.map +1 -1
  217. package/dist/tools/spawn-subagent.d.ts.map +1 -1
  218. package/dist/tools/spawn-subagent.js +1 -0
  219. package/dist/tools/spawn-subagent.js.map +1 -1
  220. package/dist/tools/write-file.js +3 -3
  221. package/dist/tools/write-file.js.map +1 -1
  222. package/dist/types.d.ts +5 -0
  223. package/dist/types.d.ts.map +1 -1
  224. package/dist/types.js +0 -3
  225. package/dist/types.js.map +1 -1
  226. package/dist/verification/index.js +2 -2
  227. package/dist/verification/index.js.map +1 -1
  228. package/dist/wizard-state.d.ts.map +1 -1
  229. package/dist/wizard-state.js +16 -2
  230. package/dist/wizard-state.js.map +1 -1
  231. package/dist/wizard-support.d.ts +2 -2
  232. package/dist/wizard-support.d.ts.map +1 -1
  233. package/dist/wizard-support.js +88 -99
  234. package/dist/wizard-support.js.map +1 -1
  235. package/dist/workflow-engine.d.ts +9 -3
  236. package/dist/workflow-engine.d.ts.map +1 -1
  237. package/dist/workflow-engine.js +378 -82
  238. package/dist/workflow-engine.js.map +1 -1
  239. package/package.json +2 -1
@@ -61,6 +61,7 @@ const state_1 = require("./orchestration/state");
61
61
  const orchestrator_profile_1 = require("./orchestrator-profile");
62
62
  const context_window_1 = require("./context-window");
63
63
  const data = __importStar(require("./local-data"));
64
+ const storage_mode_1 = require("./storage-mode");
64
65
  const fs = __importStar(require("fs"));
65
66
  const os = __importStar(require("os"));
66
67
  const path = __importStar(require("path"));
@@ -83,10 +84,20 @@ const ORCHESTRATION_NODE_RETRY_LIMITS = {
83
84
  class OrchestratorAgent {
84
85
  clerk;
85
86
  workflowEngine;
87
+ lastResponseMeta = null;
86
88
  constructor(clerk, workflowEngine) {
87
89
  this.clerk = clerk;
88
90
  this.workflowEngine = workflowEngine;
89
91
  }
92
+ isLocalDesktopRuntime() {
93
+ return this.workflowEngine.getRuntimeMode() === 'local_desktop';
94
+ }
95
+ getLastResponseMeta() {
96
+ return this.lastResponseMeta;
97
+ }
98
+ setLastResponseMeta(meta) {
99
+ this.lastResponseMeta = meta;
100
+ }
90
101
  async runNodeWithRetry(node, state, work) {
91
102
  const maxRetries = ORCHESTRATION_NODE_RETRY_LIMITS[node] ?? 0;
92
103
  let attempt = 0;
@@ -104,11 +115,60 @@ class OrchestratorAgent {
104
115
  }
105
116
  }
106
117
  }
118
+ getAgentRoutingReferences(agent) {
119
+ const refs = new Set();
120
+ const add = (value) => {
121
+ const trimmed = String(value || '').trim();
122
+ if (trimmed)
123
+ refs.add(trimmed);
124
+ };
125
+ add(agent.name);
126
+ const modelProviderText = `${agent.provider || ''} ${agent.model || ''}`.toLowerCase();
127
+ if (modelProviderText.includes('claude'))
128
+ add('Claude');
129
+ if (modelProviderText.includes('codex'))
130
+ add('Codex');
131
+ if (/\bgpt\b|gpt-/.test(modelProviderText))
132
+ add('GPT');
133
+ return Array.from(refs);
134
+ }
135
+ describeAgentResponsibilities(agent) {
136
+ const candidates = [
137
+ agent.purpose_md,
138
+ agent.identity_summary,
139
+ agent.skills_md,
140
+ agent.role_label,
141
+ agent.role_class,
142
+ ].map((value) => String(value || '').replace(/\s+/g, ' ').trim()).filter(Boolean);
143
+ for (const candidate of candidates) {
144
+ const cleaned = candidate
145
+ .replace(/^#+\s*/g, '')
146
+ .replace(/^\-\s*/g, '')
147
+ .replace(/^you are\s+/i, '')
148
+ .replace(/^role:\s*/i, '')
149
+ .trim();
150
+ if (cleaned) {
151
+ return cleaned.length > 180 ? `${cleaned.slice(0, 177).trim()}...` : cleaned;
152
+ }
153
+ }
154
+ const normalizedRole = String(agent.role_class || agent.role_label || '').trim().toLowerCase();
155
+ if (normalizedRole === 'code' || normalizedRole === 'coding') {
156
+ return 'implementation, code changes, fixes, and app changes';
157
+ }
158
+ if (normalizedRole === 'qa') {
159
+ return 'QA, review, verification, and testing';
160
+ }
161
+ if (normalizedRole === 'research') {
162
+ return 'brainstorming, research, planning, and evaluation';
163
+ }
164
+ return 'general assistance';
165
+ }
107
166
  /**
108
167
  * Main entry point. User sends a message, O handles everything.
109
168
  * Returns O's response to display to the user.
110
169
  */
111
170
  async handleUserMessage(prompt, conversationId, opts) {
171
+ this.lastResponseMeta = null;
112
172
  const conversation = conversationId ? data.getConversation(conversationId) : undefined;
113
173
  const routingMode = conversation?.routing_mode;
114
174
  const conversationProjectId = conversation?.project_id || opts.projectId;
@@ -137,6 +197,13 @@ class OrchestratorAgent {
137
197
  }
138
198
  const promptAssignments = this.parseRoleAssignments(prompt);
139
199
  const selectedOrchestrator = this.resolveSelectedOrchestratorBot(conversation);
200
+ if (selectedOrchestrator) {
201
+ this.setLastResponseMeta({
202
+ agentName: 'Orchestrator',
203
+ botId: selectedOrchestrator.id,
204
+ modelLabel: null,
205
+ });
206
+ }
140
207
  if (selectedOrchestrator) {
141
208
  orchestrationState.orchestratorBotId = selectedOrchestrator.id;
142
209
  }
@@ -197,26 +264,40 @@ class OrchestratorAgent {
197
264
  if (frontDoorResult !== null)
198
265
  return frontDoorResult;
199
266
  }
200
- this.reportHiddenRoleProgress(opts, 'intent_classifier', 'Classifying request');
201
- (0, state_1.addHelperRoleUsage)(orchestrationState, 'intent_classifier');
202
- const routedPlan = await this.decomposeAndRouteUserMessage(prompt, conversationId, opts.projectId, routingMode);
203
- let intent = routedPlan?.intents.length === 1
204
- ? this.buildIntentAnalysisFromRoutedIntent(routedPlan.intents[0])
205
- : undefined;
206
- if (intent) {
267
+ let intent;
268
+ let routedPlan;
269
+ if (selectedOrchestrator) {
270
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', 'Recovering routing from code fallback');
271
+ intent = this.fallbackClassifyUserMessage(prompt, routingMode);
272
+ orchestrationState.fallbackUsed = true;
207
273
  if (this.applyClassificationGuards(prompt, intent, routingMode)) {
208
274
  (0, state_1.markMisrouteCorrected)(orchestrationState);
209
275
  }
210
- this.reportHiddenRoleProgress(opts, 'intent_classifier', `Routed single intent as ${this.describeIntentForActivity(intent)}`);
276
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', `Classified request as ${this.describeIntentForActivity(intent)}`);
211
277
  }
212
278
  else {
213
279
  this.reportHiddenRoleProgress(opts, 'intent_classifier', 'Classifying request');
214
- intent = await this.classifyUserMessage(prompt, opts.projectId, routingMode);
215
- orchestrationState.fallbackUsed = true;
216
- if (this.applyClassificationGuards(prompt, intent, routingMode)) {
217
- (0, state_1.markMisrouteCorrected)(orchestrationState);
280
+ (0, state_1.addHelperRoleUsage)(orchestrationState, 'intent_classifier');
281
+ routedPlan = await this.decomposeAndRouteUserMessage(prompt, conversationId, opts.projectId, routingMode);
282
+ const routedIntent = routedPlan?.intents.length === 1
283
+ ? this.buildIntentAnalysisFromRoutedIntent(routedPlan.intents[0])
284
+ : undefined;
285
+ if (routedIntent) {
286
+ intent = routedIntent;
287
+ if (this.applyClassificationGuards(prompt, intent, routingMode)) {
288
+ (0, state_1.markMisrouteCorrected)(orchestrationState);
289
+ }
290
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', `Routed single intent as ${this.describeIntentForActivity(intent)}`);
291
+ }
292
+ else {
293
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', 'Classifying request');
294
+ intent = await this.classifyUserMessage(prompt, opts.projectId, routingMode);
295
+ orchestrationState.fallbackUsed = true;
296
+ if (this.applyClassificationGuards(prompt, intent, routingMode)) {
297
+ (0, state_1.markMisrouteCorrected)(orchestrationState);
298
+ }
299
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', `Classified request as ${this.describeIntentForActivity(intent)}`);
218
300
  }
219
- this.reportHiddenRoleProgress(opts, 'intent_classifier', `Classified request as ${this.describeIntentForActivity(intent)}`);
220
301
  }
221
302
  orchestrationState.intent = intent.intent;
222
303
  orchestrationState.taskType = intent.primaryMode;
@@ -400,6 +481,7 @@ class OrchestratorAgent {
400
481
  const orchestratorOwnsCoding = orchestratorName.length > 0
401
482
  && (orchestratorName === codingOwner
402
483
  || roleClass === 'code'
484
+ || roleClass === 'coding'
403
485
  || roleClass === 'coder'
404
486
  || roleClass === 'builder'
405
487
  || roleClass === 'developer');
@@ -1276,9 +1358,12 @@ class OrchestratorAgent {
1276
1358
  return finalize(this.formatProjectKnowledgeResponse(overview, []), 'Handled project knowledge request directly.');
1277
1359
  }
1278
1360
  if (/\b(hello|hi|hey|are you there|good morning|good afternoon|good evening)\b/i.test(normalized)) {
1279
- return 'I’m here and ready. Tell me what you want me to coordinate.';
1361
+ return "I'm here and ready. Tell me what you want me to coordinate.";
1362
+ }
1363
+ if (/\b(thanks|thank you|great work|good work|nice work|well done|awesome|perfect|just complementing you|just complimenting you|complimenting you|nothing else)\b/i.test(normalized)) {
1364
+ return "Thanks. I'm here when you need me.";
1280
1365
  }
1281
- return 'I’m with you. Tell me whether this is a policy change, a status check, a single-worker request, or a full team workflow, and I’ll take the right path.';
1366
+ return "I'm here. Tell me what you want to do next.";
1282
1367
  }
1283
1368
  formatPolicyUpdateResponse(prompt, intent, assignments, overview) {
1284
1369
  const lines = ['I’m reading that as an orchestration policy update, not as immediate work.'];
@@ -1464,11 +1549,15 @@ class OrchestratorAgent {
1464
1549
  .map((agent) => ({
1465
1550
  name: agent.name,
1466
1551
  roleLabel: agent.role_label || agent.role_class || 'general',
1552
+ references: this.getAgentRoutingReferences(agent),
1553
+ responsibilities: this.describeAgentResponsibilities(agent),
1467
1554
  }));
1468
1555
  const signalSpecialists = allProfiles
1469
1556
  .map((agent) => ({
1470
1557
  name: agent.name,
1471
1558
  roleLabel: agent.role_label || agent.role_class || 'general',
1559
+ references: this.getAgentRoutingReferences(agent),
1560
+ responsibilities: this.describeAgentResponsibilities(agent),
1472
1561
  }));
1473
1562
  // Build workflow names list from enabled templates
1474
1563
  const workflowNames = [];
@@ -1487,6 +1576,17 @@ class OrchestratorAgent {
1487
1576
  roleAssignments,
1488
1577
  });
1489
1578
  const normalizeName = (value) => String(value || '').trim().toLowerCase();
1579
+ const systemSuggestion = this.buildFrontDoorSystemSuggestion(prompt, frontDoorSignals, promptAssignments, roleAssignments);
1580
+ if (systemSuggestion) {
1581
+ this.recordOrchestrationAudit(state, 'understand_request', 'path_selected', systemSuggestion.reason, {
1582
+ decisionMode: systemSuggestion.mode,
1583
+ delegateTarget: systemSuggestion.delegateTarget || null,
1584
+ delegateRole: systemSuggestion.delegateRole || null,
1585
+ suggestionConfidence: systemSuggestion.confidence || null,
1586
+ fastPath: false,
1587
+ });
1588
+ }
1589
+ const useSinglePassOrchestrator = this.isLocalDesktopRuntime();
1490
1590
  const operatingPrompt = (0, orchestrator_operating_prompt_1.buildOrchestratorOperatingPrompt)({
1491
1591
  orchestratorName: orchestratorBot.name,
1492
1592
  primaryRole: orchestratorBot.role_label || orchestratorBot.role_class || null,
@@ -1501,6 +1601,7 @@ class OrchestratorAgent {
1501
1601
  explicitWorkflowKeyword: frontDoorSignals.explicitWorkflowKeyword,
1502
1602
  explicitDelegationKeyword: frontDoorSignals.explicitDelegationKeyword,
1503
1603
  },
1604
+ systemSuggestion,
1504
1605
  projectName: overview?.project.name || project?.name || null,
1505
1606
  projectFolder: project?.folder || overview?.project.folder || null,
1506
1607
  effectivePolicy: (0, policy_prompt_1.buildEffectivePolicyPromptSection)(policy, {
@@ -1509,6 +1610,7 @@ class OrchestratorAgent {
1509
1610
  }),
1510
1611
  recentSummary: recentSummaryText,
1511
1612
  lastFiveTurns: recentTurnsText,
1613
+ decisionOnly: !useSinglePassOrchestrator,
1512
1614
  });
1513
1615
  const decisionPrompt = [
1514
1616
  'This is a routing decision only. Do not perform the task.',
@@ -1525,14 +1627,44 @@ class OrchestratorAgent {
1525
1627
  this.reportHiddenRoleProgress(opts, 'orchestrator', 'Still understanding the request');
1526
1628
  }, 8_000);
1527
1629
  try {
1528
- const result = await this.runNodeWithRetry('understand_request', state, async () => this.workflowEngine.execute(decisionPrompt, null, orchestratorBot.id, {
1529
- disableDecomposition: true,
1530
- disableTools: true,
1531
- projectId: projectId || null,
1532
- workspacePath: project?.folder?.trim() || undefined,
1533
- systemPromptOverride: operatingPrompt,
1534
- }));
1535
- decision = (0, orchestrator_operating_prompt_1.parseOrchestratorFrontDoorDecision)(result.mergedResult);
1630
+ if (useSinglePassOrchestrator) {
1631
+ const result = await this.runNodeWithRetry('understand_request', state, async () => this.workflowEngine.execute(prompt, conversationId, orchestratorBot.id, {
1632
+ disableDecomposition: true,
1633
+ isOrchestrated: false,
1634
+ projectId: projectId || null,
1635
+ workspacePath: project?.folder?.trim() || undefined,
1636
+ persistConversationMessages: false,
1637
+ workerMode: false,
1638
+ systemPromptOverride: operatingPrompt,
1639
+ onWorkerChunk: opts.onWorkerChunk,
1640
+ onProgress: (progress) => {
1641
+ if (progress.event === 'step-failed') {
1642
+ this.reportHiddenRoleProgress(opts, 'orchestrator', `${progress.step.agentName} hit an issue while working on the request`);
1643
+ }
1644
+ },
1645
+ }));
1646
+ const parsed = (0, orchestrator_operating_prompt_1.parseOrchestratorFrontDoorDecision)(result.mergedResult);
1647
+ if (parsed && ['delegate', 'workflow', 'clarify', 'respond'].includes(parsed.mode)) {
1648
+ decision = parsed;
1649
+ }
1650
+ else if (parsed?.mode === 'execute_self') {
1651
+ decision = parsed;
1652
+ }
1653
+ else {
1654
+ clearInterval(understandingHeartbeat);
1655
+ return (await this.finalizeOwnedExecutionResult(result, prompt, conversationId, orchestratorBot, roleAssignments, project, opts, state)).response;
1656
+ }
1657
+ }
1658
+ else {
1659
+ const result = await this.runNodeWithRetry('understand_request', state, async () => this.workflowEngine.execute(decisionPrompt, null, orchestratorBot.id, {
1660
+ disableDecomposition: true,
1661
+ disableTools: true,
1662
+ projectId: projectId || null,
1663
+ workspacePath: project?.folder?.trim() || undefined,
1664
+ systemPromptOverride: operatingPrompt,
1665
+ }));
1666
+ decision = (0, orchestrator_operating_prompt_1.parseOrchestratorFrontDoorDecision)(result.mergedResult);
1667
+ }
1536
1668
  }
1537
1669
  catch {
1538
1670
  decision = null;
@@ -1540,9 +1672,19 @@ class OrchestratorAgent {
1540
1672
  finally {
1541
1673
  clearInterval(understandingHeartbeat);
1542
1674
  }
1675
+ if (!decision && systemSuggestion) {
1676
+ decision = {
1677
+ mode: systemSuggestion.mode,
1678
+ reason: systemSuggestion.reason,
1679
+ delegate_target: systemSuggestion.delegateTarget || undefined,
1680
+ delegate_role: systemSuggestion.delegateRole || undefined,
1681
+ delegate_request: systemSuggestion.mode === 'delegate' ? prompt : undefined,
1682
+ workflow_request: systemSuggestion.mode === 'workflow' ? prompt : undefined,
1683
+ };
1684
+ }
1543
1685
  if (!decision) {
1544
- const fallbackTaskType = (0, front_door_policy_1.inferFrontDoorTaskType)(prompt);
1545
1686
  const promptAssignedRoles = [promptAssignments.coding, promptAssignments.qa, promptAssignments.research].filter(Boolean).length;
1687
+ const fallbackTaskType = (0, front_door_policy_1.inferFrontDoorTaskType)(prompt);
1546
1688
  if (frontDoorSignals.preferredMode === 'workflow') {
1547
1689
  decision = {
1548
1690
  mode: 'workflow',
@@ -1572,34 +1714,59 @@ class OrchestratorAgent {
1572
1714
  delegate_request: prompt,
1573
1715
  };
1574
1716
  }
1575
- else if (fallbackTaskType === 'coding' && roleAssignments.coding) {
1576
- decision = {
1577
- mode: 'delegate',
1578
- reason: 'Code-based front door recovered routing for a coding task owned by the coding worker.',
1579
- delegate_target: roleAssignments.coding,
1580
- delegate_role: 'coding',
1581
- delegate_request: prompt,
1582
- };
1583
- }
1584
- else if (fallbackTaskType === 'qa' && roleAssignments.qa) {
1585
- decision = {
1586
- mode: 'delegate',
1587
- reason: 'Code-based front door recovered routing for a QA task owned by the QA worker.',
1588
- delegate_target: roleAssignments.qa,
1589
- delegate_role: 'qa',
1590
- delegate_request: prompt,
1591
- };
1592
- }
1593
- else if (fallbackTaskType === 'research' && roleAssignments.research) {
1717
+ else {
1594
1718
  decision = {
1595
- mode: 'delegate',
1596
- reason: 'Code-based front door recovered routing for a research task owned by the research worker.',
1597
- delegate_target: roleAssignments.research,
1598
- delegate_role: 'research',
1599
- delegate_request: prompt,
1719
+ mode: 'execute_self',
1720
+ reason: 'No strong routing evidence was found. Handle the request directly.',
1600
1721
  };
1601
1722
  }
1602
1723
  }
1724
+ return this.handleFrontDoorDecision(decision, prompt, conversationId, opts, orchestratorBot, projectId, project, policy, promptAssignments, roleAssignments, overview, state, signalSpecialists, workflowNames);
1725
+ }
1726
+ buildFrontDoorSystemSuggestion(prompt, frontDoorSignals, promptAssignments, roleAssignments) {
1727
+ const normalizeName = (value) => String(value || '').trim().toLowerCase();
1728
+ const taskType = (0, front_door_policy_1.inferFrontDoorTaskType)(prompt);
1729
+ if (taskType === 'policy' || taskType === 'status' || taskType === 'conversation')
1730
+ return null;
1731
+ if (/\bwhat do you think|can we|should we|do you agree|need to discuss|discuss more|is this a good idea|thoughts\??|recommend(?:ation)?\b/i.test(prompt)) {
1732
+ return null;
1733
+ }
1734
+ if ((0, front_door_policy_1.isContextualWorkerReferencePrompt)(prompt))
1735
+ return null;
1736
+ const promptAssignedRoles = [promptAssignments.coding, promptAssignments.qa, promptAssignments.research].filter(Boolean).length;
1737
+ const multiWorkerRequested = frontDoorSignals.matchedWorkflowNames.length > 0
1738
+ || frontDoorSignals.preferredMode === 'workflow'
1739
+ || taskType === 'mixed'
1740
+ || promptAssignedRoles >= 2
1741
+ || frontDoorSignals.matchedBots.length >= 2
1742
+ || frontDoorSignals.matchedRoles.length >= 2;
1743
+ if (multiWorkerRequested) {
1744
+ return {
1745
+ mode: 'workflow',
1746
+ reason: frontDoorSignals.matchedWorkflowNames.length > 0
1747
+ ? 'Code-based front door suggests workflow because the prompt names an available workflow.'
1748
+ : 'Code-based front door suggests workflow because the prompt clearly points to multi-worker or staged work.',
1749
+ confidence: frontDoorSignals.matchedWorkflowNames.length > 0 || frontDoorSignals.explicitOrchestrationRequested ? 'high' : 'medium',
1750
+ };
1751
+ }
1752
+ const preferredTarget = frontDoorSignals.preferredTarget;
1753
+ if (!preferredTarget)
1754
+ return null;
1755
+ const delegateRole = frontDoorSignals.matchedRoles[0]
1756
+ || (normalizeName(preferredTarget) === normalizeName(roleAssignments.qa) ? 'qa'
1757
+ : normalizeName(preferredTarget) === normalizeName(roleAssignments.research) ? 'research'
1758
+ : 'coding');
1759
+ return {
1760
+ mode: 'delegate',
1761
+ reason: frontDoorSignals.explicitOrchestrationRequested
1762
+ ? 'Code-based front door suggests delegate because the prompt explicitly requests worker assignment or TODO handling.'
1763
+ : 'Code-based front door suggests delegate because the prompt clearly assigns one available worker or role.',
1764
+ confidence: frontDoorSignals.explicitOrchestrationRequested ? 'high' : 'medium',
1765
+ delegateTarget: preferredTarget,
1766
+ delegateRole,
1767
+ };
1768
+ }
1769
+ async handleFrontDoorDecision(decision, prompt, conversationId, opts, orchestratorBot, projectId, project, policy, promptAssignments, roleAssignments, overview, state, signalSpecialists, workflowNames) {
1603
1770
  if (!decision) {
1604
1771
  this.recordOrchestrationAudit(state, 'understand_request', 'blocked', 'Orchestrator-first decision phase did not return valid JSON. Falling back to legacy clerk chain.', { featureFlag: 'orchestrator_v2_enabled' });
1605
1772
  return null;
@@ -1949,6 +2116,7 @@ class OrchestratorAgent {
1949
2116
  const result = await this.runNodeWithRetry('run_workflow', state, async () => this.workflowEngine.execute(taskPrompt, conversationId, owner.id, {
1950
2117
  apiKey: this.resolveApiKey(owner.provider),
1951
2118
  taskId: task.id,
2119
+ stepDescription: task.title,
1952
2120
  disableDecomposition: true,
1953
2121
  isOrchestrated: true,
1954
2122
  projectId: project?.id || opts.projectId || state.projectId || null,
@@ -1989,6 +2157,7 @@ class OrchestratorAgent {
1989
2157
  async handleBlockedTodoTask(task, opts, state, orchestratorBot, project) {
1990
2158
  const policy = data.getEffectiveOrchestrationPolicy(project?.id || opts.projectId || state.projectId || undefined);
1991
2159
  const artifacts = data.listTodoArtifactsForTask(task.id, 'active').map((item) => item.path_or_ref);
2160
+ const completedTasks = data.listCompletedTodoTasksForConversation(task.conversation_id || state.conversationId || '');
1992
2161
  const workerRole = task.task_type || task.next_worker_role || 'general';
1993
2162
  const blockedPrompt = (0, orchestrator_blocked_prompt_1.buildBlockedWorkerOrchestratorPrompt)({
1994
2163
  orchestratorName: orchestratorBot.name,
@@ -2001,33 +2170,33 @@ class OrchestratorAgent {
2001
2170
  workspacePath: project?.folder || null,
2002
2171
  effectivePolicy: policy,
2003
2172
  artifactRefs: artifacts,
2173
+ completedTasks,
2004
2174
  blockerSummary: task.blocker_summary || 'Worker is blocked.',
2005
2175
  checkedContext: task.blocker_checked || 'No checked context was recorded.',
2006
2176
  userQuestion: task.blocker_question || 'What should happen next?',
2007
2177
  });
2008
- const response = await this.runNodeWithRetry('finalize_response', state, async () => {
2009
- const result = await this.workflowEngine.execute(blockedPrompt, task.conversation_id || state.conversationId || '', orchestratorBot.id, {
2010
- apiKey: this.resolveApiKey(orchestratorBot.provider),
2011
- disableDecomposition: true,
2012
- disableTools: true,
2013
- workerMode: false,
2014
- persistConversationMessages: false,
2015
- onWorkerChunk: opts.onWorkerChunk,
2016
- workspacePath: project?.folder?.trim() || undefined,
2017
- });
2018
- return this.stripWorkerProtocol(result.mergedResult || '');
2019
- });
2178
+ let rawResponse = '';
2179
+ try {
2180
+ rawResponse = await this.runNodeWithRetry('finalize_response', state, async () => this.respondAsClerk(blockedPrompt, 'You are Clerk. Write the user-facing blocker message after work has already started. Be concise, outcome-first, and do not restate the full original request.'));
2181
+ }
2182
+ catch (error) {
2183
+ this.recordOrchestrationAudit(state, 'finalize_response', 'blocked', `Blocked final response generation failed: ${error?.message || error}`);
2184
+ rawResponse = '';
2185
+ }
2186
+ const response = (!rawResponse.trim() || /no steps completed\.?/i.test(rawResponse))
2187
+ ? ''
2188
+ : rawResponse;
2020
2189
  state.finalResponseDraft = response;
2021
2190
  return response;
2022
2191
  }
2023
2192
  async finalizeQueuedTodoChain(conversationId, opts, state, orchestratorBot, project, workflowName) {
2024
- const completedTasks = data.listCompletedTodoTasksForConversation(conversationId);
2193
+ const completedTasks = data.listCompletedTodoTasksForConversation(conversationId).map((task) => ({
2194
+ ...task,
2195
+ artifactRefs: data.listTodoArtifactsForTask(task.id, 'completed').map((item) => item.path_or_ref),
2196
+ }));
2025
2197
  if (completedTasks.length === 0) {
2026
- const fallback = workflowName
2027
- ? `The "${workflowName}" workflow finished without any completed worker output.`
2028
- : 'The queued workflow finished without any completed worker output.';
2029
- state.finalResponseDraft = fallback;
2030
- return fallback;
2198
+ state.finalResponseDraft = '';
2199
+ return '';
2031
2200
  }
2032
2201
  const policy = data.getEffectiveOrchestrationPolicy(project?.id || opts.projectId || state.projectId || undefined);
2033
2202
  const finalPrompt = (0, orchestrator_final_response_prompt_1.buildOrchestratorFinalResponsePrompt)({
@@ -2037,18 +2206,17 @@ class OrchestratorAgent {
2037
2206
  effectivePolicy: policy,
2038
2207
  completedTasks,
2039
2208
  });
2040
- const response = await this.runNodeWithRetry('finalize_response', state, async () => {
2041
- const result = await this.workflowEngine.execute(finalPrompt, conversationId, orchestratorBot.id, {
2042
- apiKey: this.resolveApiKey(orchestratorBot.provider),
2043
- disableDecomposition: true,
2044
- disableTools: true,
2045
- workerMode: false,
2046
- persistConversationMessages: false,
2047
- onWorkerChunk: opts.onWorkerChunk,
2048
- workspacePath: project?.folder?.trim() || undefined,
2049
- });
2050
- return this.stripWorkerProtocol(result.mergedResult || '');
2051
- });
2209
+ let response = '';
2210
+ try {
2211
+ response = await this.runNodeWithRetry('finalize_response', state, async () => this.respondAsClerk(finalPrompt, 'You are Clerk. Write the final user-facing answer after the workflow is complete. Do not restate the full request. Say what was created, where it is, the concise result, and only any important caveat. Do not claim everything was verified, defect-free, or ready for use unless a completed verification step explicitly established that.'));
2212
+ }
2213
+ catch (error) {
2214
+ this.recordOrchestrationAudit(state, 'finalize_response', 'blocked', `Final response generation failed: ${error?.message || error}`);
2215
+ response = '';
2216
+ }
2217
+ if (!response.trim() || /no steps completed\.?/i.test(response)) {
2218
+ response = '';
2219
+ }
2052
2220
  state.finalResponseDraft = response;
2053
2221
  this.recordOrchestrationAudit(state, 'finalize_response', 'completed', workflowName
2054
2222
  ? `Completed automatic TODO dispatch for workflow "${workflowName}".`
@@ -2066,6 +2234,7 @@ class OrchestratorAgent {
2066
2234
  originalPrompt: prompt,
2067
2235
  stepInstruction: 'Handle this delegated request directly.',
2068
2236
  owner,
2237
+ previousWorker: null,
2069
2238
  nextWorker: null,
2070
2239
  projectName,
2071
2240
  workspacePath,
@@ -2098,6 +2267,7 @@ class OrchestratorAgent {
2098
2267
  originalPrompt: prompt,
2099
2268
  stepInstruction: step.instruction,
2100
2269
  owner: step.owner,
2270
+ previousWorker: steps[index - 1]?.owner || null,
2101
2271
  nextWorker: steps[index + 1]?.owner || null,
2102
2272
  projectName,
2103
2273
  workspacePath,
@@ -2126,6 +2296,7 @@ class OrchestratorAgent {
2126
2296
  originalPrompt: prompt,
2127
2297
  stepInstruction: this.defaultStepInstructionForRole(item.role, prompt),
2128
2298
  owner: item.agent,
2299
+ previousWorker: owners[index - 1]?.agent || null,
2129
2300
  nextWorker: owners[index + 1]?.agent || null,
2130
2301
  projectName: projectName || null,
2131
2302
  workspacePath: workspacePath || null,
@@ -2178,16 +2349,34 @@ class OrchestratorAgent {
2178
2349
  }
2179
2350
  buildWorkerTaskPrompt(input) {
2180
2351
  const workerRole = String(input.owner.role_label || input.owner.role_class || 'general').trim() || 'general';
2352
+ const taskType = this.normalizeTaskTypeForWorker(input.owner, null);
2181
2353
  const composedTask = [
2182
2354
  `Step instruction: ${input.stepInstruction}`,
2183
- '',
2184
- 'Original user request:',
2185
- input.originalPrompt,
2355
+ taskType === 'qa'
2356
+ ? 'QA scope: Use PREVIOUS WORKER HANDOFF, queued task details, and explicit success criteria as the source of truth for this QA pass. Only search for broader context if those inputs are incomplete.'
2357
+ : taskType === 'verify'
2358
+ ? 'Verification scope: Compare the delivered work against the original user request and the explicit success criteria.'
2359
+ : '',
2360
+ String(input.nextWorker?.role_label || input.nextWorker?.role_class || '').trim().toLowerCase() === 'qa'
2361
+ ? 'QA handoff rule: Scope the handoff to static artifact/code review, concrete implementation claims, and explicit pass/fail checks. Do not ask the next QA worker to open a browser, capture screenshots, test desktop/mobile breakpoints, check animations or hover effects, or re-compare against the full original request unless the user explicitly asked for runtime verification.'
2362
+ : '',
2363
+ String(input.nextWorker?.role_label || input.nextWorker?.role_class || '').trim().toLowerCase() === 'qa'
2364
+ ? 'QA handoff rule: Do not ask the next QA worker to use memory search, image analysis, or broad codebase discovery unless a specific missing expectation cannot be checked from the artifact and nearby implementation.'
2365
+ : '',
2366
+ taskType === 'qa'
2367
+ ? null
2368
+ : '',
2369
+ taskType === 'qa'
2370
+ ? null
2371
+ : 'Original user request (reference only):',
2372
+ taskType === 'qa'
2373
+ ? null
2374
+ : input.originalPrompt,
2186
2375
  ].join('\n');
2187
2376
  return (0, worker_operating_prompt_1.buildWorkerOperatingPrompt)({
2188
2377
  workerName: input.owner.name,
2189
2378
  workerRole,
2190
- taskType: this.normalizeTaskTypeForWorker(input.owner, null),
2379
+ taskType,
2191
2380
  taskTitle: input.stepInstruction,
2192
2381
  taskPrompt: composedTask,
2193
2382
  projectName: input.projectName,
@@ -2196,6 +2385,8 @@ class OrchestratorAgent {
2196
2385
  heading: '',
2197
2386
  defaultLine: 'No confirmed special policy is set.',
2198
2387
  }),
2388
+ previousWorkerName: input.previousWorker?.name || null,
2389
+ previousWorkerRole: input.previousWorker ? String(input.previousWorker.role_label || input.previousWorker.role_class || 'general') : null,
2199
2390
  nextWorkerName: input.nextWorker?.name || null,
2200
2391
  nextWorkerRole: input.nextWorker ? String(input.nextWorker.role_label || input.nextWorker.role_class || 'general') : null,
2201
2392
  });
@@ -2204,7 +2395,7 @@ class OrchestratorAgent {
2204
2395
  if (role === 'research')
2205
2396
  return `Research and plan: ${this.summarizeTodoTitle(prompt)}`;
2206
2397
  if (role === 'qa')
2207
- return `QA and verify: ${this.summarizeTodoTitle(prompt)}`;
2398
+ return `QA and review: ${this.summarizeTodoTitle(prompt)}`;
2208
2399
  return `Build and implement: ${this.summarizeTodoTitle(prompt)}`;
2209
2400
  }
2210
2401
  defaultStepInstructionForRole(role, prompt) {
@@ -2212,7 +2403,7 @@ class OrchestratorAgent {
2212
2403
  return 'Research, brainstorm, or plan the work so the implementation handoff is specific and actionable.';
2213
2404
  }
2214
2405
  if (role === 'qa') {
2215
- return 'Review and QA the completed work against the original request. If issues remain, prepare a fix handoff for the previous worker.';
2406
+ return 'QA the completed work against the previous worker handoff, delivered artifact, and explicit success criteria. Focus on artifact/code review only. Do not do browser checks, image analysis, memory search, or broad codebase discovery unless runtime verification was explicitly requested or the handoff is missing a specific fact you cannot otherwise inspect. If issues remain, prepare a precise fix handoff for the previous worker.';
2216
2407
  }
2217
2408
  return `Implement the requested work directly. Keep the original request in scope: ${prompt}`;
2218
2409
  }
@@ -2220,7 +2411,7 @@ class OrchestratorAgent {
2220
2411
  if (role === 'research')
2221
2412
  return 'Produce a clear handoff that gives the next worker enough detail to execute without guessing.';
2222
2413
  if (role === 'qa')
2223
- return 'Verify the work against the request and prepare the next handoff if fixes are needed.';
2414
+ return 'Verify the delivered work against the handoff and explicit success criteria, and prepare a fix handoff if issues remain.';
2224
2415
  return 'Complete the requested implementation and prepare the next worker handoff.';
2225
2416
  }
2226
2417
  summarizeTodoTitle(text) {
@@ -2268,6 +2459,9 @@ class OrchestratorAgent {
2268
2459
  }
2269
2460
  },
2270
2461
  }));
2462
+ return this.finalizeOwnedExecutionResult(result, prompt, conversationId, orchestratorBot, roleAssignments, project, opts, state, options);
2463
+ }
2464
+ async finalizeOwnedExecutionResult(result, prompt, conversationId, orchestratorBot, roleAssignments, project, opts, state, options) {
2271
2465
  const response = this.stripWorkerProtocol(result.mergedResult);
2272
2466
  const footer = (0, status_parser_1.parseStructuredFooter)(result.mergedResult);
2273
2467
  const failureStep = result.steps.find((step) => step.status === 'failed');
@@ -2318,7 +2512,7 @@ class OrchestratorAgent {
2318
2512
  '',
2319
2513
  `Your task: Perform ${roleLabel} on the completed work above.`,
2320
2514
  role === 'qa'
2321
- ? 'Review the work against the original request. Report what passes and what fails. Be specific about any issues found.'
2515
+ ? 'QA the completed work against the delivered artifact and the explicit requirements that are already present. Use the original request only to fill in missing expectations. Report what passes and what fails, and be specific about any issues found.'
2322
2516
  : role === 'research'
2323
2517
  ? 'Analyze the work and provide insights, suggestions, or additional research as requested.'
2324
2518
  : 'Review and provide your feedback on the completed work.',
@@ -2393,7 +2587,7 @@ class OrchestratorAgent {
2393
2587
  return assignedName === orchestratorName;
2394
2588
  const roleClass = String(orchestratorBot.role_class || '').trim().toLowerCase();
2395
2589
  if (role === 'coding')
2396
- return ['code', 'coder', 'builder', 'developer', 'engineer', 'orchestrator', 'manager'].includes(roleClass);
2590
+ return ['code', 'coding', 'coder', 'builder', 'developer', 'engineer', 'orchestrator', 'manager'].includes(roleClass);
2397
2591
  if (role === 'qa')
2398
2592
  return ['qa', 'review', 'reviewer'].includes(roleClass);
2399
2593
  if (role === 'research')
@@ -2667,61 +2861,8 @@ class OrchestratorAgent {
2667
2861
  return response;
2668
2862
  }
2669
2863
  async maybeRunFinalCriticPass(input) {
2670
- if (input.skip)
2671
- return input.candidateResponse;
2672
- if (input.state?.riskLevel !== 'high')
2673
- return input.candidateResponse;
2674
- const qaName = input.roleAssignments.qa;
2675
- if (!qaName)
2676
- return input.candidateResponse;
2677
- const agent = (0, orchestrator_profile_1.filterOutOrchestratorProfiles)(data.listAgentProfiles())
2678
- .find((candidate) => candidate.name.toLowerCase() === qaName.toLowerCase());
2679
- if (!agent)
2680
- return input.candidateResponse;
2681
- input.state && (0, state_1.addHelperRoleUsage)(input.state, 'verifier');
2682
- input.state && (0, state_1.addDelegateTarget)(input.state, agent.name);
2683
- input.state && this.recordOrchestrationAudit(input.state, 'verify_result', 'delegated', `Running final high-risk review with ${agent.name}.`, { qaAgent: agent.name });
2684
- const reviewPrompt = [
2685
- 'You are performing a final high-risk review for an already-completed response.',
2686
- 'Check for safety, consistency with the user request, and whether the response appears complete enough to show as finished.',
2687
- '',
2688
- `Original user request:\n${input.prompt}`,
2689
- '',
2690
- `Candidate final response:\n${input.candidateResponse}`,
2691
- '',
2692
- 'Return this exact footer structure:',
2693
- 'RESULT: PASS or FAIL, followed by one short sentence.',
2694
- 'SUMMARY: one short sentence.',
2695
- 'STATUS: PASS or FAIL',
2696
- ].join('\n');
2697
- try {
2698
- const result = await this.runNodeWithRetry('verify_result', input.state, async () => this.workflowEngine.execute(reviewPrompt, input.conversationId, agent.id, {
2699
- disableDecomposition: true,
2700
- disableTools: true,
2701
- projectId: input.project?.id || input.state?.projectId || null,
2702
- workspacePath: input.project?.folder?.trim() || undefined,
2703
- }));
2704
- const cleaned = this.stripWorkerProtocol(result.mergedResult);
2705
- const footer = (0, status_parser_1.parseStructuredFooter)(result.mergedResult);
2706
- if (input.state) {
2707
- (0, state_1.appendArtifact)(input.state, 'qaFindings', cleaned || footer.summary || footer.result || 'High-risk review completed.');
2708
- }
2709
- const status = String(footer.status || '').trim().toUpperCase();
2710
- if (status === 'FAIL' || status === 'BLOCKED') {
2711
- input.state && this.recordOrchestrationAudit(input.state, 'verify_result', 'blocked', `${agent.name} flagged the final response for revision.`, { qaStatus: status, qaSummary: footer.summary || cleaned || null });
2712
- return [
2713
- 'I paused after final review found issues that still need attention.',
2714
- '',
2715
- `QA review (${agent.name}): ${cleaned || footer.summary || 'The final response was not approved.'}`,
2716
- ].join('\n');
2717
- }
2718
- input.state && this.recordOrchestrationAudit(input.state, 'verify_result', 'verified', `${agent.name} approved the final high-risk response.`, { qaStatus: status || 'PASS' });
2719
- return input.candidateResponse;
2720
- }
2721
- catch (error) {
2722
- input.state && this.recordOrchestrationAudit(input.state, 'verify_result', 'blocked', `Final review failed to run cleanly: ${error?.message || error}`);
2723
- return input.candidateResponse;
2724
- }
2864
+ void input;
2865
+ return input.candidateResponse;
2725
2866
  }
2726
2867
  isHighRiskPrompt(prompt) {
2727
2868
  return /\b(delete|remove|destroy|drop|wipe|erase|purge|archive|unarchive|deploy|ship|publish|release|migrate|migration|schema change|database change|permission|permissions|access|oauth|token|secret|credential|auth|security|api key|provider|settings?)\b/i.test(prompt);
@@ -3215,6 +3356,14 @@ class OrchestratorAgent {
3215
3356
  reportHiddenRoleProgress(opts, roleName, detail) {
3216
3357
  opts.onProgress(`${roleName}::${detail}`);
3217
3358
  }
3359
+ formatConfirmationRequest(prompt) {
3360
+ const singleLine = String(prompt || '').replace(/\s+/g, ' ').trim();
3361
+ if (!singleLine)
3362
+ return 'Requested action';
3363
+ if (singleLine.length <= 180)
3364
+ return singleLine;
3365
+ return `${singleLine.slice(0, 177)}...`;
3366
+ }
3218
3367
  describeIntentForActivity(intent) {
3219
3368
  switch (intent.primaryMode) {
3220
3369
  case 'PROXY_MODE':
@@ -3325,6 +3474,21 @@ class OrchestratorAgent {
3325
3474
  }
3326
3475
  return assignments;
3327
3476
  }
3477
+ clerkModelLabel() {
3478
+ const runtime = this.clerk.getRuntimeInfo();
3479
+ if (!runtime.model)
3480
+ return 'Clerk';
3481
+ return `${runtime.model} | Clerk`;
3482
+ }
3483
+ async respondAsClerk(userPrompt, systemPrompt) {
3484
+ const response = await this.clerk.respond(userPrompt, systemPrompt);
3485
+ this.setLastResponseMeta({
3486
+ agentName: 'Clerk',
3487
+ botId: null,
3488
+ modelLabel: this.clerkModelLabel(),
3489
+ });
3490
+ return response;
3491
+ }
3328
3492
  hasRoleAssignments(assignments) {
3329
3493
  return Boolean(assignments.coding || assignments.qa || assignments.research);
3330
3494
  }
@@ -3356,7 +3520,7 @@ class OrchestratorAgent {
3356
3520
  const result = {};
3357
3521
  const roleClass = String(orchestratorBot?.role_class || '').trim().toLowerCase();
3358
3522
  if (orchestratorBot?.name) {
3359
- if (['code', 'coder', 'builder', 'developer', 'engineer'].includes(roleClass)) {
3523
+ if (['code', 'coding', 'coder', 'builder', 'developer', 'engineer'].includes(roleClass)) {
3360
3524
  result.coding = orchestratorBot.name;
3361
3525
  }
3362
3526
  else if (['qa', 'review', 'reviewer'].includes(roleClass)) {
@@ -3367,7 +3531,7 @@ class OrchestratorAgent {
3367
3531
  }
3368
3532
  }
3369
3533
  const findByRole = (roles) => bots.find((bot) => roles.includes(String(bot.role_class || '').trim().toLowerCase()));
3370
- result.coding = result.coding || findByRole(['code', 'coder', 'builder', 'developer', 'engineer'])?.name;
3534
+ result.coding = result.coding || findByRole(['code', 'coding', 'coder', 'builder', 'developer', 'engineer'])?.name;
3371
3535
  result.qa = result.qa || findByRole(['qa', 'review', 'reviewer'])?.name;
3372
3536
  result.research = result.research || findByRole(['research', 'researcher', 'analyst', 'advisor'])?.name;
3373
3537
  return result;
@@ -3437,17 +3601,10 @@ class OrchestratorAgent {
3437
3601
  },
3438
3602
  });
3439
3603
  return [
3440
- 'I want to pause before I do that because it looks like a higher-risk action.',
3441
- '',
3442
- `Why I paused: ${executionSpec.riskReasons.join(', ') || 'sensitive change'}.`,
3443
- ...(deterministicPlan.enabled
3444
- ? [
3445
- '',
3446
- 'Deterministic guardrail path:',
3447
- ...deterministicPlan.steps.map((step, index) => `${index + 1}. ${step}`),
3448
- ]
3449
- : []),
3450
- 'Reply with "yes, continue" to proceed or "no, cancel" to stop here.',
3604
+ 'Are you sure you want to continue with this request?',
3605
+ `Request: ${this.formatConfirmationRequest(prompt)}`,
3606
+ `Reason: ${executionSpec.riskReasons.join(', ') || 'sensitive change'}.`,
3607
+ 'Reply with "yes, continue" or "no, cancel".',
3451
3608
  ].join('\n');
3452
3609
  }
3453
3610
  async handlePendingCheckpoint(prompt, checkpoint, opts, fallbackState) {
@@ -3457,11 +3614,14 @@ class OrchestratorAgent {
3457
3614
  return 'Understood. I cancelled that pending action.';
3458
3615
  }
3459
3616
  if (!this.isCheckpointConfirmation(prompt)) {
3617
+ const pausedPrompt = typeof checkpoint.payload?.prompt === 'string'
3618
+ ? checkpoint.payload.prompt
3619
+ : fallbackState.userPromptRaw || '';
3460
3620
  return [
3461
- 'I still have a paused high-risk action waiting for confirmation.',
3462
- '',
3621
+ 'I still have a paused action waiting for confirmation.',
3622
+ `Request: ${this.formatConfirmationRequest(pausedPrompt)}`,
3463
3623
  `Reason: ${checkpoint.reason}.`,
3464
- 'Reply with "yes, continue" to resume it or "no, cancel" to discard it.',
3624
+ 'Reply with "yes, continue" or "no, cancel".',
3465
3625
  ].join('\n');
3466
3626
  }
3467
3627
  const payload = checkpoint.payload || {};
@@ -3710,7 +3870,7 @@ class OrchestratorAgent {
3710
3870
  merged.qa ? `${merged.qa} handles QA` : null,
3711
3871
  merged.research ? `${merged.research} handles research` : null,
3712
3872
  ].filter(Boolean);
3713
- if (parts.length > 0) {
3873
+ if (parts.length > 0 && (0, storage_mode_1.isServerStorageMode)()) {
3714
3874
  data.upsertMemoryFact({
3715
3875
  agentId,
3716
3876
  conversationId,
@@ -3922,13 +4082,15 @@ class OrchestratorAgent {
3922
4082
  if (!workflow)
3923
4083
  return;
3924
4084
  data.setProjectSetting(projectId, ORCHESTRATOR_WORKFLOW_SETTING_KEY, JSON.stringify(workflow));
3925
- data.upsertMemoryFact({
3926
- agentId,
3927
- conversationId,
3928
- factType: 'operating_instruction',
3929
- content: `Preferred workflow for this project: ${workflow.steps.join('; ')}.`,
3930
- extractionMethod: 'orchestrator',
3931
- });
4085
+ if ((0, storage_mode_1.isServerStorageMode)()) {
4086
+ data.upsertMemoryFact({
4087
+ agentId,
4088
+ conversationId,
4089
+ factType: 'operating_instruction',
4090
+ content: `Preferred workflow for this project: ${workflow.steps.join('; ')}.`,
4091
+ extractionMethod: 'orchestrator',
4092
+ });
4093
+ }
3932
4094
  data.logAdminAudit({
3933
4095
  actorType: 'orchestrator',
3934
4096
  actorId: 'Project Manager',