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.
- package/dist/agent-config.d.ts +9 -1
- package/dist/agent-config.d.ts.map +1 -1
- package/dist/agent-config.js +4 -1
- package/dist/agent-config.js.map +1 -1
- package/dist/approval.d.ts +1 -0
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +12 -0
- package/dist/approval.js.map +1 -1
- package/dist/auth/auto-detect.d.ts +11 -3
- package/dist/auth/auto-detect.d.ts.map +1 -1
- package/dist/auth/auto-detect.js +136 -168
- package/dist/auth/auto-detect.js.map +1 -1
- package/dist/auth/subscription-runtime.js +1 -1
- package/dist/auth/subscription-runtime.js.map +1 -1
- package/dist/auto-organizer.d.ts.map +1 -1
- package/dist/auto-organizer.js +4 -3
- package/dist/auto-organizer.js.map +1 -1
- package/dist/backfill.d.ts.map +1 -1
- package/dist/backfill.js +34 -30
- package/dist/backfill.js.map +1 -1
- package/dist/bot-manager.d.ts +4 -8
- package/dist/bot-manager.d.ts.map +1 -1
- package/dist/bot-manager.js +31 -160
- package/dist/bot-manager.js.map +1 -1
- package/dist/clerk-model.d.ts +15 -7
- package/dist/clerk-model.d.ts.map +1 -1
- package/dist/clerk-model.js +78 -43
- package/dist/clerk-model.js.map +1 -1
- package/dist/cli-session-epoch.d.ts +10 -0
- package/dist/cli-session-epoch.d.ts.map +1 -0
- package/dist/cli-session-epoch.js +61 -0
- package/dist/cli-session-epoch.js.map +1 -0
- package/dist/cli.js +7 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/import-history.js +5 -1
- package/dist/commands/import-history.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +30 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/pool.js +1 -1
- package/dist/commands/pool.js.map +1 -1
- package/dist/commands/setup.d.ts +37 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +146 -43
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +117 -255
- package/dist/commands/start.js.map +1 -1
- package/dist/config-cleanup.d.ts.map +1 -1
- package/dist/config-cleanup.js +2 -1
- package/dist/config-cleanup.js.map +1 -1
- package/dist/config.d.ts +6 -9
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -18
- package/dist/config.js.map +1 -1
- package/dist/context-window.d.ts +33 -5
- package/dist/context-window.d.ts.map +1 -1
- package/dist/context-window.js +122 -21
- package/dist/context-window.js.map +1 -1
- package/dist/eval/orchestrator-front-door-replay.js +1 -1
- package/dist/eval/orchestrator-front-door-replay.js.map +1 -1
- package/dist/eval/policy-detection-replay.js +1 -1
- package/dist/eval/policy-detection-replay.js.map +1 -1
- package/dist/import-parser-core.d.ts.map +1 -1
- package/dist/import-parser-core.js +74 -8
- package/dist/import-parser-core.js.map +1 -1
- package/dist/integration-tokens.d.ts +1 -6
- package/dist/integration-tokens.d.ts.map +1 -1
- package/dist/integration-tokens.js +38 -40
- package/dist/integration-tokens.js.map +1 -1
- package/dist/local-cli-pty-manager.d.ts +50 -0
- package/dist/local-cli-pty-manager.d.ts.map +1 -0
- package/dist/local-cli-pty-manager.js +645 -0
- package/dist/local-cli-pty-manager.js.map +1 -0
- package/dist/local-data.d.ts +89 -6
- package/dist/local-data.d.ts.map +1 -1
- package/dist/local-data.js +600 -63
- package/dist/local-data.js.map +1 -1
- package/dist/local-db.d.ts.map +1 -1
- package/dist/local-db.js +74 -1
- package/dist/local-db.js.map +1 -1
- package/dist/local-funnel.d.ts +0 -7
- package/dist/local-funnel.d.ts.map +1 -1
- package/dist/local-funnel.js +22 -30
- package/dist/local-funnel.js.map +1 -1
- package/dist/local-import-worker.d.ts.map +1 -1
- package/dist/local-import-worker.js +49 -4
- package/dist/local-import-worker.js.map +1 -1
- package/dist/local-memory-search.d.ts +1 -0
- package/dist/local-memory-search.d.ts.map +1 -1
- package/dist/local-memory-search.js +107 -21
- package/dist/local-memory-search.js.map +1 -1
- package/dist/local-server.d.ts +21 -0
- package/dist/local-server.d.ts.map +1 -1
- package/dist/local-server.js +1057 -501
- package/dist/local-server.js.map +1 -1
- package/dist/mcp/bridge-server.d.ts.map +1 -1
- package/dist/mcp/bridge-server.js +2 -1
- package/dist/mcp/bridge-server.js.map +1 -1
- package/dist/mcp/local-memory-server.d.ts +6 -1
- package/dist/mcp/local-memory-server.d.ts.map +1 -1
- package/dist/mcp/local-memory-server.js +38 -13
- package/dist/mcp/local-memory-server.js.map +1 -1
- package/dist/mcp/manager.d.ts +3 -22
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +66 -320
- package/dist/mcp/manager.js.map +1 -1
- package/dist/memory-extraction.d.ts +2 -0
- package/dist/memory-extraction.d.ts.map +1 -1
- package/dist/memory-extraction.js +3 -1
- package/dist/memory-extraction.js.map +1 -1
- package/dist/message-loop.d.ts +1 -3
- package/dist/message-loop.d.ts.map +1 -1
- package/dist/message-loop.js +220 -437
- package/dist/message-loop.js.map +1 -1
- package/dist/mqtt-client.d.ts +2 -28
- package/dist/mqtt-client.d.ts.map +1 -1
- package/dist/mqtt-client.js +2 -2
- package/dist/mqtt-client.js.map +1 -1
- package/dist/oauth.d.ts +6 -0
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +91 -0
- package/dist/oauth.js.map +1 -1
- package/dist/orchestration/front-door-policy.d.ts +5 -2
- package/dist/orchestration/front-door-policy.d.ts.map +1 -1
- package/dist/orchestration/front-door-policy.js +25 -28
- package/dist/orchestration/front-door-policy.js.map +1 -1
- package/dist/orchestration/orchestrator-blocked-prompt.d.ts +2 -1
- package/dist/orchestration/orchestrator-blocked-prompt.d.ts.map +1 -1
- package/dist/orchestration/orchestrator-blocked-prompt.js +12 -1
- package/dist/orchestration/orchestrator-blocked-prompt.js.map +1 -1
- package/dist/orchestration/orchestrator-final-response-prompt.d.ts +4 -1
- package/dist/orchestration/orchestrator-final-response-prompt.d.ts.map +1 -1
- package/dist/orchestration/orchestrator-final-response-prompt.js +9 -7
- package/dist/orchestration/orchestrator-final-response-prompt.js.map +1 -1
- package/dist/orchestration/orchestrator-operating-prompt.d.ts +11 -0
- package/dist/orchestration/orchestrator-operating-prompt.d.ts.map +1 -1
- package/dist/orchestration/orchestrator-operating-prompt.js +67 -44
- package/dist/orchestration/orchestrator-operating-prompt.js.map +1 -1
- package/dist/orchestration/worker-operating-prompt.d.ts +2 -0
- package/dist/orchestration/worker-operating-prompt.d.ts.map +1 -1
- package/dist/orchestration/worker-operating-prompt.js +41 -2
- package/dist/orchestration/worker-operating-prompt.js.map +1 -1
- package/dist/orchestrator.d.ts +17 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +328 -166
- package/dist/orchestrator.js.map +1 -1
- package/dist/prompt-template.js +3 -3
- package/dist/prompt-template.js.map +1 -1
- package/dist/providers/anthropic.d.ts +0 -5
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +29 -75
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/claude-cli-prompt.d.ts.map +1 -1
- package/dist/providers/claude-cli-prompt.js +22 -6
- package/dist/providers/claude-cli-prompt.js.map +1 -1
- package/dist/providers/claude-cli.d.ts.map +1 -1
- package/dist/providers/claude-cli.js +36 -142
- package/dist/providers/claude-cli.js.map +1 -1
- package/dist/providers/codex-cli.d.ts.map +1 -1
- package/dist/providers/codex-cli.js +148 -74
- package/dist/providers/codex-cli.js.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +4 -2
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +27 -2
- package/dist/providers/openai.js.map +1 -1
- package/dist/runtime-context.d.ts +10 -0
- package/dist/runtime-context.d.ts.map +1 -0
- package/dist/runtime-context.js +30 -0
- package/dist/runtime-context.js.map +1 -0
- package/dist/storage-mode.d.ts +5 -0
- package/dist/storage-mode.d.ts.map +1 -0
- package/dist/storage-mode.js +21 -0
- package/dist/storage-mode.js.map +1 -0
- package/dist/subagent/queue.d.ts.map +1 -1
- package/dist/subagent/queue.js +1 -0
- package/dist/subagent/queue.js.map +1 -1
- package/dist/summarization-pipeline.d.ts +10 -0
- package/dist/summarization-pipeline.d.ts.map +1 -1
- package/dist/summarization-pipeline.js +147 -34
- package/dist/summarization-pipeline.js.map +1 -1
- package/dist/tool-permissions.d.ts +2 -0
- package/dist/tool-permissions.d.ts.map +1 -0
- package/dist/tool-permissions.js +25 -0
- package/dist/tool-permissions.js.map +1 -0
- package/dist/tools/analyze-image.js +2 -2
- package/dist/tools/analyze-image.js.map +1 -1
- package/dist/tools/edit-file.js +3 -3
- package/dist/tools/edit-file.js.map +1 -1
- package/dist/tools/index.d.ts +7 -8
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +106 -60
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list-directory.js +7 -4
- package/dist/tools/list-directory.js.map +1 -1
- package/dist/tools/read-file.js +3 -3
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/run-command.js +3 -3
- package/dist/tools/run-command.js.map +1 -1
- package/dist/tools/sandbox.d.ts +10 -5
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +41 -13
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/search-codebase.js +2 -2
- package/dist/tools/search-codebase.js.map +1 -1
- package/dist/tools/search-local-memory.d.ts.map +1 -1
- package/dist/tools/search-local-memory.js +19 -8
- package/dist/tools/search-local-memory.js.map +1 -1
- package/dist/tools/search-memory.d.ts.map +1 -1
- package/dist/tools/search-memory.js +9 -3
- package/dist/tools/search-memory.js.map +1 -1
- package/dist/tools/spawn-subagent.d.ts.map +1 -1
- package/dist/tools/spawn-subagent.js +1 -0
- package/dist/tools/spawn-subagent.js.map +1 -1
- package/dist/tools/write-file.js +3 -3
- package/dist/tools/write-file.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +1 -1
- package/dist/verification/index.js +2 -2
- package/dist/verification/index.js.map +1 -1
- package/dist/wizard-state.d.ts.map +1 -1
- package/dist/wizard-state.js +16 -2
- package/dist/wizard-state.js.map +1 -1
- package/dist/wizard-support.d.ts +2 -2
- package/dist/wizard-support.d.ts.map +1 -1
- package/dist/wizard-support.js +88 -99
- package/dist/wizard-support.js.map +1 -1
- package/dist/workflow-engine.d.ts +9 -3
- package/dist/workflow-engine.d.ts.map +1 -1
- package/dist/workflow-engine.js +378 -82
- package/dist/workflow-engine.js.map +1 -1
- package/package.json +2 -1
package/dist/orchestrator.js
CHANGED
|
@@ -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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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', `
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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 '
|
|
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 '
|
|
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
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
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
|
|
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: '
|
|
1596
|
-
reason: '
|
|
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
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
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
|
-
|
|
2027
|
-
|
|
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
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
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
|
-
|
|
2185
|
-
|
|
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
|
|
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
|
|
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 '
|
|
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
|
|
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
|
-
? '
|
|
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
|
-
|
|
2671
|
-
|
|
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
|
-
'
|
|
3441
|
-
|
|
3442
|
-
`
|
|
3443
|
-
|
|
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
|
|
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"
|
|
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
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
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',
|