funolio-agent 1.0.7 → 1.0.47

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 (155) hide show
  1. package/dist/approval.d.ts +1 -0
  2. package/dist/approval.d.ts.map +1 -1
  3. package/dist/approval.js +12 -0
  4. package/dist/approval.js.map +1 -1
  5. package/dist/auth/auto-detect.d.ts +11 -3
  6. package/dist/auth/auto-detect.d.ts.map +1 -1
  7. package/dist/auth/auto-detect.js +136 -168
  8. package/dist/auth/auto-detect.js.map +1 -1
  9. package/dist/auth/subscription-runtime.js +1 -1
  10. package/dist/auth/subscription-runtime.js.map +1 -1
  11. package/dist/backfill.d.ts.map +1 -1
  12. package/dist/backfill.js +31 -28
  13. package/dist/backfill.js.map +1 -1
  14. package/dist/bot-manager.d.ts +16 -4
  15. package/dist/bot-manager.d.ts.map +1 -1
  16. package/dist/bot-manager.js +280 -73
  17. package/dist/bot-manager.js.map +1 -1
  18. package/dist/clerk-model.d.ts +10 -6
  19. package/dist/clerk-model.d.ts.map +1 -1
  20. package/dist/clerk-model.js +39 -16
  21. package/dist/clerk-model.js.map +1 -1
  22. package/dist/cli.js +7 -2
  23. package/dist/cli.js.map +1 -1
  24. package/dist/commands/import-history.js +5 -1
  25. package/dist/commands/import-history.js.map +1 -1
  26. package/dist/commands/start.d.ts.map +1 -1
  27. package/dist/commands/start.js +57 -225
  28. package/dist/commands/start.js.map +1 -1
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +13 -2
  31. package/dist/config.js.map +1 -1
  32. package/dist/context-window.js +1 -1
  33. package/dist/context-window.js.map +1 -1
  34. package/dist/import-parser-core.d.ts.map +1 -1
  35. package/dist/import-parser-core.js +74 -8
  36. package/dist/import-parser-core.js.map +1 -1
  37. package/dist/local-data.d.ts +59 -6
  38. package/dist/local-data.d.ts.map +1 -1
  39. package/dist/local-data.js +544 -62
  40. package/dist/local-data.js.map +1 -1
  41. package/dist/local-db.d.ts.map +1 -1
  42. package/dist/local-db.js +21 -1
  43. package/dist/local-db.js.map +1 -1
  44. package/dist/local-funnel.d.ts +0 -7
  45. package/dist/local-funnel.d.ts.map +1 -1
  46. package/dist/local-funnel.js +19 -28
  47. package/dist/local-funnel.js.map +1 -1
  48. package/dist/local-import-worker.d.ts.map +1 -1
  49. package/dist/local-import-worker.js +49 -4
  50. package/dist/local-import-worker.js.map +1 -1
  51. package/dist/local-memory-search.d.ts.map +1 -1
  52. package/dist/local-memory-search.js +6 -3
  53. package/dist/local-memory-search.js.map +1 -1
  54. package/dist/local-server.d.ts +37 -0
  55. package/dist/local-server.d.ts.map +1 -1
  56. package/dist/local-server.js +731 -227
  57. package/dist/local-server.js.map +1 -1
  58. package/dist/mcp/local-memory-server.d.ts +1 -1
  59. package/dist/mcp/local-memory-server.d.ts.map +1 -1
  60. package/dist/mcp/local-memory-server.js +23 -11
  61. package/dist/mcp/local-memory-server.js.map +1 -1
  62. package/dist/mcp/manager.d.ts.map +1 -1
  63. package/dist/mcp/manager.js +86 -18
  64. package/dist/mcp/manager.js.map +1 -1
  65. package/dist/message-loop.d.ts +5 -10
  66. package/dist/message-loop.d.ts.map +1 -1
  67. package/dist/message-loop.js +393 -289
  68. package/dist/message-loop.js.map +1 -1
  69. package/dist/mqtt-client.d.ts +4 -1
  70. package/dist/mqtt-client.d.ts.map +1 -1
  71. package/dist/mqtt-client.js.map +1 -1
  72. package/dist/orchestration/orchestrator-blocked-prompt.d.ts +2 -1
  73. package/dist/orchestration/orchestrator-blocked-prompt.d.ts.map +1 -1
  74. package/dist/orchestration/orchestrator-blocked-prompt.js +11 -0
  75. package/dist/orchestration/orchestrator-blocked-prompt.js.map +1 -1
  76. package/dist/orchestration/orchestrator-final-response-prompt.d.ts +4 -1
  77. package/dist/orchestration/orchestrator-final-response-prompt.d.ts.map +1 -1
  78. package/dist/orchestration/orchestrator-final-response-prompt.js +8 -6
  79. package/dist/orchestration/orchestrator-final-response-prompt.js.map +1 -1
  80. package/dist/orchestration/worker-operating-prompt.d.ts +2 -0
  81. package/dist/orchestration/worker-operating-prompt.d.ts.map +1 -1
  82. package/dist/orchestration/worker-operating-prompt.js +39 -0
  83. package/dist/orchestration/worker-operating-prompt.js.map +1 -1
  84. package/dist/orchestrator.d.ts +13 -0
  85. package/dist/orchestrator.d.ts.map +1 -1
  86. package/dist/orchestrator.js +235 -133
  87. package/dist/orchestrator.js.map +1 -1
  88. package/dist/providers/anthropic.d.ts +0 -5
  89. package/dist/providers/anthropic.d.ts.map +1 -1
  90. package/dist/providers/anthropic.js +29 -75
  91. package/dist/providers/anthropic.js.map +1 -1
  92. package/dist/providers/claude-cli.d.ts.map +1 -1
  93. package/dist/providers/claude-cli.js +16 -140
  94. package/dist/providers/claude-cli.js.map +1 -1
  95. package/dist/providers/codex-cli.d.ts.map +1 -1
  96. package/dist/providers/codex-cli.js +78 -59
  97. package/dist/providers/codex-cli.js.map +1 -1
  98. package/dist/providers/google.d.ts.map +1 -1
  99. package/dist/providers/google.js +4 -2
  100. package/dist/providers/google.js.map +1 -1
  101. package/dist/providers/index.d.ts +2 -0
  102. package/dist/providers/index.d.ts.map +1 -1
  103. package/dist/providers/index.js.map +1 -1
  104. package/dist/providers/openai.d.ts.map +1 -1
  105. package/dist/providers/openai.js +27 -2
  106. package/dist/providers/openai.js.map +1 -1
  107. package/dist/storage-mode.d.ts +5 -0
  108. package/dist/storage-mode.d.ts.map +1 -0
  109. package/dist/storage-mode.js +21 -0
  110. package/dist/storage-mode.js.map +1 -0
  111. package/dist/summarization-pipeline.d.ts +9 -0
  112. package/dist/summarization-pipeline.d.ts.map +1 -1
  113. package/dist/summarization-pipeline.js +61 -17
  114. package/dist/summarization-pipeline.js.map +1 -1
  115. package/dist/tools/analyze-image.js +2 -2
  116. package/dist/tools/analyze-image.js.map +1 -1
  117. package/dist/tools/edit-file.js +3 -3
  118. package/dist/tools/edit-file.js.map +1 -1
  119. package/dist/tools/index.d.ts.map +1 -1
  120. package/dist/tools/index.js +36 -0
  121. package/dist/tools/index.js.map +1 -1
  122. package/dist/tools/list-directory.js +7 -4
  123. package/dist/tools/list-directory.js.map +1 -1
  124. package/dist/tools/read-file.js +3 -3
  125. package/dist/tools/read-file.js.map +1 -1
  126. package/dist/tools/run-command.js +3 -3
  127. package/dist/tools/run-command.js.map +1 -1
  128. package/dist/tools/sandbox.d.ts +10 -5
  129. package/dist/tools/sandbox.d.ts.map +1 -1
  130. package/dist/tools/sandbox.js +41 -13
  131. package/dist/tools/sandbox.js.map +1 -1
  132. package/dist/tools/search-codebase.js +2 -2
  133. package/dist/tools/search-codebase.js.map +1 -1
  134. package/dist/tools/search-local-memory.d.ts.map +1 -1
  135. package/dist/tools/search-local-memory.js +19 -8
  136. package/dist/tools/search-local-memory.js.map +1 -1
  137. package/dist/tools/spawn-subagent.js.map +1 -1
  138. package/dist/tools/write-file.js +3 -3
  139. package/dist/tools/write-file.js.map +1 -1
  140. package/dist/types.d.ts +2 -0
  141. package/dist/types.d.ts.map +1 -1
  142. package/dist/verification/index.js +2 -2
  143. package/dist/verification/index.js.map +1 -1
  144. package/dist/wizard-state.d.ts.map +1 -1
  145. package/dist/wizard-state.js +16 -2
  146. package/dist/wizard-state.js.map +1 -1
  147. package/dist/wizard-support.d.ts +2 -2
  148. package/dist/wizard-support.d.ts.map +1 -1
  149. package/dist/wizard-support.js +80 -93
  150. package/dist/wizard-support.js.map +1 -1
  151. package/dist/workflow-engine.d.ts +3 -1
  152. package/dist/workflow-engine.d.ts.map +1 -1
  153. package/dist/workflow-engine.js +131 -12
  154. package/dist/workflow-engine.js.map +1 -1
  155. package/package.json +1 -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,17 @@ 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
+ getLastResponseMeta() {
93
+ return this.lastResponseMeta;
94
+ }
95
+ setLastResponseMeta(meta) {
96
+ this.lastResponseMeta = meta;
97
+ }
90
98
  async runNodeWithRetry(node, state, work) {
91
99
  const maxRetries = ORCHESTRATION_NODE_RETRY_LIMITS[node] ?? 0;
92
100
  let attempt = 0;
@@ -109,6 +117,7 @@ class OrchestratorAgent {
109
117
  * Returns O's response to display to the user.
110
118
  */
111
119
  async handleUserMessage(prompt, conversationId, opts) {
120
+ this.lastResponseMeta = null;
112
121
  const conversation = conversationId ? data.getConversation(conversationId) : undefined;
113
122
  const routingMode = conversation?.routing_mode;
114
123
  const conversationProjectId = conversation?.project_id || opts.projectId;
@@ -137,6 +146,13 @@ class OrchestratorAgent {
137
146
  }
138
147
  const promptAssignments = this.parseRoleAssignments(prompt);
139
148
  const selectedOrchestrator = this.resolveSelectedOrchestratorBot(conversation);
149
+ if (selectedOrchestrator) {
150
+ this.setLastResponseMeta({
151
+ agentName: 'Orchestrator',
152
+ botId: selectedOrchestrator.id,
153
+ modelLabel: null,
154
+ });
155
+ }
140
156
  if (selectedOrchestrator) {
141
157
  orchestrationState.orchestratorBotId = selectedOrchestrator.id;
142
158
  }
@@ -197,26 +213,40 @@ class OrchestratorAgent {
197
213
  if (frontDoorResult !== null)
198
214
  return frontDoorResult;
199
215
  }
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) {
216
+ let intent;
217
+ let routedPlan;
218
+ if (selectedOrchestrator) {
219
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', 'Recovering routing from code fallback');
220
+ intent = this.fallbackClassifyUserMessage(prompt, routingMode);
221
+ orchestrationState.fallbackUsed = true;
207
222
  if (this.applyClassificationGuards(prompt, intent, routingMode)) {
208
223
  (0, state_1.markMisrouteCorrected)(orchestrationState);
209
224
  }
210
- this.reportHiddenRoleProgress(opts, 'intent_classifier', `Routed single intent as ${this.describeIntentForActivity(intent)}`);
225
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', `Classified request as ${this.describeIntentForActivity(intent)}`);
211
226
  }
212
227
  else {
213
228
  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);
229
+ (0, state_1.addHelperRoleUsage)(orchestrationState, 'intent_classifier');
230
+ routedPlan = await this.decomposeAndRouteUserMessage(prompt, conversationId, opts.projectId, routingMode);
231
+ const routedIntent = routedPlan?.intents.length === 1
232
+ ? this.buildIntentAnalysisFromRoutedIntent(routedPlan.intents[0])
233
+ : undefined;
234
+ if (routedIntent) {
235
+ intent = routedIntent;
236
+ if (this.applyClassificationGuards(prompt, intent, routingMode)) {
237
+ (0, state_1.markMisrouteCorrected)(orchestrationState);
238
+ }
239
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', `Routed single intent as ${this.describeIntentForActivity(intent)}`);
240
+ }
241
+ else {
242
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', 'Classifying request');
243
+ intent = await this.classifyUserMessage(prompt, opts.projectId, routingMode);
244
+ orchestrationState.fallbackUsed = true;
245
+ if (this.applyClassificationGuards(prompt, intent, routingMode)) {
246
+ (0, state_1.markMisrouteCorrected)(orchestrationState);
247
+ }
248
+ this.reportHiddenRoleProgress(opts, 'intent_classifier', `Classified request as ${this.describeIntentForActivity(intent)}`);
218
249
  }
219
- this.reportHiddenRoleProgress(opts, 'intent_classifier', `Classified request as ${this.describeIntentForActivity(intent)}`);
220
250
  }
221
251
  orchestrationState.intent = intent.intent;
222
252
  orchestrationState.taskType = intent.primaryMode;
@@ -400,6 +430,7 @@ class OrchestratorAgent {
400
430
  const orchestratorOwnsCoding = orchestratorName.length > 0
401
431
  && (orchestratorName === codingOwner
402
432
  || roleClass === 'code'
433
+ || roleClass === 'coding'
403
434
  || roleClass === 'coder'
404
435
  || roleClass === 'builder'
405
436
  || roleClass === 'developer');
@@ -1276,9 +1307,12 @@ class OrchestratorAgent {
1276
1307
  return finalize(this.formatProjectKnowledgeResponse(overview, []), 'Handled project knowledge request directly.');
1277
1308
  }
1278
1309
  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.';
1310
+ return "I'm here and ready. Tell me what you want me to coordinate.";
1280
1311
  }
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.';
1312
+ 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)) {
1313
+ return "Thanks. I'm here when you need me.";
1314
+ }
1315
+ return "I'm here. Tell me what you want to do next.";
1282
1316
  }
1283
1317
  formatPolicyUpdateResponse(prompt, intent, assignments, overview) {
1284
1318
  const lines = ['I’m reading that as an orchestration policy update, not as immediate work.'];
@@ -1487,6 +1521,16 @@ class OrchestratorAgent {
1487
1521
  roleAssignments,
1488
1522
  });
1489
1523
  const normalizeName = (value) => String(value || '').trim().toLowerCase();
1524
+ const fastDecision = this.buildFastFrontDoorDecision(prompt, frontDoorSignals, promptAssignments, roleAssignments);
1525
+ if (fastDecision) {
1526
+ this.recordOrchestrationAudit(state, 'understand_request', 'path_selected', fastDecision.reason, {
1527
+ decisionMode: fastDecision.mode,
1528
+ delegateTarget: fastDecision.delegate_target || null,
1529
+ delegateRole: fastDecision.delegate_role || null,
1530
+ fastPath: true,
1531
+ });
1532
+ return this.handleFrontDoorDecision(fastDecision, prompt, conversationId, opts, orchestratorBot, projectId, project, policy, promptAssignments, roleAssignments, overview, state, signalSpecialists, workflowNames);
1533
+ }
1490
1534
  const operatingPrompt = (0, orchestrator_operating_prompt_1.buildOrchestratorOperatingPrompt)({
1491
1535
  orchestratorName: orchestratorBot.name,
1492
1536
  primaryRole: orchestratorBot.role_label || orchestratorBot.role_class || null,
@@ -1528,6 +1572,7 @@ class OrchestratorAgent {
1528
1572
  const result = await this.runNodeWithRetry('understand_request', state, async () => this.workflowEngine.execute(decisionPrompt, null, orchestratorBot.id, {
1529
1573
  disableDecomposition: true,
1530
1574
  disableTools: true,
1575
+ disallowCliRuntime: true,
1531
1576
  projectId: projectId || null,
1532
1577
  workspacePath: project?.folder?.trim() || undefined,
1533
1578
  systemPromptOverride: operatingPrompt,
@@ -1600,6 +1645,71 @@ class OrchestratorAgent {
1600
1645
  };
1601
1646
  }
1602
1647
  }
1648
+ return this.handleFrontDoorDecision(decision, prompt, conversationId, opts, orchestratorBot, projectId, project, policy, promptAssignments, roleAssignments, overview, state, signalSpecialists, workflowNames);
1649
+ }
1650
+ buildFastFrontDoorDecision(prompt, frontDoorSignals, promptAssignments, roleAssignments) {
1651
+ const normalizeName = (value) => String(value || '').trim().toLowerCase();
1652
+ const taskType = (0, front_door_policy_1.inferFrontDoorTaskType)(prompt);
1653
+ const normalizedPrompt = prompt.trim().toLowerCase();
1654
+ if (taskType === 'conversation') {
1655
+ if (/\b(hello|hi|hey|are you there|good morning|good afternoon|good evening)\b/i.test(normalizedPrompt)) {
1656
+ return {
1657
+ mode: 'respond',
1658
+ reason: 'Code-first front door handled a simple greeting conversationally.',
1659
+ response: "I'm here and ready. Tell me what you want me to coordinate.",
1660
+ };
1661
+ }
1662
+ 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(normalizedPrompt)) {
1663
+ return {
1664
+ mode: 'respond',
1665
+ reason: 'Code-first front door handled a simple conversational compliment without invoking workflow routing.',
1666
+ response: "Thanks. I'm here when you need me.",
1667
+ };
1668
+ }
1669
+ }
1670
+ const promptAssignedRoles = [promptAssignments.coding, promptAssignments.qa, promptAssignments.research].filter(Boolean).length;
1671
+ const multiWorkerRequested = taskType === 'mixed'
1672
+ || promptAssignedRoles >= 2
1673
+ || frontDoorSignals.matchedBots.length >= 2
1674
+ || frontDoorSignals.matchedRoles.length >= 2
1675
+ || frontDoorSignals.matchedWorkflowNames.length > 0
1676
+ || frontDoorSignals.preferredMode === 'workflow';
1677
+ if (multiWorkerRequested) {
1678
+ return {
1679
+ mode: 'workflow',
1680
+ reason: frontDoorSignals.matchedWorkflowNames.length > 0
1681
+ ? 'Code-first front door fast-pathed a named or selected workflow request without waiting on orchestrator routing.'
1682
+ : 'Code-first front door fast-pathed an obvious staged multi-worker request without waiting on orchestrator routing.',
1683
+ workflow_request: prompt,
1684
+ };
1685
+ }
1686
+ if (taskType === 'policy' || taskType === 'status' || taskType === 'conversation')
1687
+ return null;
1688
+ 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)) {
1689
+ return null;
1690
+ }
1691
+ const preferredTarget = frontDoorSignals.preferredTarget
1692
+ || (taskType === 'coding' ? roleAssignments.coding
1693
+ : taskType === 'qa' ? roleAssignments.qa
1694
+ : taskType === 'research' ? roleAssignments.research
1695
+ : null);
1696
+ if (!preferredTarget)
1697
+ return null;
1698
+ const delegateRole = frontDoorSignals.matchedRoles[0]
1699
+ || (normalizeName(preferredTarget) === normalizeName(roleAssignments.qa) ? 'qa'
1700
+ : normalizeName(preferredTarget) === normalizeName(roleAssignments.research) ? 'research'
1701
+ : 'coding');
1702
+ return {
1703
+ mode: 'delegate',
1704
+ reason: frontDoorSignals.explicitOrchestrationRequested
1705
+ ? 'Code-first front door fast-pathed an explicit single-worker request without waiting on orchestrator routing.'
1706
+ : 'Code-first front door fast-pathed a single-worker task to the assigned specialist.',
1707
+ delegate_target: preferredTarget,
1708
+ delegate_role: delegateRole,
1709
+ delegate_request: prompt,
1710
+ };
1711
+ }
1712
+ async handleFrontDoorDecision(decision, prompt, conversationId, opts, orchestratorBot, projectId, project, policy, promptAssignments, roleAssignments, overview, state, signalSpecialists, workflowNames) {
1603
1713
  if (!decision) {
1604
1714
  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
1715
  return null;
@@ -1949,6 +2059,7 @@ class OrchestratorAgent {
1949
2059
  const result = await this.runNodeWithRetry('run_workflow', state, async () => this.workflowEngine.execute(taskPrompt, conversationId, owner.id, {
1950
2060
  apiKey: this.resolveApiKey(owner.provider),
1951
2061
  taskId: task.id,
2062
+ stepDescription: task.title,
1952
2063
  disableDecomposition: true,
1953
2064
  isOrchestrated: true,
1954
2065
  projectId: project?.id || opts.projectId || state.projectId || null,
@@ -1989,6 +2100,7 @@ class OrchestratorAgent {
1989
2100
  async handleBlockedTodoTask(task, opts, state, orchestratorBot, project) {
1990
2101
  const policy = data.getEffectiveOrchestrationPolicy(project?.id || opts.projectId || state.projectId || undefined);
1991
2102
  const artifacts = data.listTodoArtifactsForTask(task.id, 'active').map((item) => item.path_or_ref);
2103
+ const completedTasks = data.listCompletedTodoTasksForConversation(task.conversation_id || state.conversationId || '');
1992
2104
  const workerRole = task.task_type || task.next_worker_role || 'general';
1993
2105
  const blockedPrompt = (0, orchestrator_blocked_prompt_1.buildBlockedWorkerOrchestratorPrompt)({
1994
2106
  orchestratorName: orchestratorBot.name,
@@ -2001,33 +2113,33 @@ class OrchestratorAgent {
2001
2113
  workspacePath: project?.folder || null,
2002
2114
  effectivePolicy: policy,
2003
2115
  artifactRefs: artifacts,
2116
+ completedTasks,
2004
2117
  blockerSummary: task.blocker_summary || 'Worker is blocked.',
2005
2118
  checkedContext: task.blocker_checked || 'No checked context was recorded.',
2006
2119
  userQuestion: task.blocker_question || 'What should happen next?',
2007
2120
  });
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
- });
2121
+ let rawResponse = '';
2122
+ try {
2123
+ 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.'));
2124
+ }
2125
+ catch (error) {
2126
+ this.recordOrchestrationAudit(state, 'finalize_response', 'blocked', `Blocked final response generation failed: ${error?.message || error}`);
2127
+ rawResponse = '';
2128
+ }
2129
+ const response = (!rawResponse.trim() || /no steps completed\.?/i.test(rawResponse))
2130
+ ? ''
2131
+ : rawResponse;
2020
2132
  state.finalResponseDraft = response;
2021
2133
  return response;
2022
2134
  }
2023
2135
  async finalizeQueuedTodoChain(conversationId, opts, state, orchestratorBot, project, workflowName) {
2024
- const completedTasks = data.listCompletedTodoTasksForConversation(conversationId);
2136
+ const completedTasks = data.listCompletedTodoTasksForConversation(conversationId).map((task) => ({
2137
+ ...task,
2138
+ artifactRefs: data.listTodoArtifactsForTask(task.id, 'completed').map((item) => item.path_or_ref),
2139
+ }));
2025
2140
  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;
2141
+ state.finalResponseDraft = '';
2142
+ return '';
2031
2143
  }
2032
2144
  const policy = data.getEffectiveOrchestrationPolicy(project?.id || opts.projectId || state.projectId || undefined);
2033
2145
  const finalPrompt = (0, orchestrator_final_response_prompt_1.buildOrchestratorFinalResponsePrompt)({
@@ -2037,18 +2149,17 @@ class OrchestratorAgent {
2037
2149
  effectivePolicy: policy,
2038
2150
  completedTasks,
2039
2151
  });
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
- });
2152
+ let response = '';
2153
+ try {
2154
+ 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.'));
2155
+ }
2156
+ catch (error) {
2157
+ this.recordOrchestrationAudit(state, 'finalize_response', 'blocked', `Final response generation failed: ${error?.message || error}`);
2158
+ response = '';
2159
+ }
2160
+ if (!response.trim() || /no steps completed\.?/i.test(response)) {
2161
+ response = '';
2162
+ }
2052
2163
  state.finalResponseDraft = response;
2053
2164
  this.recordOrchestrationAudit(state, 'finalize_response', 'completed', workflowName
2054
2165
  ? `Completed automatic TODO dispatch for workflow "${workflowName}".`
@@ -2066,6 +2177,7 @@ class OrchestratorAgent {
2066
2177
  originalPrompt: prompt,
2067
2178
  stepInstruction: 'Handle this delegated request directly.',
2068
2179
  owner,
2180
+ previousWorker: null,
2069
2181
  nextWorker: null,
2070
2182
  projectName,
2071
2183
  workspacePath,
@@ -2098,6 +2210,7 @@ class OrchestratorAgent {
2098
2210
  originalPrompt: prompt,
2099
2211
  stepInstruction: step.instruction,
2100
2212
  owner: step.owner,
2213
+ previousWorker: steps[index - 1]?.owner || null,
2101
2214
  nextWorker: steps[index + 1]?.owner || null,
2102
2215
  projectName,
2103
2216
  workspacePath,
@@ -2126,6 +2239,7 @@ class OrchestratorAgent {
2126
2239
  originalPrompt: prompt,
2127
2240
  stepInstruction: this.defaultStepInstructionForRole(item.role, prompt),
2128
2241
  owner: item.agent,
2242
+ previousWorker: owners[index - 1]?.agent || null,
2129
2243
  nextWorker: owners[index + 1]?.agent || null,
2130
2244
  projectName: projectName || null,
2131
2245
  workspacePath: workspacePath || null,
@@ -2178,16 +2292,34 @@ class OrchestratorAgent {
2178
2292
  }
2179
2293
  buildWorkerTaskPrompt(input) {
2180
2294
  const workerRole = String(input.owner.role_label || input.owner.role_class || 'general').trim() || 'general';
2295
+ const taskType = this.normalizeTaskTypeForWorker(input.owner, null);
2181
2296
  const composedTask = [
2182
2297
  `Step instruction: ${input.stepInstruction}`,
2183
- '',
2184
- 'Original user request:',
2185
- input.originalPrompt,
2298
+ taskType === 'qa'
2299
+ ? '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.'
2300
+ : taskType === 'verify'
2301
+ ? 'Verification scope: Compare the delivered work against the original user request and the explicit success criteria.'
2302
+ : '',
2303
+ String(input.nextWorker?.role_label || input.nextWorker?.role_class || '').trim().toLowerCase() === 'qa'
2304
+ ? '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.'
2305
+ : '',
2306
+ String(input.nextWorker?.role_label || input.nextWorker?.role_class || '').trim().toLowerCase() === 'qa'
2307
+ ? '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.'
2308
+ : '',
2309
+ taskType === 'qa'
2310
+ ? null
2311
+ : '',
2312
+ taskType === 'qa'
2313
+ ? null
2314
+ : 'Original user request (reference only):',
2315
+ taskType === 'qa'
2316
+ ? null
2317
+ : input.originalPrompt,
2186
2318
  ].join('\n');
2187
2319
  return (0, worker_operating_prompt_1.buildWorkerOperatingPrompt)({
2188
2320
  workerName: input.owner.name,
2189
2321
  workerRole,
2190
- taskType: this.normalizeTaskTypeForWorker(input.owner, null),
2322
+ taskType,
2191
2323
  taskTitle: input.stepInstruction,
2192
2324
  taskPrompt: composedTask,
2193
2325
  projectName: input.projectName,
@@ -2196,6 +2328,8 @@ class OrchestratorAgent {
2196
2328
  heading: '',
2197
2329
  defaultLine: 'No confirmed special policy is set.',
2198
2330
  }),
2331
+ previousWorkerName: input.previousWorker?.name || null,
2332
+ previousWorkerRole: input.previousWorker ? String(input.previousWorker.role_label || input.previousWorker.role_class || 'general') : null,
2199
2333
  nextWorkerName: input.nextWorker?.name || null,
2200
2334
  nextWorkerRole: input.nextWorker ? String(input.nextWorker.role_label || input.nextWorker.role_class || 'general') : null,
2201
2335
  });
@@ -2204,7 +2338,7 @@ class OrchestratorAgent {
2204
2338
  if (role === 'research')
2205
2339
  return `Research and plan: ${this.summarizeTodoTitle(prompt)}`;
2206
2340
  if (role === 'qa')
2207
- return `QA and verify: ${this.summarizeTodoTitle(prompt)}`;
2341
+ return `QA and review: ${this.summarizeTodoTitle(prompt)}`;
2208
2342
  return `Build and implement: ${this.summarizeTodoTitle(prompt)}`;
2209
2343
  }
2210
2344
  defaultStepInstructionForRole(role, prompt) {
@@ -2212,7 +2346,7 @@ class OrchestratorAgent {
2212
2346
  return 'Research, brainstorm, or plan the work so the implementation handoff is specific and actionable.';
2213
2347
  }
2214
2348
  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.';
2349
+ 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
2350
  }
2217
2351
  return `Implement the requested work directly. Keep the original request in scope: ${prompt}`;
2218
2352
  }
@@ -2220,7 +2354,7 @@ class OrchestratorAgent {
2220
2354
  if (role === 'research')
2221
2355
  return 'Produce a clear handoff that gives the next worker enough detail to execute without guessing.';
2222
2356
  if (role === 'qa')
2223
- return 'Verify the work against the request and prepare the next handoff if fixes are needed.';
2357
+ return 'Verify the delivered work against the handoff and explicit success criteria, and prepare a fix handoff if issues remain.';
2224
2358
  return 'Complete the requested implementation and prepare the next worker handoff.';
2225
2359
  }
2226
2360
  summarizeTodoTitle(text) {
@@ -2318,7 +2452,7 @@ class OrchestratorAgent {
2318
2452
  '',
2319
2453
  `Your task: Perform ${roleLabel} on the completed work above.`,
2320
2454
  role === 'qa'
2321
- ? 'Review the work against the original request. Report what passes and what fails. Be specific about any issues found.'
2455
+ ? '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
2456
  : role === 'research'
2323
2457
  ? 'Analyze the work and provide insights, suggestions, or additional research as requested.'
2324
2458
  : 'Review and provide your feedback on the completed work.',
@@ -2393,7 +2527,7 @@ class OrchestratorAgent {
2393
2527
  return assignedName === orchestratorName;
2394
2528
  const roleClass = String(orchestratorBot.role_class || '').trim().toLowerCase();
2395
2529
  if (role === 'coding')
2396
- return ['code', 'coder', 'builder', 'developer', 'engineer', 'orchestrator', 'manager'].includes(roleClass);
2530
+ return ['code', 'coding', 'coder', 'builder', 'developer', 'engineer', 'orchestrator', 'manager'].includes(roleClass);
2397
2531
  if (role === 'qa')
2398
2532
  return ['qa', 'review', 'reviewer'].includes(roleClass);
2399
2533
  if (role === 'research')
@@ -2667,61 +2801,8 @@ class OrchestratorAgent {
2667
2801
  return response;
2668
2802
  }
2669
2803
  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
- }
2804
+ void input;
2805
+ return input.candidateResponse;
2725
2806
  }
2726
2807
  isHighRiskPrompt(prompt) {
2727
2808
  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 +3296,14 @@ class OrchestratorAgent {
3215
3296
  reportHiddenRoleProgress(opts, roleName, detail) {
3216
3297
  opts.onProgress(`${roleName}::${detail}`);
3217
3298
  }
3299
+ formatConfirmationRequest(prompt) {
3300
+ const singleLine = String(prompt || '').replace(/\s+/g, ' ').trim();
3301
+ if (!singleLine)
3302
+ return 'Requested action';
3303
+ if (singleLine.length <= 180)
3304
+ return singleLine;
3305
+ return `${singleLine.slice(0, 177)}...`;
3306
+ }
3218
3307
  describeIntentForActivity(intent) {
3219
3308
  switch (intent.primaryMode) {
3220
3309
  case 'PROXY_MODE':
@@ -3325,6 +3414,21 @@ class OrchestratorAgent {
3325
3414
  }
3326
3415
  return assignments;
3327
3416
  }
3417
+ clerkModelLabel() {
3418
+ const runtime = this.clerk.getRuntimeInfo();
3419
+ if (!runtime.model)
3420
+ return 'Clerk';
3421
+ return `${runtime.model} | Clerk`;
3422
+ }
3423
+ async respondAsClerk(userPrompt, systemPrompt) {
3424
+ const response = await this.clerk.respond(userPrompt, systemPrompt);
3425
+ this.setLastResponseMeta({
3426
+ agentName: 'Clerk',
3427
+ botId: null,
3428
+ modelLabel: this.clerkModelLabel(),
3429
+ });
3430
+ return response;
3431
+ }
3328
3432
  hasRoleAssignments(assignments) {
3329
3433
  return Boolean(assignments.coding || assignments.qa || assignments.research);
3330
3434
  }
@@ -3356,7 +3460,7 @@ class OrchestratorAgent {
3356
3460
  const result = {};
3357
3461
  const roleClass = String(orchestratorBot?.role_class || '').trim().toLowerCase();
3358
3462
  if (orchestratorBot?.name) {
3359
- if (['code', 'coder', 'builder', 'developer', 'engineer'].includes(roleClass)) {
3463
+ if (['code', 'coding', 'coder', 'builder', 'developer', 'engineer'].includes(roleClass)) {
3360
3464
  result.coding = orchestratorBot.name;
3361
3465
  }
3362
3466
  else if (['qa', 'review', 'reviewer'].includes(roleClass)) {
@@ -3367,7 +3471,7 @@ class OrchestratorAgent {
3367
3471
  }
3368
3472
  }
3369
3473
  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;
3474
+ result.coding = result.coding || findByRole(['code', 'coding', 'coder', 'builder', 'developer', 'engineer'])?.name;
3371
3475
  result.qa = result.qa || findByRole(['qa', 'review', 'reviewer'])?.name;
3372
3476
  result.research = result.research || findByRole(['research', 'researcher', 'analyst', 'advisor'])?.name;
3373
3477
  return result;
@@ -3437,17 +3541,10 @@ class OrchestratorAgent {
3437
3541
  },
3438
3542
  });
3439
3543
  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.',
3544
+ 'Are you sure you want to continue with this request?',
3545
+ `Request: ${this.formatConfirmationRequest(prompt)}`,
3546
+ `Reason: ${executionSpec.riskReasons.join(', ') || 'sensitive change'}.`,
3547
+ 'Reply with "yes, continue" or "no, cancel".',
3451
3548
  ].join('\n');
3452
3549
  }
3453
3550
  async handlePendingCheckpoint(prompt, checkpoint, opts, fallbackState) {
@@ -3457,11 +3554,14 @@ class OrchestratorAgent {
3457
3554
  return 'Understood. I cancelled that pending action.';
3458
3555
  }
3459
3556
  if (!this.isCheckpointConfirmation(prompt)) {
3557
+ const pausedPrompt = typeof checkpoint.payload?.prompt === 'string'
3558
+ ? checkpoint.payload.prompt
3559
+ : fallbackState.userPromptRaw || '';
3460
3560
  return [
3461
- 'I still have a paused high-risk action waiting for confirmation.',
3462
- '',
3561
+ 'I still have a paused action waiting for confirmation.',
3562
+ `Request: ${this.formatConfirmationRequest(pausedPrompt)}`,
3463
3563
  `Reason: ${checkpoint.reason}.`,
3464
- 'Reply with "yes, continue" to resume it or "no, cancel" to discard it.',
3564
+ 'Reply with "yes, continue" or "no, cancel".',
3465
3565
  ].join('\n');
3466
3566
  }
3467
3567
  const payload = checkpoint.payload || {};
@@ -3710,7 +3810,7 @@ class OrchestratorAgent {
3710
3810
  merged.qa ? `${merged.qa} handles QA` : null,
3711
3811
  merged.research ? `${merged.research} handles research` : null,
3712
3812
  ].filter(Boolean);
3713
- if (parts.length > 0) {
3813
+ if (parts.length > 0 && (0, storage_mode_1.isServerStorageMode)()) {
3714
3814
  data.upsertMemoryFact({
3715
3815
  agentId,
3716
3816
  conversationId,
@@ -3922,13 +4022,15 @@ class OrchestratorAgent {
3922
4022
  if (!workflow)
3923
4023
  return;
3924
4024
  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
- });
4025
+ if ((0, storage_mode_1.isServerStorageMode)()) {
4026
+ data.upsertMemoryFact({
4027
+ agentId,
4028
+ conversationId,
4029
+ factType: 'operating_instruction',
4030
+ content: `Preferred workflow for this project: ${workflow.steps.join('; ')}.`,
4031
+ extractionMethod: 'orchestrator',
4032
+ });
4033
+ }
3932
4034
  data.logAdminAudit({
3933
4035
  actorType: 'orchestrator',
3934
4036
  actorId: 'Project Manager',