funolio-agent 0.16.4 → 0.17.2

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 (53) hide show
  1. package/dist/auth/token-refresh.d.ts +13 -5
  2. package/dist/auth/token-refresh.d.ts.map +1 -1
  3. package/dist/auth/token-refresh.js +123 -28
  4. package/dist/auth/token-refresh.js.map +1 -1
  5. package/dist/clerk-model.d.ts.map +1 -1
  6. package/dist/clerk-model.js +3 -0
  7. package/dist/clerk-model.js.map +1 -1
  8. package/dist/config.d.ts +9 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/local-server.d.ts.map +1 -1
  12. package/dist/local-server.js +49 -54
  13. package/dist/local-server.js.map +1 -1
  14. package/dist/message-loop.d.ts +1 -0
  15. package/dist/message-loop.d.ts.map +1 -1
  16. package/dist/message-loop.js +24 -1
  17. package/dist/message-loop.js.map +1 -1
  18. package/dist/oauth.d.ts.map +1 -1
  19. package/dist/oauth.js +20 -0
  20. package/dist/oauth.js.map +1 -1
  21. package/dist/orchestration/front-door-policy.d.ts +8 -0
  22. package/dist/orchestration/front-door-policy.d.ts.map +1 -1
  23. package/dist/orchestration/front-door-policy.js +69 -203
  24. package/dist/orchestration/front-door-policy.js.map +1 -1
  25. package/dist/orchestration/orchestrator-operating-prompt.d.ts.map +1 -1
  26. package/dist/orchestration/orchestrator-operating-prompt.js +5 -8
  27. package/dist/orchestration/orchestrator-operating-prompt.js.map +1 -1
  28. package/dist/orchestration/safeguards.d.ts +1 -1
  29. package/dist/orchestration/safeguards.js +1 -1
  30. package/dist/orchestrator.d.ts +7 -2
  31. package/dist/orchestrator.d.ts.map +1 -1
  32. package/dist/orchestrator.js +100 -73
  33. package/dist/orchestrator.js.map +1 -1
  34. package/dist/providers/anthropic.d.ts +1 -0
  35. package/dist/providers/anthropic.d.ts.map +1 -1
  36. package/dist/providers/anthropic.js +10 -2
  37. package/dist/providers/anthropic.js.map +1 -1
  38. package/dist/tools/run-command.d.ts.map +1 -1
  39. package/dist/tools/run-command.js +90 -11
  40. package/dist/tools/run-command.js.map +1 -1
  41. package/dist/tools/schedule-task.d.ts.map +1 -1
  42. package/dist/tools/schedule-task.js +3 -0
  43. package/dist/tools/schedule-task.js.map +1 -1
  44. package/dist/tools/search-memory.d.ts.map +1 -1
  45. package/dist/tools/search-memory.js +10 -6
  46. package/dist/tools/search-memory.js.map +1 -1
  47. package/dist/types.d.ts +2 -0
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/workflow-engine.d.ts +6 -1
  50. package/dist/workflow-engine.d.ts.map +1 -1
  51. package/dist/workflow-engine.js +30 -1
  52. package/dist/workflow-engine.js.map +1 -1
  53. package/package.json +1 -1
@@ -9,7 +9,7 @@
9
9
  * 4. Verifies results against success criteria
10
10
  * 5. Reports concise status back to the user
11
11
  *
12
- * The user sees ONLY their conversation with O.
12
+ * The user sees ONLY their conversation with the orchestrator.
13
13
  * Worker LLM conversations happen behind the scenes.
14
14
  */
15
15
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
@@ -67,7 +67,7 @@ const ORCHESTRATION_NODE_RETRY_LIMITS = {
67
67
  understand_request: 0,
68
68
  risk_gate: 0,
69
69
  choose_path: 0,
70
- execute_direct: 0,
70
+ execute_direct: 1,
71
71
  delegate_specialist: 1,
72
72
  run_workflow: 1,
73
73
  verify_result: 2,
@@ -164,12 +164,37 @@ class OrchestratorAgent {
164
164
  }
165
165
  }
166
166
  }
167
- if (this.isOrchestratorV2Enabled() && selectedOrchestrator) {
168
- const v2Response = await this.handleOrchestratorFrontDoor(prompt, conversationId, opts, selectedOrchestrator, conversationProjectId, initialProject, initialPolicy, promptAssignments, initialAssignments, initialOverview, orchestrationState);
169
- if (v2Response) {
170
- return v2Response;
167
+ // ─── Single-Call Orchestrator ─────────────────────────
168
+ // Ben always does the work first. If user named another bot, delegate AFTER.
169
+ if (selectedOrchestrator) {
170
+ orchestrationState.intent = 'execute_self';
171
+ (0, state_1.setOrchestrationPath)(orchestrationState, 'direct', 'direct');
172
+ this.recordOrchestrationAudit(orchestrationState, 'choose_path', 'path_selected', 'Orchestrator executes directly (single-call model).');
173
+ // Check for delegation targets before executing
174
+ const mentionedOtherBots = this.getMentionedAgents(prompt)
175
+ .filter((a) => a.id !== selectedOrchestrator.id);
176
+ // Step 1: Ben does the work — if delegating, tell Ben to skip the delegate's role
177
+ let executionPrompt = prompt;
178
+ if (mentionedOtherBots.length > 0) {
179
+ const botNames = mentionedOtherBots.map((b) => b.name).join(', ');
180
+ const role = this.inferDelegationRole(prompt);
181
+ executionPrompt = `${prompt}\n\n[System: ${botNames} will handle ${role} after you finish. Do YOUR work only — do not perform ${role} yourself. Complete your task and return your deliverable.]`;
182
+ }
183
+ const directResult = await this.executeOrchestratorOwnedWork(executionPrompt, conversationId, selectedOrchestrator, initialAssignments, initialProject, opts, orchestrationState);
184
+ if (mentionedOtherBots.length > 0 && directResult.ok) {
185
+ const delegateBot = mentionedOtherBots[0];
186
+ this.recordOrchestrationAudit(orchestrationState, 'delegate_specialist', 'delegated', `Delegating to ${delegateBot.name} after orchestrator completed work.`, { delegateBot: delegateBot.name });
187
+ try {
188
+ const delegateResponse = await this.executeDelegation(directResult.response, prompt, delegateBot, conversationId, initialProject, opts, orchestrationState);
189
+ return delegateResponse;
190
+ }
191
+ catch (err) {
192
+ // If delegation fails, still return Ben's result
193
+ this.recordOrchestrationAudit(orchestrationState, 'delegate_specialist', 'blocked', `Delegation to ${delegateBot.name} failed: ${err?.message || err}. Returning orchestrator result.`);
194
+ return directResult.response;
195
+ }
171
196
  }
172
- orchestrationState.fallbackUsed = true;
197
+ return directResult.response;
173
198
  }
174
199
  this.reportHiddenRoleProgress(opts, 'intent_classifier', 'Classifying request');
175
200
  (0, state_1.addHelperRoleUsage)(orchestrationState, 'intent_classifier');
@@ -943,15 +968,9 @@ class OrchestratorAgent {
943
968
  return lines.join('\n\n');
944
969
  }
945
970
  getAgentReferenceTerms(agent) {
946
- const terms = [agent.name];
947
- const normalized = agent.name.trim().toLowerCase();
948
- if (normalized === 'brain')
949
- terms.push('gpt');
950
- if (normalized === 'ben')
951
- terms.push('claude');
952
- if (normalized === 'john')
953
- terms.push('codex');
954
- return Array.from(new Set(terms.map((term) => term.trim()).filter(Boolean)));
971
+ // Only use exact bot names — no aliases.
972
+ // Aliases like "claude" → "Ben" cause false matches on every prompt.
973
+ return [agent.name.trim()].filter(Boolean);
955
974
  }
956
975
  /**
957
976
  * Interpret user intent using the clerk.
@@ -1420,13 +1439,6 @@ class OrchestratorAgent {
1420
1439
  .replace(/\n?STATUS:\s*(PASS|FAIL|COMPLETE|BLOCKED|UNKNOWN)\s*$/i, '')
1421
1440
  .trim() || rawText.trim();
1422
1441
  }
1423
- isOrchestratorV2Enabled() {
1424
- const envValue = String(process.env.ORCH_V2_ENABLED || '').trim().toLowerCase();
1425
- if (envValue === '1' || envValue === 'true' || envValue === 'yes' || envValue === 'on') {
1426
- return true;
1427
- }
1428
- return data.getSetting('orchestrator_v2_enabled') === 'true';
1429
- }
1430
1442
  resolveSelectedOrchestratorBot(conversation) {
1431
1443
  const conversationBotId = conversation?.agent_id;
1432
1444
  if (conversationBotId) {
@@ -1526,22 +1538,10 @@ class OrchestratorAgent {
1526
1538
  explicitWorkflowRequested: normalizedDecision.explicitWorkflowRequested,
1527
1539
  corrected: normalizedDecision.corrected,
1528
1540
  });
1541
+ // Clarify should never reach here (parser + policy convert to execute_self),
1542
+ // but if it somehow does, treat it as execute_self — just do the work.
1529
1543
  if (decision.mode === 'clarify') {
1530
- (0, state_1.setOrchestrationPath)(state, 'clarify');
1531
- this.recordOrchestrationAudit(state, 'require_confirmation', 'clarification_requested', decision.reason || 'Orchestrator requested clarification.', { clarificationQuestions: decision.clarification_questions || [] });
1532
- return this.formatClarificationResponse({
1533
- primaryMode: 'DIRECT_CONVERSATION',
1534
- secondaryModes: [],
1535
- executionOrder: ['DIRECT_CONVERSATION'],
1536
- userFacingMode: 'DIRECT_RESPONSE',
1537
- targetScope: 'SELF',
1538
- confidence: 'HIGH',
1539
- intent: 'question',
1540
- isMultiStep: false,
1541
- reasoning: decision.reason,
1542
- needsClarification: true,
1543
- clarificationQuestions: decision.clarification_questions || ['Can you clarify what you want me to do?'],
1544
- }, overview);
1544
+ decision.mode = 'execute_self';
1545
1545
  }
1546
1546
  if (decision.mode === 'respond') {
1547
1547
  const response = decision.response?.trim() || this.handleDirectConversation(prompt, overview, roleAssignments, state);
@@ -1550,39 +1550,10 @@ class OrchestratorAgent {
1550
1550
  return response;
1551
1551
  }
1552
1552
  if (decision.mode === 'execute_self') {
1553
- const directIntent = {
1554
- primaryMode: 'DIRECT_CONVERSATION',
1555
- secondaryModes: [],
1556
- executionOrder: ['DIRECT_CONVERSATION'],
1557
- userFacingMode: 'DIRECT_RESPONSE',
1558
- targetScope: 'SELF',
1559
- confidence: 'HIGH',
1560
- intent: this.inferFrontDoorIntent(normalizedDecision.taskType, decision),
1561
- isMultiStep: false,
1562
- reasoning: decision.reason,
1563
- };
1564
- const frontDoorEffectivePolicy = this.buildFrontDoorEffectivePolicy(policy, orchestratorBot, roleAssignments, normalizedDecision.taskType);
1565
- const validation = this.validateIntentExecution(prompt, directIntent, frontDoorEffectivePolicy);
1566
- if (!validation.ok) {
1567
- return validation.clarificationQuestions?.length
1568
- ? this.formatClarificationResponse({ ...directIntent, clarificationQuestions: validation.clarificationQuestions }, overview)
1569
- : null;
1570
- }
1571
- const guardrailQuestions = this.buildDeterministicGuardrailQuestions(validation.executionSpec, roleAssignments, directIntent);
1572
- if (guardrailQuestions.length > 0) {
1573
- return this.formatClarificationResponse({ ...directIntent, clarificationQuestions: guardrailQuestions }, overview);
1574
- }
1575
- this.applyExecutionSpecToState(state, validation.executionSpec);
1576
- if (validation.executionSpec.requiresConfirmation) {
1577
- return this.createConfirmationCheckpoint(prompt, conversationId, directIntent, validation.executionSpec, projectId, roleAssignments, state);
1578
- }
1553
+ // No guardrails, no confirmation checkpoints, no validation blocking.
1554
+ // Ben just does the work — like Claude CLI.
1555
+ (0, state_1.setOrchestrationPath)(state, 'direct', 'direct');
1579
1556
  const directResult = await this.executeOrchestratorOwnedWork(prompt, conversationId, orchestratorBot, roleAssignments, project, opts, state);
1580
- if (directResult.ok && directIntent.intent === 'build' && state.requestedArtifactTargets.length > 0) {
1581
- const artifactValidation = this.validateRequestedArtifactTargets(state, project);
1582
- if (!artifactValidation.ok) {
1583
- return `${orchestratorBot.name} encountered an issue: ${artifactValidation.message}`;
1584
- }
1585
- }
1586
1557
  return directResult.response;
1587
1558
  }
1588
1559
  if (decision.mode === 'delegate') {
@@ -1736,11 +1707,11 @@ class OrchestratorAgent {
1736
1707
  this.reportHiddenRoleProgress(opts, 'orchestrator', `${orchestratorBot.name} is working on the request`);
1737
1708
  const result = await this.runNodeWithRetry('execute_direct', state, async () => this.workflowEngine.execute(prompt, conversationId, orchestratorBot.id, {
1738
1709
  disableDecomposition: true,
1739
- isOrchestrated: true,
1710
+ isOrchestrated: false,
1740
1711
  projectId: project?.id || state.projectId || null,
1741
1712
  workspacePath: project?.folder?.trim() || undefined,
1742
- persistConversationMessages: false,
1743
- workerMode: true,
1713
+ persistConversationMessages: true,
1714
+ workerMode: false,
1744
1715
  onWorkerChunk: opts.onWorkerChunk,
1745
1716
  onProgress: (progress) => {
1746
1717
  if (progress.event === 'step-started') {
@@ -1790,6 +1761,59 @@ class OrchestratorAgent {
1790
1761
  this.recordOrchestrationAudit(state, 'finalize_response', 'completed', 'Completed orchestrator-owned execution.');
1791
1762
  return { ok: true, response: reviewed, rawResult: result };
1792
1763
  }
1764
+ /**
1765
+ * Delegate Ben's completed work to another bot (e.g., John for QA, Brain for research).
1766
+ * Called AFTER executeOrchestratorOwnedWork completes.
1767
+ */
1768
+ async executeDelegation(benResult, originalPrompt, delegateBot, conversationId, project, opts, state) {
1769
+ const role = this.inferDelegationRole(originalPrompt);
1770
+ const roleLabel = role === 'qa' ? 'QA / review' : role === 'research' ? 'research / analysis' : 'review';
1771
+ const delegationPrompt = [
1772
+ `The user's original request:\n${originalPrompt}`,
1773
+ '',
1774
+ `The orchestrator (Ben) completed the following work:\n${benResult.length > 8000 ? benResult.slice(0, 8000) + '\n...(truncated)' : benResult}`,
1775
+ '',
1776
+ `Your task: Perform ${roleLabel} on the completed work above.`,
1777
+ role === 'qa'
1778
+ ? 'Review the work against the original request. Report what passes and what fails. Be specific about any issues found.'
1779
+ : role === 'research'
1780
+ ? 'Analyze the work and provide insights, suggestions, or additional research as requested.'
1781
+ : 'Review and provide your feedback on the completed work.',
1782
+ ].join('\n');
1783
+ this.reportHiddenRoleProgress(opts, 'orchestrator', `Sending work to ${delegateBot.name} for ${roleLabel}`);
1784
+ (0, state_1.addHelperRoleUsage)(state, 'dispatch_controller');
1785
+ (0, state_1.addDelegateTarget)(state, delegateBot.name);
1786
+ const result = await this.runNodeWithRetry('delegate_specialist', state, async () => this.workflowEngine.execute(delegationPrompt, conversationId, delegateBot.id, {
1787
+ disableDecomposition: true,
1788
+ isOrchestrated: false,
1789
+ workerMode: false,
1790
+ projectId: project?.id || state.projectId || null,
1791
+ workspacePath: project?.folder?.trim() || undefined,
1792
+ persistConversationMessages: true,
1793
+ onWorkerChunk: opts.onWorkerChunk,
1794
+ onProgress: (progress) => {
1795
+ if (progress.event === 'step-started') {
1796
+ this.reportHiddenRoleProgress(opts, 'orchestrator', `${delegateBot.name} is reviewing the work`);
1797
+ }
1798
+ else if (progress.event === 'step-completed') {
1799
+ this.reportHiddenRoleProgress(opts, 'orchestrator', `${delegateBot.name} completed review`);
1800
+ }
1801
+ },
1802
+ }));
1803
+ const delegateResponse = this.stripWorkerProtocol(result.mergedResult);
1804
+ this.reportHiddenRoleProgress(opts, 'orchestrator', `${delegateBot.name} completed ${roleLabel}`);
1805
+ this.recordOrchestrationAudit(state, 'finalize_response', 'completed', `Delegation to ${delegateBot.name} completed.`);
1806
+ // Return combined response
1807
+ return `${benResult}\n\n---\n\n**${delegateBot.name} (${roleLabel}):**\n\n${delegateResponse}`;
1808
+ }
1809
+ inferDelegationRole(prompt) {
1810
+ const lower = prompt.toLowerCase();
1811
+ if (/\b(qa|test|review|check|verify|audit|pass\/fail)\b/.test(lower))
1812
+ return 'qa';
1813
+ if (/\b(research|brainstorm|analyze|investigate|evaluate)\b/.test(lower))
1814
+ return 'research';
1815
+ return 'discuss';
1816
+ }
1793
1817
  inferRequestedWorkflowRoles(prompt, roleAssignments) {
1794
1818
  const normalized = String(prompt || '').toLowerCase();
1795
1819
  const explicitResearch = /\b(brain|gpt|research(?:es|ed|ing)?|investigat(?:e|es|ed|ing)|analy[sz](?:e|es|ed|ing)|analysis|sound idea|review the idea|pressure[- ]?test|brainstorm(?:ed|ing)?)\b/.test(normalized)
@@ -2421,6 +2445,9 @@ class OrchestratorAgent {
2421
2445
  if (state && workflowTemplate) {
2422
2446
  state.preferredWorkflowUsed = true;
2423
2447
  }
2448
+ // TODO: planWorkflow uses the cheap clerk (GPT-4o-mini). Per architecture doc,
2449
+ // workflow planning should be done by the orchestrator LLM, not the clerk.
2450
+ // The clerk should only do summaries, extraction, and compression.
2424
2451
  const plannedSteps = !workflowTemplate
2425
2452
  ? await this.clerk.planWorkflow({
2426
2453
  prompt,