funolio-agent 1.0.53 → 1.0.75
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/approval.d.ts +1 -6
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +2 -7
- package/dist/approval.js.map +1 -1
- package/dist/bot-manager.d.ts +5 -1
- package/dist/bot-manager.d.ts.map +1 -1
- package/dist/bot-manager.js +23 -13
- package/dist/bot-manager.js.map +1 -1
- package/dist/cli-session-epoch.d.ts +1 -1
- package/dist/cli-session-epoch.d.ts.map +1 -1
- package/dist/cli-session-epoch.js +1 -1
- package/dist/cli-session-epoch.js.map +1 -1
- package/dist/cli-session-registry.d.ts +35 -0
- package/dist/cli-session-registry.d.ts.map +1 -0
- package/dist/cli-session-registry.js +177 -0
- package/dist/cli-session-registry.js.map +1 -0
- package/dist/cli.js +62 -0
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-server-manager.d.ts +129 -0
- package/dist/codex-app-server-manager.d.ts.map +1 -0
- package/dist/codex-app-server-manager.js +768 -0
- package/dist/codex-app-server-manager.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +8 -30
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup.d.ts +4 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +9 -25
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +77 -2
- package/dist/commands/start.js.map +1 -1
- package/dist/completion-marker.d.ts +7 -0
- package/dist/completion-marker.d.ts.map +1 -0
- package/dist/completion-marker.js +28 -0
- package/dist/completion-marker.js.map +1 -0
- package/dist/config.d.ts +6 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -3
- package/dist/config.js.map +1 -1
- package/dist/context-window.d.ts.map +1 -1
- package/dist/context-window.js +8 -1
- package/dist/context-window.js.map +1 -1
- package/dist/live-activity.d.ts +29 -0
- package/dist/live-activity.d.ts.map +1 -0
- package/dist/live-activity.js +36 -0
- package/dist/live-activity.js.map +1 -0
- package/dist/local-cli-pty-manager.d.ts +51 -0
- package/dist/local-cli-pty-manager.d.ts.map +1 -1
- package/dist/local-cli-pty-manager.js +1227 -114
- package/dist/local-cli-pty-manager.js.map +1 -1
- package/dist/local-data.d.ts +41 -0
- package/dist/local-data.d.ts.map +1 -1
- package/dist/local-data.js +140 -4
- package/dist/local-data.js.map +1 -1
- package/dist/local-db.d.ts.map +1 -1
- package/dist/local-db.js +55 -1
- package/dist/local-db.js.map +1 -1
- package/dist/local-server.d.ts +25 -0
- package/dist/local-server.d.ts.map +1 -1
- package/dist/local-server.js +528 -267
- package/dist/local-server.js.map +1 -1
- package/dist/message-loop.d.ts +6 -0
- package/dist/message-loop.d.ts.map +1 -1
- package/dist/message-loop.js +239 -89
- package/dist/message-loop.js.map +1 -1
- package/dist/mqtt-client.d.ts +10 -1
- package/dist/mqtt-client.d.ts.map +1 -1
- package/dist/mqtt-client.js +14 -1
- package/dist/mqtt-client.js.map +1 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +69 -29
- package/dist/oauth.js.map +1 -1
- package/dist/orchestration/orchestrator-operating-prompt.d.ts +1 -0
- package/dist/orchestration/orchestrator-operating-prompt.d.ts.map +1 -1
- package/dist/orchestration/orchestrator-operating-prompt.js +60 -0
- package/dist/orchestration/orchestrator-operating-prompt.js.map +1 -1
- package/dist/orchestration/validation.d.ts +40 -0
- package/dist/orchestration/validation.d.ts.map +1 -0
- package/dist/orchestration/validation.js +203 -0
- package/dist/orchestration/validation.js.map +1 -0
- package/dist/orchestrator.d.ts +21 -32
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +287 -725
- package/dist/orchestrator.js.map +1 -1
- package/dist/providers/claude-cli-prompt.d.ts.map +1 -1
- package/dist/providers/claude-cli-prompt.js +49 -5
- 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 +56 -5
- 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 +15 -10
- package/dist/providers/codex-cli.js.map +1 -1
- package/dist/response-guard.js +1 -1
- package/dist/response-guard.js.map +1 -1
- package/dist/tools/admin-tools.d.ts.map +1 -1
- package/dist/tools/admin-tools.js +8 -2
- package/dist/tools/admin-tools.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/search-conversation-history.d.ts +16 -0
- package/dist/tools/search-conversation-history.d.ts.map +1 -0
- package/dist/tools/search-conversation-history.js +324 -0
- package/dist/tools/search-conversation-history.js.map +1 -0
- package/dist/wizard-state.d.ts +7 -0
- package/dist/wizard-state.d.ts.map +1 -1
- package/dist/wizard-state.js +31 -2
- package/dist/wizard-state.js.map +1 -1
- package/dist/workflow-engine.d.ts +4 -1
- package/dist/workflow-engine.d.ts.map +1 -1
- package/dist/workflow-engine.js +190 -29
- package/dist/workflow-engine.js.map +1 -1
- package/package.json +1 -1
package/dist/orchestrator.js
CHANGED
|
@@ -47,7 +47,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
47
47
|
})();
|
|
48
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
49
|
exports.OrchestratorAgent = void 0;
|
|
50
|
+
exports.buildLocalDesktopOrchestratorRuntime = buildLocalDesktopOrchestratorRuntime;
|
|
51
|
+
const index_1 = require("./providers/index");
|
|
50
52
|
const status_parser_1 = require("./orchestration/status-parser");
|
|
53
|
+
const validation_1 = require("./orchestration/validation");
|
|
51
54
|
const front_door_policy_1 = require("./orchestration/front-door-policy");
|
|
52
55
|
const deterministic_path_1 = require("./orchestration/deterministic-path");
|
|
53
56
|
const orchestrator_operating_prompt_1 = require("./orchestration/orchestrator-operating-prompt");
|
|
@@ -61,6 +64,7 @@ const state_1 = require("./orchestration/state");
|
|
|
61
64
|
const orchestrator_profile_1 = require("./orchestrator-profile");
|
|
62
65
|
const context_window_1 = require("./context-window");
|
|
63
66
|
const data = __importStar(require("./local-data"));
|
|
67
|
+
const cli_session_epoch_1 = require("./cli-session-epoch");
|
|
64
68
|
const storage_mode_1 = require("./storage-mode");
|
|
65
69
|
const fs = __importStar(require("fs"));
|
|
66
70
|
const os = __importStar(require("os"));
|
|
@@ -81,12 +85,130 @@ const ORCHESTRATION_NODE_RETRY_LIMITS = {
|
|
|
81
85
|
require_confirmation: 0,
|
|
82
86
|
};
|
|
83
87
|
// ─── Orchestrator Agent ──────────────────────────────────────────
|
|
88
|
+
function resolveLocalProviderCredentials(providerName, profile) {
|
|
89
|
+
if (index_1.CLI_PROVIDERS.has(providerName)) {
|
|
90
|
+
return { apiKey: 'cli-auth' };
|
|
91
|
+
}
|
|
92
|
+
const directConnection = profile?.provider_connection_id
|
|
93
|
+
? data.getProviderConnection(profile.provider_connection_id)
|
|
94
|
+
: undefined;
|
|
95
|
+
if (directConnection?.oauth_token) {
|
|
96
|
+
return { apiKey: directConnection.oauth_token, authMode: 'oauth-bearer' };
|
|
97
|
+
}
|
|
98
|
+
if (directConnection?.api_key_enc) {
|
|
99
|
+
return { apiKey: directConnection.api_key_enc };
|
|
100
|
+
}
|
|
101
|
+
const providerConnection = data.findProviderConnection(providerName);
|
|
102
|
+
if (providerConnection?.oauth_token) {
|
|
103
|
+
return { apiKey: providerConnection.oauth_token, authMode: 'oauth-bearer' };
|
|
104
|
+
}
|
|
105
|
+
if (providerConnection?.api_key_enc) {
|
|
106
|
+
return { apiKey: providerConnection.api_key_enc };
|
|
107
|
+
}
|
|
108
|
+
if (profile?.api_key_enc) {
|
|
109
|
+
return { apiKey: profile.api_key_enc };
|
|
110
|
+
}
|
|
111
|
+
switch (providerName) {
|
|
112
|
+
case 'anthropic':
|
|
113
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
114
|
+
return { apiKey: process.env.ANTHROPIC_API_KEY };
|
|
115
|
+
break;
|
|
116
|
+
case 'openai':
|
|
117
|
+
if (process.env.OPENAI_API_KEY)
|
|
118
|
+
return { apiKey: process.env.OPENAI_API_KEY };
|
|
119
|
+
break;
|
|
120
|
+
case 'google':
|
|
121
|
+
if (process.env.GOOGLE_API_KEY)
|
|
122
|
+
return { apiKey: process.env.GOOGLE_API_KEY };
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`No credentials configured for provider ${providerName}.`);
|
|
126
|
+
}
|
|
127
|
+
function buildLocalDesktopOrchestratorRuntime(selectedBot) {
|
|
128
|
+
if (data.isClerkOrchestratorEnabled()) {
|
|
129
|
+
const providerName = (data.getSetting('clerk_provider') || '').trim();
|
|
130
|
+
const model = (data.getSetting('clerk_model') || '').trim() || 'default';
|
|
131
|
+
if (!providerName) {
|
|
132
|
+
throw new Error('Clerk is selected as orchestrator, but no clerk provider is configured.');
|
|
133
|
+
}
|
|
134
|
+
const credentials = resolveLocalProviderCredentials(providerName);
|
|
135
|
+
const llm = (0, index_1.createProvider)(providerName, {
|
|
136
|
+
apiKey: credentials.apiKey,
|
|
137
|
+
model,
|
|
138
|
+
runtimeMode: 'local_desktop',
|
|
139
|
+
...(credentials.authMode ? { authMode: credentials.authMode } : {}),
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
kind: 'clerk',
|
|
143
|
+
llm,
|
|
144
|
+
providerName,
|
|
145
|
+
model,
|
|
146
|
+
agentName: 'Clerk',
|
|
147
|
+
botId: null,
|
|
148
|
+
modelLabel: model ? `${model} | Clerk` : 'Clerk',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const profile = selectedBot || data.getOrchestratorBot() || data.getDefaultAgentProfile();
|
|
152
|
+
if (!profile) {
|
|
153
|
+
throw new Error('No orchestrator or default bot is configured.');
|
|
154
|
+
}
|
|
155
|
+
const credentials = resolveLocalProviderCredentials(profile.provider, profile);
|
|
156
|
+
const llm = (0, index_1.createProvider)(profile.provider, {
|
|
157
|
+
apiKey: credentials.apiKey,
|
|
158
|
+
model: (profile.model || '').trim() || 'default',
|
|
159
|
+
runtimeMode: 'local_desktop',
|
|
160
|
+
...(credentials.authMode ? { authMode: credentials.authMode } : {}),
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
kind: 'bot',
|
|
164
|
+
llm,
|
|
165
|
+
providerName: profile.provider,
|
|
166
|
+
model: profile.model || null,
|
|
167
|
+
agentName: profile.name,
|
|
168
|
+
botId: profile.id,
|
|
169
|
+
modelLabel: profile.model || profile.name,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
84
172
|
class OrchestratorAgent {
|
|
85
|
-
|
|
173
|
+
orchestratorRuntime;
|
|
86
174
|
workflowEngine;
|
|
87
175
|
lastResponseMeta = null;
|
|
88
|
-
constructor(
|
|
89
|
-
|
|
176
|
+
constructor(orchestratorRuntime, workflowEngine) {
|
|
177
|
+
const runtimeCandidate = orchestratorRuntime;
|
|
178
|
+
if (runtimeCandidate && typeof runtimeCandidate.kind === 'string') {
|
|
179
|
+
this.orchestratorRuntime = orchestratorRuntime;
|
|
180
|
+
}
|
|
181
|
+
else if (runtimeCandidate && typeof runtimeCandidate.respond === 'function') {
|
|
182
|
+
const runtimeInfo = typeof runtimeCandidate.getRuntimeInfo === 'function'
|
|
183
|
+
? runtimeCandidate.getRuntimeInfo()
|
|
184
|
+
: {};
|
|
185
|
+
this.orchestratorRuntime = {
|
|
186
|
+
kind: 'clerk',
|
|
187
|
+
llm: {
|
|
188
|
+
chat: async (options) => ({
|
|
189
|
+
content: await runtimeCandidate.respond(String(options.messages?.[0]?.content || ''), String(options.system || '')),
|
|
190
|
+
}),
|
|
191
|
+
},
|
|
192
|
+
providerName: runtimeInfo?.provider || 'openai',
|
|
193
|
+
model: runtimeInfo?.model || null,
|
|
194
|
+
agentName: 'Clerk',
|
|
195
|
+
botId: null,
|
|
196
|
+
modelLabel: runtimeInfo?.model ? `${runtimeInfo.model} | Clerk` : 'Clerk',
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
this.orchestratorRuntime = {
|
|
201
|
+
kind: 'clerk',
|
|
202
|
+
llm: {
|
|
203
|
+
chat: async () => ({ content: '' }),
|
|
204
|
+
},
|
|
205
|
+
providerName: 'openai',
|
|
206
|
+
model: null,
|
|
207
|
+
agentName: 'Orchestrator',
|
|
208
|
+
botId: null,
|
|
209
|
+
modelLabel: null,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
90
212
|
this.workflowEngine = workflowEngine;
|
|
91
213
|
}
|
|
92
214
|
isLocalDesktopRuntime() {
|
|
@@ -264,41 +386,14 @@ class OrchestratorAgent {
|
|
|
264
386
|
if (frontDoorResult !== null)
|
|
265
387
|
return frontDoorResult;
|
|
266
388
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
orchestrationState
|
|
273
|
-
if (this.applyClassificationGuards(prompt, intent, routingMode)) {
|
|
274
|
-
(0, state_1.markMisrouteCorrected)(orchestrationState);
|
|
275
|
-
}
|
|
276
|
-
this.reportHiddenRoleProgress(opts, 'intent_classifier', `Classified request as ${this.describeIntentForActivity(intent)}`);
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
this.reportHiddenRoleProgress(opts, 'intent_classifier', 'Classifying request');
|
|
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)}`);
|
|
300
|
-
}
|
|
389
|
+
this.reportHiddenRoleProgress(opts, 'intent_classifier', selectedOrchestrator ? 'Recovering routing from code fallback' : 'Classifying request');
|
|
390
|
+
(0, state_1.addHelperRoleUsage)(orchestrationState, 'intent_classifier');
|
|
391
|
+
const intent = this.fallbackClassifyUserMessage(prompt, routingMode);
|
|
392
|
+
orchestrationState.fallbackUsed = true;
|
|
393
|
+
if (this.applyClassificationGuards(prompt, intent, routingMode)) {
|
|
394
|
+
(0, state_1.markMisrouteCorrected)(orchestrationState);
|
|
301
395
|
}
|
|
396
|
+
this.reportHiddenRoleProgress(opts, 'intent_classifier', `Classified request as ${this.describeIntentForActivity(intent)}`);
|
|
302
397
|
orchestrationState.intent = intent.intent;
|
|
303
398
|
orchestrationState.taskType = intent.primaryMode;
|
|
304
399
|
this.recordOrchestrationAudit(orchestrationState, 'understand_request', 'classified', intent.reasoning || `Classified as ${intent.primaryMode}.`, {
|
|
@@ -331,11 +426,6 @@ class OrchestratorAgent {
|
|
|
331
426
|
if (this.isOrchestratorControlMessage(prompt)) {
|
|
332
427
|
return this.formatOrchestratorControlResponse(prompt, effectiveAssignments, projectOverview);
|
|
333
428
|
}
|
|
334
|
-
if (routedPlan && routedPlan.intents.length > 1) {
|
|
335
|
-
(0, state_1.setOrchestrationPath)(orchestrationState, 'workflow', 'workflow');
|
|
336
|
-
this.recordOrchestrationAudit(orchestrationState, 'choose_path', 'path_selected', 'Selected ordered multi-intent workflow path.', { intentCount: routedPlan.intents.length });
|
|
337
|
-
return this.handleOrderedIntentSequence(routedPlan, prompt, conversationId, opts, promptAssignments, effectiveProjectId, effectiveProject, effectivePolicy, effectiveAssignments, projectOverview);
|
|
338
|
-
}
|
|
339
429
|
if (this.hasMode(intent, 'POLICY_UPDATE')) {
|
|
340
430
|
(0, state_1.addHelperRoleUsage)(orchestrationState, 'policy_interpreter');
|
|
341
431
|
this.reportHiddenRoleProgress(opts, 'policy_interpreter', 'Reviewing requested orchestration policy changes');
|
|
@@ -498,437 +588,6 @@ class OrchestratorAgent {
|
|
|
498
588
|
}
|
|
499
589
|
return next;
|
|
500
590
|
}
|
|
501
|
-
async decomposeAndRouteUserMessage(prompt, conversationId, projectId, routingMode) {
|
|
502
|
-
try {
|
|
503
|
-
const agents = (0, orchestrator_profile_1.filterOutOrchestratorProfiles)(data.listAgentProfiles());
|
|
504
|
-
const firstPass = await this.clerk.decomposeUserIntents({
|
|
505
|
-
prompt,
|
|
506
|
-
agents,
|
|
507
|
-
});
|
|
508
|
-
if (!firstPass?.intents?.length)
|
|
509
|
-
return null;
|
|
510
|
-
let plan = firstPass;
|
|
511
|
-
if (firstPass.intents.some((intent) => intent.needsContext)) {
|
|
512
|
-
const recentTurnsText = this.buildIntentVerifierRecentTurns(conversationId);
|
|
513
|
-
const verified = await this.clerk.verifyDecomposedUserIntents({
|
|
514
|
-
prompt,
|
|
515
|
-
priorPlan: firstPass,
|
|
516
|
-
recentTurnsText,
|
|
517
|
-
});
|
|
518
|
-
if (verified?.intents?.length) {
|
|
519
|
-
plan = verified;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
const clarificationIntents = plan.intents.filter((intent) => intent.needsClarification);
|
|
523
|
-
if (clarificationIntents.length > 0) {
|
|
524
|
-
return {
|
|
525
|
-
intents: clarificationIntents.map((intent) => ({
|
|
526
|
-
id: intent.id,
|
|
527
|
-
request: intent.request,
|
|
528
|
-
category: 'CLARIFICATION_NEEDED',
|
|
529
|
-
targetAgent: undefined,
|
|
530
|
-
dependsOn: [...intent.dependsOn],
|
|
531
|
-
requirements: [...intent.requirements],
|
|
532
|
-
confidence: intent.confidence || 'MEDIUM',
|
|
533
|
-
needsClarification: true,
|
|
534
|
-
clarificationQuestions: [...(intent.clarificationQuestions || [])],
|
|
535
|
-
reasoning: plan.reasoning || 'Intent verification requires clarification before routing.',
|
|
536
|
-
})),
|
|
537
|
-
reasoning: plan.reasoning,
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
const routed = [];
|
|
541
|
-
for (const atom of plan.intents) {
|
|
542
|
-
const routedAtom = await this.clerk.routeIntentAtom({
|
|
543
|
-
atom,
|
|
544
|
-
agents,
|
|
545
|
-
routingMode,
|
|
546
|
-
});
|
|
547
|
-
if (!routedAtom)
|
|
548
|
-
return null;
|
|
549
|
-
routed.push(routedAtom);
|
|
550
|
-
}
|
|
551
|
-
const collapsed = this.collapseSingleWorkerPlan(prompt, routed);
|
|
552
|
-
return {
|
|
553
|
-
intents: collapsed,
|
|
554
|
-
reasoning: plan.reasoning,
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
catch {
|
|
558
|
-
return null;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
buildIntentVerifierRecentTurns(conversationId) {
|
|
562
|
-
try {
|
|
563
|
-
const context = (0, context_window_1.getPromptContextWindow)(conversationId, 3);
|
|
564
|
-
return (0, context_window_1.formatTurnsForPrompt)(context.turns);
|
|
565
|
-
}
|
|
566
|
-
catch {
|
|
567
|
-
return '(no recent messages)';
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
collapseSingleWorkerPlan(prompt, intents) {
|
|
571
|
-
if (intents.length <= 1)
|
|
572
|
-
return intents;
|
|
573
|
-
if (this.hasExplicitOrderingLanguage(prompt))
|
|
574
|
-
return intents;
|
|
575
|
-
if (!this.hasConcreteArtifactDetails(prompt))
|
|
576
|
-
return intents;
|
|
577
|
-
if (this.countMentionedAgents(prompt) > 1)
|
|
578
|
-
return intents;
|
|
579
|
-
if (!intents.every((intent) => intent.category === 'ASK_WORKER' || intent.category === 'FULL_WORKFLOW'))
|
|
580
|
-
return intents;
|
|
581
|
-
const resolvedTargets = Array.from(new Set(intents
|
|
582
|
-
.map((intent) => (intent.targetAgent || '').trim().toLowerCase())
|
|
583
|
-
.filter(Boolean)));
|
|
584
|
-
if (resolvedTargets.length > 1 && this.countMentionedAgents(prompt) > 0)
|
|
585
|
-
return intents;
|
|
586
|
-
const mergedRequirements = Array.from(new Set(intents
|
|
587
|
-
.flatMap((intent) => [intent.request, ...intent.requirements])
|
|
588
|
-
.map((item) => item.trim())
|
|
589
|
-
.filter(Boolean)));
|
|
590
|
-
const collapsedConfidence = intents.some((intent) => (intent.confidence || 'MEDIUM') === 'LOW')
|
|
591
|
-
? 'LOW'
|
|
592
|
-
: intents.some((intent) => (intent.confidence || 'MEDIUM') === 'MEDIUM')
|
|
593
|
-
? 'MEDIUM'
|
|
594
|
-
: 'HIGH';
|
|
595
|
-
return [{
|
|
596
|
-
...intents[0],
|
|
597
|
-
category: 'ASK_WORKER',
|
|
598
|
-
request: prompt.trim(),
|
|
599
|
-
targetAgent: this.countMentionedAgents(prompt) > 0
|
|
600
|
-
? intents.find((intent) => intent.targetAgent)?.targetAgent
|
|
601
|
-
: undefined,
|
|
602
|
-
dependsOn: [],
|
|
603
|
-
requirements: mergedRequirements,
|
|
604
|
-
confidence: collapsedConfidence,
|
|
605
|
-
needsClarification: intents.some((intent) => intent.needsClarification),
|
|
606
|
-
clarificationQuestions: Array.from(new Set(intents.flatMap((intent) => intent.clarificationQuestions || []))),
|
|
607
|
-
reasoning: 'Collapsed multiple single-worker fragments into one deliverable for one worker.',
|
|
608
|
-
}];
|
|
609
|
-
}
|
|
610
|
-
hasExplicitOrderingLanguage(prompt) {
|
|
611
|
-
return /\b(before|after|then|first|last|next|once that is done|once done|if that succeeds|after that|follow the workflow|workflow|todo|handoff)\b/i.test(prompt);
|
|
612
|
-
}
|
|
613
|
-
buildIntentAnalysisFromRoutedIntent(routed) {
|
|
614
|
-
let primaryMode = 'DIRECT_CONVERSATION';
|
|
615
|
-
let userFacingMode = 'DIRECT_RESPONSE';
|
|
616
|
-
let targetScope = 'SELF';
|
|
617
|
-
let isMultiStep = false;
|
|
618
|
-
switch (routed.category) {
|
|
619
|
-
case 'ASK_WORKER':
|
|
620
|
-
primaryMode = 'PROXY_MODE';
|
|
621
|
-
userFacingMode = 'ASK_WORKER';
|
|
622
|
-
targetScope = 'ONE_WORKER';
|
|
623
|
-
break;
|
|
624
|
-
case 'STATUS_CHECK':
|
|
625
|
-
primaryMode = 'STATUS_INQUIRY';
|
|
626
|
-
userFacingMode = 'STATUS_CHECK';
|
|
627
|
-
break;
|
|
628
|
-
case 'POLICY_CONFIGURATION':
|
|
629
|
-
primaryMode = 'POLICY_UPDATE';
|
|
630
|
-
userFacingMode = 'POLICY_CONFIGURATION';
|
|
631
|
-
break;
|
|
632
|
-
case 'FULL_WORKFLOW':
|
|
633
|
-
primaryMode = 'WORKFLOW_MODE';
|
|
634
|
-
userFacingMode = 'FULL_WORKFLOW';
|
|
635
|
-
targetScope = 'MULTI_WORKER';
|
|
636
|
-
isMultiStep = true;
|
|
637
|
-
break;
|
|
638
|
-
case 'MEMORY_CAPTURE':
|
|
639
|
-
primaryMode = 'MEMORY_CAPTURE';
|
|
640
|
-
userFacingMode = 'DIRECT_RESPONSE';
|
|
641
|
-
break;
|
|
642
|
-
default:
|
|
643
|
-
primaryMode = 'DIRECT_CONVERSATION';
|
|
644
|
-
userFacingMode = 'DIRECT_RESPONSE';
|
|
645
|
-
break;
|
|
646
|
-
}
|
|
647
|
-
const intent = this.inferIntentFromRequest(routed.request, routed.category);
|
|
648
|
-
const targetAgent = routed.targetAgent;
|
|
649
|
-
return {
|
|
650
|
-
primaryMode,
|
|
651
|
-
secondaryModes: [],
|
|
652
|
-
executionOrder: [primaryMode],
|
|
653
|
-
userFacingMode,
|
|
654
|
-
targetScope,
|
|
655
|
-
confidence: routed.confidence,
|
|
656
|
-
intent,
|
|
657
|
-
projectMatch: undefined,
|
|
658
|
-
topicMatch: undefined,
|
|
659
|
-
targetAgent,
|
|
660
|
-
isMultiStep,
|
|
661
|
-
reasoning: routed.reasoning,
|
|
662
|
-
needsClarification: routed.category === 'CLARIFICATION_NEEDED' || routed.needsClarification,
|
|
663
|
-
clarificationQuestions: routed.clarificationQuestions,
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
inferIntentFromRequest(request, category) {
|
|
667
|
-
const normalized = request.toLowerCase();
|
|
668
|
-
if (category === 'POLICY_CONFIGURATION')
|
|
669
|
-
return 'plan';
|
|
670
|
-
if (category === 'STATUS_CHECK')
|
|
671
|
-
return 'question';
|
|
672
|
-
if (/\b(brainstorm|research|pressure-test|feasibility|is this a good idea|is this sound|evaluate this idea)\b/i.test(normalized)) {
|
|
673
|
-
return 'brainstorm';
|
|
674
|
-
}
|
|
675
|
-
if (/\b(review|qa|test|audit|verify)\b/i.test(normalized))
|
|
676
|
-
return 'review';
|
|
677
|
-
if (/\b(plan|outline|roadmap|approach)\b/i.test(normalized))
|
|
678
|
-
return 'plan';
|
|
679
|
-
if (/\b(build|code|implement|create|fix|write|update)\b/i.test(normalized))
|
|
680
|
-
return 'build';
|
|
681
|
-
if (/\b(what|how|why|when|where|show|list|find|check)\b/i.test(normalized))
|
|
682
|
-
return 'question';
|
|
683
|
-
return category === 'ASK_WORKER' ? 'discuss' : 'simple';
|
|
684
|
-
}
|
|
685
|
-
async handleOrderedIntentSequence(routedPlan, originalPrompt, conversationId, opts, promptAssignments, effectiveProjectId, effectiveProject, effectivePolicy, effectiveAssignments, projectOverview) {
|
|
686
|
-
const normalizedIntents = this.normalizeOrderedIntentSequence(originalPrompt, routedPlan.intents, effectiveAssignments);
|
|
687
|
-
this.reportHiddenRoleProgress(opts, 'intent_classifier', `Decomposed request into ${normalizedIntents.length} ordered intents`);
|
|
688
|
-
if (normalizedIntents.length > 1) {
|
|
689
|
-
opts.onProgress(this.formatOrderedIntentPlanPreview(normalizedIntents));
|
|
690
|
-
}
|
|
691
|
-
const persistedPlan = data.createIntentExecutionPlan({
|
|
692
|
-
conversationId,
|
|
693
|
-
sourcePrompt: originalPrompt,
|
|
694
|
-
reasoning: routedPlan.reasoning,
|
|
695
|
-
intents: normalizedIntents.map((intent, index) => ({
|
|
696
|
-
atomId: intent.id,
|
|
697
|
-
orderIndex: index + 1,
|
|
698
|
-
request: intent.request,
|
|
699
|
-
category: intent.category,
|
|
700
|
-
targetAgent: intent.targetAgent,
|
|
701
|
-
requirements: intent.requirements,
|
|
702
|
-
dependsOn: intent.dependsOn,
|
|
703
|
-
confidence: intent.confidence,
|
|
704
|
-
needsClarification: intent.needsClarification,
|
|
705
|
-
clarificationQuestions: intent.clarificationQuestions,
|
|
706
|
-
reasoning: intent.reasoning,
|
|
707
|
-
})),
|
|
708
|
-
});
|
|
709
|
-
data.updateIntentExecutionPlan(persistedPlan.id, { status: 'running' });
|
|
710
|
-
const lines = [];
|
|
711
|
-
let completedIntents = 0;
|
|
712
|
-
for (let index = 0; index < normalizedIntents.length; index += 1) {
|
|
713
|
-
const routed = normalizedIntents[index];
|
|
714
|
-
const persistedAtom = persistedPlan.atoms[index];
|
|
715
|
-
if (routed.category === 'CLARIFICATION_NEEDED' || routed.needsClarification) {
|
|
716
|
-
if (persistedAtom) {
|
|
717
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
718
|
-
status: 'blocked',
|
|
719
|
-
error: 'Clarification required before execution.',
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
data.updateIntentExecutionPlan(persistedPlan.id, {
|
|
723
|
-
status: 'blocked',
|
|
724
|
-
completedIntents,
|
|
725
|
-
});
|
|
726
|
-
const clarificationIntent = this.buildIntentAnalysisFromRoutedIntent(routed);
|
|
727
|
-
return this.formatClarificationResponse(clarificationIntent, projectOverview);
|
|
728
|
-
}
|
|
729
|
-
const intent = this.buildIntentAnalysisFromRoutedIntent(routed);
|
|
730
|
-
if (persistedAtom) {
|
|
731
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
732
|
-
status: 'running',
|
|
733
|
-
startedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
734
|
-
error: null,
|
|
735
|
-
});
|
|
736
|
-
}
|
|
737
|
-
const executionValidation = this.validateIntentExecution(routed.request, intent, effectivePolicy);
|
|
738
|
-
if (!executionValidation.ok) {
|
|
739
|
-
if (persistedAtom) {
|
|
740
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
741
|
-
status: 'failed',
|
|
742
|
-
error: executionValidation.reason,
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
data.updateIntentExecutionPlan(persistedPlan.id, {
|
|
746
|
-
status: 'failed',
|
|
747
|
-
completedIntents,
|
|
748
|
-
});
|
|
749
|
-
if (executionValidation.clarificationQuestions?.length) {
|
|
750
|
-
return this.formatClarificationResponse({ ...intent, clarificationQuestions: executionValidation.clarificationQuestions }, projectOverview);
|
|
751
|
-
}
|
|
752
|
-
return `I couldn't execute step ${index + 1} safely: ${executionValidation.reason}`;
|
|
753
|
-
}
|
|
754
|
-
const executionSpec = executionValidation.executionSpec;
|
|
755
|
-
const stepHeader = `**Step ${index + 1}:** ${routed.request}`;
|
|
756
|
-
if (this.hasMode(intent, 'POLICY_UPDATE')) {
|
|
757
|
-
const policyResponse = await this.handlePolicyUpdate(routed.request, conversationId, intent, this.parseRoleAssignments(routed.request), effectiveProjectId, projectOverview, effectivePolicy);
|
|
758
|
-
if (persistedAtom) {
|
|
759
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
760
|
-
status: 'completed',
|
|
761
|
-
resultSummary: this.summarizeResultForIntentPlan(policyResponse),
|
|
762
|
-
completedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
completedIntents += 1;
|
|
766
|
-
if (index < routedPlan.intents.length - 1) {
|
|
767
|
-
data.updateIntentExecutionPlan(persistedPlan.id, {
|
|
768
|
-
status: 'blocked',
|
|
769
|
-
completedIntents,
|
|
770
|
-
});
|
|
771
|
-
return `${policyResponse}\n\nI paused the remaining ordered steps until the policy change is confirmed.`;
|
|
772
|
-
}
|
|
773
|
-
data.updateIntentExecutionPlan(persistedPlan.id, {
|
|
774
|
-
status: 'completed',
|
|
775
|
-
completedIntents,
|
|
776
|
-
finishedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
777
|
-
});
|
|
778
|
-
return policyResponse;
|
|
779
|
-
}
|
|
780
|
-
if (this.hasMode(intent, 'STATUS_INQUIRY')) {
|
|
781
|
-
const statusResponse = this.formatStatusInquiryResponse(conversationId, effectiveProjectId);
|
|
782
|
-
if (persistedAtom) {
|
|
783
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
784
|
-
status: 'completed',
|
|
785
|
-
resultSummary: this.summarizeResultForIntentPlan(statusResponse),
|
|
786
|
-
completedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
787
|
-
});
|
|
788
|
-
}
|
|
789
|
-
completedIntents += 1;
|
|
790
|
-
lines.push(stepHeader);
|
|
791
|
-
lines.push(statusResponse);
|
|
792
|
-
continue;
|
|
793
|
-
}
|
|
794
|
-
if (this.hasMode(intent, 'MEMORY_CAPTURE')) {
|
|
795
|
-
const memoryResponse = this.formatMemoryCaptureResponse(routed.request);
|
|
796
|
-
if (persistedAtom) {
|
|
797
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
798
|
-
status: 'completed',
|
|
799
|
-
resultSummary: this.summarizeResultForIntentPlan(memoryResponse),
|
|
800
|
-
completedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
completedIntents += 1;
|
|
804
|
-
lines.push(stepHeader);
|
|
805
|
-
lines.push(memoryResponse);
|
|
806
|
-
continue;
|
|
807
|
-
}
|
|
808
|
-
if (executionSpec.executionMode === 'direct') {
|
|
809
|
-
const directResponse = this.handleDirectConversation(routed.request, projectOverview, effectiveAssignments);
|
|
810
|
-
if (persistedAtom) {
|
|
811
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
812
|
-
status: 'completed',
|
|
813
|
-
resultSummary: this.summarizeResultForIntentPlan(directResponse),
|
|
814
|
-
completedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
815
|
-
});
|
|
816
|
-
}
|
|
817
|
-
completedIntents += 1;
|
|
818
|
-
lines.push(stepHeader);
|
|
819
|
-
lines.push(directResponse);
|
|
820
|
-
continue;
|
|
821
|
-
}
|
|
822
|
-
if (executionSpec.executionMode === 'proxy') {
|
|
823
|
-
const proxyPrompt = this.buildSimpleWorkerPrompt(routed.request, executionSpec.targetAgents[0] || intent.targetAgent || 'the worker');
|
|
824
|
-
const proxyResult = await this.executeProxyRequest(proxyPrompt, conversationId, intent, opts, executionSpec, effectiveAssignments);
|
|
825
|
-
const proxyResponse = proxyResult.response;
|
|
826
|
-
if (!proxyResult.ok) {
|
|
827
|
-
if (persistedAtom) {
|
|
828
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
829
|
-
status: 'failed',
|
|
830
|
-
error: proxyResult.error || proxyResponse,
|
|
831
|
-
completedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
data.updateIntentExecutionPlan(persistedPlan.id, {
|
|
835
|
-
status: 'failed',
|
|
836
|
-
completedIntents,
|
|
837
|
-
finishedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
838
|
-
});
|
|
839
|
-
lines.push(stepHeader);
|
|
840
|
-
lines.push(proxyResponse);
|
|
841
|
-
return lines.join('\n\n');
|
|
842
|
-
}
|
|
843
|
-
if (persistedAtom) {
|
|
844
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
845
|
-
status: 'completed',
|
|
846
|
-
resultSummary: this.summarizeResultForIntentPlan(proxyResponse),
|
|
847
|
-
completedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
completedIntents += 1;
|
|
851
|
-
lines.push(stepHeader);
|
|
852
|
-
lines.push(proxyResponse);
|
|
853
|
-
continue;
|
|
854
|
-
}
|
|
855
|
-
if (executionSpec.executionMode === 'workflow') {
|
|
856
|
-
const workflowResponse = await this.handleOrchestratedRequest(routed.request, conversationId, intent, executionSpec, opts, effectiveAssignments, effectiveProject);
|
|
857
|
-
if (persistedAtom) {
|
|
858
|
-
data.updateIntentExecutionAtom(persistedAtom.id, {
|
|
859
|
-
status: 'completed',
|
|
860
|
-
resultSummary: this.summarizeResultForIntentPlan(workflowResponse),
|
|
861
|
-
completedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
completedIntents += 1;
|
|
865
|
-
lines.push(stepHeader);
|
|
866
|
-
lines.push(workflowResponse);
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
data.updateIntentExecutionPlan(persistedPlan.id, {
|
|
870
|
-
status: 'completed',
|
|
871
|
-
completedIntents,
|
|
872
|
-
finishedAt: new Date().toISOString().replace('T', ' ').replace('Z', ''),
|
|
873
|
-
});
|
|
874
|
-
return lines.join('\n\n');
|
|
875
|
-
}
|
|
876
|
-
normalizeOrderedIntentSequence(prompt, intents, assignments) {
|
|
877
|
-
const collapsed = this.collapseSingleWorkerPlan(prompt, intents);
|
|
878
|
-
const normalized = collapsed.map((intent) => this.applyImplicitWorkerTarget(intent, assignments));
|
|
879
|
-
return this.dropRedundantSequentialWorkerHandoffs(normalized);
|
|
880
|
-
}
|
|
881
|
-
applyImplicitWorkerTarget(intent, assignments) {
|
|
882
|
-
if (intent.category !== 'ASK_WORKER' || intent.targetAgent || intent.needsClarification) {
|
|
883
|
-
return intent;
|
|
884
|
-
}
|
|
885
|
-
const targetAgent = this.resolveImplicitWorkerTarget(intent.request, intent.requirements, assignments);
|
|
886
|
-
return targetAgent ? { ...intent, targetAgent } : intent;
|
|
887
|
-
}
|
|
888
|
-
resolveImplicitWorkerTarget(request, requirements, assignments) {
|
|
889
|
-
const text = `${request}\n${requirements.join('\n')}`.toLowerCase();
|
|
890
|
-
if (/\b(research|brainstorm|feasibility|evaluate|pressure-test|is this sound|idea)\b/i.test(text)) {
|
|
891
|
-
return assignments.research || assignments.coding || assignments.qa;
|
|
892
|
-
}
|
|
893
|
-
if (/\b(qa|review|verify|test|audit|check)\b/i.test(text)) {
|
|
894
|
-
return assignments.qa || assignments.coding || assignments.research;
|
|
895
|
-
}
|
|
896
|
-
if (/\b(build|code|implement|create|fix|write|update|design)\b/i.test(text)) {
|
|
897
|
-
return assignments.coding || assignments.qa || assignments.research;
|
|
898
|
-
}
|
|
899
|
-
return assignments.coding || assignments.research || assignments.qa;
|
|
900
|
-
}
|
|
901
|
-
dropRedundantSequentialWorkerHandoffs(intents) {
|
|
902
|
-
if (intents.length <= 1)
|
|
903
|
-
return intents;
|
|
904
|
-
const filtered = [];
|
|
905
|
-
for (let index = 0; index < intents.length; index += 1) {
|
|
906
|
-
const current = intents[index];
|
|
907
|
-
const next = intents[index + 1];
|
|
908
|
-
const currentText = current.request.toLowerCase();
|
|
909
|
-
const nextText = next?.request.toLowerCase() || '';
|
|
910
|
-
const redundantFeedbackStep = Boolean(next &&
|
|
911
|
-
current.category === 'ASK_WORKER' &&
|
|
912
|
-
next.category === 'ASK_WORKER' &&
|
|
913
|
-
current.targetAgent &&
|
|
914
|
-
next.targetAgent &&
|
|
915
|
-
current.targetAgent.toLowerCase() === next.targetAgent.toLowerCase() &&
|
|
916
|
-
/\b(give|send|provide|pass along)\b.*\b(feedback|qa)\b/.test(currentText) &&
|
|
917
|
-
/\b(fix|implement|apply|address)\b/.test(nextText));
|
|
918
|
-
if (!redundantFeedbackStep) {
|
|
919
|
-
filtered.push(current);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
return filtered;
|
|
923
|
-
}
|
|
924
|
-
formatOrderedIntentPlanPreview(intents) {
|
|
925
|
-
const lines = [`**Here's my plan (${intents.length} steps):**`];
|
|
926
|
-
intents.forEach((intent, index) => {
|
|
927
|
-
const agentLabel = intent.targetAgent ? ` (${intent.targetAgent})` : '';
|
|
928
|
-
lines.push(`${index + 1}. ${intent.request}${agentLabel}`);
|
|
929
|
-
});
|
|
930
|
-
return lines.join('\n');
|
|
931
|
-
}
|
|
932
591
|
shouldForceProxy(prompt, intent) {
|
|
933
592
|
if (intent.primaryMode === 'PROXY_MODE')
|
|
934
593
|
return true;
|
|
@@ -1055,82 +714,6 @@ class OrchestratorAgent {
|
|
|
1055
714
|
// Aliases like "claude" → "Ben" cause false matches on every prompt.
|
|
1056
715
|
return [agent.name.trim()].filter(Boolean);
|
|
1057
716
|
}
|
|
1058
|
-
/**
|
|
1059
|
-
* Interpret user intent using the clerk.
|
|
1060
|
-
* This is the structured hidden-role classifier for Phase 1.
|
|
1061
|
-
*/
|
|
1062
|
-
async classifyUserMessage(prompt, projectId, routingMode) {
|
|
1063
|
-
try {
|
|
1064
|
-
const agents = (0, orchestrator_profile_1.filterOutOrchestratorProfiles)(data.listAgentProfiles());
|
|
1065
|
-
const projects = data.listProjects();
|
|
1066
|
-
const topics = data.listTopics({ limit: 20 });
|
|
1067
|
-
const parsed = await this.clerk.classifyOrchestratorIntent({
|
|
1068
|
-
prompt,
|
|
1069
|
-
agents,
|
|
1070
|
-
projects,
|
|
1071
|
-
topics,
|
|
1072
|
-
projectId,
|
|
1073
|
-
routingMode,
|
|
1074
|
-
});
|
|
1075
|
-
if (parsed) {
|
|
1076
|
-
const primaryMode = this.normalizeMode(parsed.primaryMode);
|
|
1077
|
-
const secondaryModes = this.normalizeModeList(parsed.secondaryModes);
|
|
1078
|
-
const executionOrder = this.normalizeModeList(parsed.executionOrder);
|
|
1079
|
-
return {
|
|
1080
|
-
primaryMode,
|
|
1081
|
-
secondaryModes,
|
|
1082
|
-
executionOrder: executionOrder.length > 0
|
|
1083
|
-
? executionOrder
|
|
1084
|
-
: [primaryMode, ...secondaryModes.filter((mode) => mode !== primaryMode)],
|
|
1085
|
-
userFacingMode: parsed.userFacingMode,
|
|
1086
|
-
targetScope: parsed.targetScope,
|
|
1087
|
-
confidence: parsed.confidence,
|
|
1088
|
-
intent: parsed.intent || 'simple',
|
|
1089
|
-
projectMatch: undefined,
|
|
1090
|
-
topicMatch: undefined,
|
|
1091
|
-
targetAgent: parsed.targetAgent || undefined,
|
|
1092
|
-
isMultiStep: Boolean(parsed.isMultiStep),
|
|
1093
|
-
reasoning: parsed.reasoning || '',
|
|
1094
|
-
needsClarification: Boolean(parsed.needsClarification),
|
|
1095
|
-
clarificationQuestions: Array.isArray(parsed.clarificationQuestions) ? parsed.clarificationQuestions.map(String) : undefined,
|
|
1096
|
-
};
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
catch { /* fall through */ }
|
|
1100
|
-
return this.fallbackClassifyUserMessage(prompt, routingMode);
|
|
1101
|
-
// Default: treat as simple single-step
|
|
1102
|
-
return {
|
|
1103
|
-
primaryMode: 'DIRECT_CONVERSATION',
|
|
1104
|
-
secondaryModes: [],
|
|
1105
|
-
executionOrder: ['DIRECT_CONVERSATION'],
|
|
1106
|
-
userFacingMode: 'DIRECT_RESPONSE',
|
|
1107
|
-
targetScope: 'SELF',
|
|
1108
|
-
confidence: 'LOW',
|
|
1109
|
-
intent: 'simple',
|
|
1110
|
-
isMultiStep: false,
|
|
1111
|
-
reasoning: 'Intent analysis failed - defaulting to direct conversation',
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
normalizeMode(raw) {
|
|
1115
|
-
const value = String(raw || '').trim().toUpperCase();
|
|
1116
|
-
switch (value) {
|
|
1117
|
-
case 'POLICY_UPDATE':
|
|
1118
|
-
case 'MEMORY_CAPTURE':
|
|
1119
|
-
case 'STATUS_INQUIRY':
|
|
1120
|
-
case 'PROXY_MODE':
|
|
1121
|
-
case 'WORKFLOW_MODE':
|
|
1122
|
-
return value;
|
|
1123
|
-
default:
|
|
1124
|
-
return 'DIRECT_CONVERSATION';
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
normalizeModeList(raw) {
|
|
1128
|
-
if (!Array.isArray(raw))
|
|
1129
|
-
return [];
|
|
1130
|
-
return raw
|
|
1131
|
-
.map((item) => this.normalizeMode(item))
|
|
1132
|
-
.filter((mode, index, list) => list.indexOf(mode) === index);
|
|
1133
|
-
}
|
|
1134
717
|
fallbackClassifyUserMessage(prompt, routingMode) {
|
|
1135
718
|
const normalized = prompt.toLowerCase();
|
|
1136
719
|
const targetAgent = this.extractTargetAgentName(prompt);
|
|
@@ -1543,9 +1126,20 @@ class OrchestratorAgent {
|
|
|
1543
1126
|
const recentSummaryText = contextWindow.summary?.summary_text?.trim()
|
|
1544
1127
|
? `${contextWindow.carriedForward ? '(carried forward from the previous conversation in this topic)\n' : ''}${contextWindow.summary.summary_text.trim()}`
|
|
1545
1128
|
: '(none)';
|
|
1546
|
-
// Build specialists list
|
|
1129
|
+
// Build specialists list — include all bots except pure orchestrators.
|
|
1130
|
+
// A bot that is_orchestrator but also has a worker role_class (code, qa, research, etc.)
|
|
1131
|
+
// should still appear as an available worker.
|
|
1547
1132
|
const allProfiles = data.listAgentProfiles();
|
|
1548
|
-
const
|
|
1133
|
+
const PURE_ORCHESTRATOR_ROLES = new Set(['orchestrator', 'project manager']);
|
|
1134
|
+
const specialists = allProfiles
|
|
1135
|
+
.filter((agent) => {
|
|
1136
|
+
const rc = String(agent.role_class || '').trim().toLowerCase();
|
|
1137
|
+
if (agent.is_orchestrator === 1 && PURE_ORCHESTRATOR_ROLES.has(rc))
|
|
1138
|
+
return false;
|
|
1139
|
+
if (!agent.is_orchestrator && (0, orchestrator_profile_1.isOrchestratorProfile)(agent))
|
|
1140
|
+
return false;
|
|
1141
|
+
return true;
|
|
1142
|
+
})
|
|
1549
1143
|
.map((agent) => ({
|
|
1550
1144
|
name: agent.name,
|
|
1551
1145
|
roleLabel: agent.role_label || agent.role_class || 'general',
|
|
@@ -1586,12 +1180,20 @@ class OrchestratorAgent {
|
|
|
1586
1180
|
fastPath: false,
|
|
1587
1181
|
});
|
|
1588
1182
|
}
|
|
1589
|
-
const useSinglePassOrchestrator = this.isLocalDesktopRuntime();
|
|
1183
|
+
const useSinglePassOrchestrator = this.isLocalDesktopRuntime() && this.orchestratorRuntime.kind === 'bot';
|
|
1184
|
+
const localPromptContract = useSinglePassOrchestrator
|
|
1185
|
+
? (index_1.CLI_PROVIDERS.has(orchestratorBot.provider)
|
|
1186
|
+
&& conversationId
|
|
1187
|
+
&& !!(0, cli_session_epoch_1.selectCliSessionEpoch)(conversationId, orchestratorBot.id, orchestratorBot.provider).resumeSessionId
|
|
1188
|
+
? 'cli_recurring'
|
|
1189
|
+
: 'api_or_fresh_cli')
|
|
1190
|
+
: 'legacy';
|
|
1590
1191
|
const operatingPrompt = (0, orchestrator_operating_prompt_1.buildOrchestratorOperatingPrompt)({
|
|
1591
1192
|
orchestratorName: orchestratorBot.name,
|
|
1592
1193
|
primaryRole: orchestratorBot.role_label || orchestratorBot.role_class || null,
|
|
1593
1194
|
specialists,
|
|
1594
1195
|
workflowNames,
|
|
1196
|
+
localPromptContract,
|
|
1595
1197
|
entryMode: frontDoorSignals.explicitOrchestrationRequested ? 'orchestration_bias' : 'normal',
|
|
1596
1198
|
signalSummary: {
|
|
1597
1199
|
matchedBots: frontDoorSignals.matchedBots,
|
|
@@ -1622,7 +1224,6 @@ class OrchestratorAgent {
|
|
|
1622
1224
|
].join('\n');
|
|
1623
1225
|
let decision = null;
|
|
1624
1226
|
this.reportHiddenRoleProgress(opts, 'orchestrator', 'Understanding request');
|
|
1625
|
-
opts.onProgress('Understanding request...');
|
|
1626
1227
|
let understandingHeartbeat = setInterval(() => {
|
|
1627
1228
|
this.reportHiddenRoleProgress(opts, 'orchestrator', 'Still understanding the request');
|
|
1628
1229
|
}, 8_000);
|
|
@@ -1656,14 +1257,14 @@ class OrchestratorAgent {
|
|
|
1656
1257
|
}
|
|
1657
1258
|
}
|
|
1658
1259
|
else {
|
|
1659
|
-
const
|
|
1660
|
-
|
|
1661
|
-
|
|
1260
|
+
const rawDecision = await this.runNodeWithRetry('understand_request', state, async () => this.runOrchestratorPrompt(decisionPrompt, operatingPrompt, {
|
|
1261
|
+
conversationId: null,
|
|
1262
|
+
orchestratorBot,
|
|
1662
1263
|
projectId: projectId || null,
|
|
1663
1264
|
workspacePath: project?.folder?.trim() || undefined,
|
|
1664
|
-
|
|
1265
|
+
disableTools: true,
|
|
1665
1266
|
}));
|
|
1666
|
-
decision = (0, orchestrator_operating_prompt_1.parseOrchestratorFrontDoorDecision)(
|
|
1267
|
+
decision = (0, orchestrator_operating_prompt_1.parseOrchestratorFrontDoorDecision)(rawDecision);
|
|
1667
1268
|
}
|
|
1668
1269
|
}
|
|
1669
1270
|
catch {
|
|
@@ -1831,6 +1432,37 @@ class OrchestratorAgent {
|
|
|
1831
1432
|
return response;
|
|
1832
1433
|
}
|
|
1833
1434
|
if (decision.mode === 'execute_self') {
|
|
1435
|
+
if (this.orchestratorRuntime.kind === 'clerk') {
|
|
1436
|
+
const fallbackDelegateTarget = roleAssignments.coding
|
|
1437
|
+
|| data.getDefaultAgentProfile()?.name
|
|
1438
|
+
|| data.listAgentProfiles().find((agent) => !(0, orchestrator_profile_1.isOrchestratorProfile)(agent))?.name;
|
|
1439
|
+
if (fallbackDelegateTarget) {
|
|
1440
|
+
const delegateIntent = {
|
|
1441
|
+
primaryMode: 'PROXY_MODE',
|
|
1442
|
+
secondaryModes: [],
|
|
1443
|
+
executionOrder: ['PROXY_MODE'],
|
|
1444
|
+
userFacingMode: 'ASK_WORKER',
|
|
1445
|
+
targetScope: 'ONE_WORKER',
|
|
1446
|
+
confidence: 'MEDIUM',
|
|
1447
|
+
intent: normalizedDecision.taskType === 'qa'
|
|
1448
|
+
? 'review'
|
|
1449
|
+
: normalizedDecision.taskType === 'research'
|
|
1450
|
+
? 'brainstorm'
|
|
1451
|
+
: 'build',
|
|
1452
|
+
targetAgent: fallbackDelegateTarget,
|
|
1453
|
+
isMultiStep: false,
|
|
1454
|
+
reasoning: 'Clerk orchestrator cannot own implementation work directly, so the task falls back to the default worker.',
|
|
1455
|
+
};
|
|
1456
|
+
const delegatePrompt = prompt;
|
|
1457
|
+
const executionSpec = this.buildFrontDoorDelegateExecutionSpec(delegatePrompt, delegateIntent, fallbackDelegateTarget);
|
|
1458
|
+
const guardrailQuestions = this.buildDeterministicGuardrailQuestions(executionSpec, roleAssignments, delegateIntent);
|
|
1459
|
+
if (guardrailQuestions.length > 0) {
|
|
1460
|
+
return this.formatClarificationResponse({ ...delegateIntent, clarificationQuestions: guardrailQuestions }, overview);
|
|
1461
|
+
}
|
|
1462
|
+
this.applyExecutionSpecToState(state, executionSpec);
|
|
1463
|
+
return this.handleProxyRequest(delegatePrompt, conversationId, delegateIntent, opts, executionSpec, roleAssignments, state);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1834
1466
|
// No guardrails, no confirmation checkpoints, no validation blocking.
|
|
1835
1467
|
// Ben just does the work — like Claude CLI.
|
|
1836
1468
|
(0, state_1.setOrchestrationPath)(state, 'direct', 'direct');
|
|
@@ -2177,7 +1809,13 @@ class OrchestratorAgent {
|
|
|
2177
1809
|
});
|
|
2178
1810
|
let rawResponse = '';
|
|
2179
1811
|
try {
|
|
2180
|
-
rawResponse = await this.runNodeWithRetry('finalize_response', state, async () => this.
|
|
1812
|
+
rawResponse = await this.runNodeWithRetry('finalize_response', state, async () => this.runOrchestratorPrompt(blockedPrompt, `You are ${this.orchestratorRuntime.agentName}. Write the user-facing blocker message after work has already started. Be concise, outcome-first, and do not restate the full original request.`, {
|
|
1813
|
+
conversationId: task.conversation_id || state.conversationId || null,
|
|
1814
|
+
orchestratorBot,
|
|
1815
|
+
projectId: project?.id || opts.projectId || state.projectId || null,
|
|
1816
|
+
workspacePath: project?.folder?.trim() || undefined,
|
|
1817
|
+
disableTools: true,
|
|
1818
|
+
}));
|
|
2181
1819
|
}
|
|
2182
1820
|
catch (error) {
|
|
2183
1821
|
this.recordOrchestrationAudit(state, 'finalize_response', 'blocked', `Blocked final response generation failed: ${error?.message || error}`);
|
|
@@ -2208,7 +1846,13 @@ class OrchestratorAgent {
|
|
|
2208
1846
|
});
|
|
2209
1847
|
let response = '';
|
|
2210
1848
|
try {
|
|
2211
|
-
response = await this.runNodeWithRetry('finalize_response', state, async () => this.
|
|
1849
|
+
response = await this.runNodeWithRetry('finalize_response', state, async () => this.runOrchestratorPrompt(finalPrompt, `You are ${this.orchestratorRuntime.agentName}. 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.`, {
|
|
1850
|
+
conversationId,
|
|
1851
|
+
orchestratorBot,
|
|
1852
|
+
projectId: project?.id || opts.projectId || state.projectId || null,
|
|
1853
|
+
workspacePath: project?.folder?.trim() || undefined,
|
|
1854
|
+
disableTools: true,
|
|
1855
|
+
}));
|
|
2212
1856
|
}
|
|
2213
1857
|
catch (error) {
|
|
2214
1858
|
this.recordOrchestrationAudit(state, 'finalize_response', 'blocked', `Final response generation failed: ${error?.message || error}`);
|
|
@@ -2551,6 +2195,38 @@ class OrchestratorAgent {
|
|
|
2551
2195
|
return 'research';
|
|
2552
2196
|
return 'discuss';
|
|
2553
2197
|
}
|
|
2198
|
+
async runOrchestratorPrompt(userPrompt, systemPrompt, opts) {
|
|
2199
|
+
if (this.orchestratorRuntime.kind === 'bot' && opts.orchestratorBot) {
|
|
2200
|
+
const result = await this.workflowEngine.execute(userPrompt, opts.conversationId, opts.orchestratorBot.id, {
|
|
2201
|
+
disableDecomposition: true,
|
|
2202
|
+
disableTools: opts.disableTools === true,
|
|
2203
|
+
isOrchestrated: false,
|
|
2204
|
+
projectId: opts.projectId ?? null,
|
|
2205
|
+
workspacePath: opts.workspacePath,
|
|
2206
|
+
persistConversationMessages: false,
|
|
2207
|
+
workerMode: false,
|
|
2208
|
+
systemPromptOverride: systemPrompt,
|
|
2209
|
+
});
|
|
2210
|
+
this.setLastResponseMeta({
|
|
2211
|
+
agentName: opts.orchestratorBot.name,
|
|
2212
|
+
botId: opts.orchestratorBot.id,
|
|
2213
|
+
modelLabel: this.orchestratorRuntime.modelLabel,
|
|
2214
|
+
});
|
|
2215
|
+
return this.stripWorkerProtocol(result.mergedResult || '');
|
|
2216
|
+
}
|
|
2217
|
+
const response = await this.orchestratorRuntime.llm.chat({
|
|
2218
|
+
messages: [{ role: 'user', content: userPrompt }],
|
|
2219
|
+
system: systemPrompt,
|
|
2220
|
+
stream: false,
|
|
2221
|
+
runtimeMode: 'local_desktop',
|
|
2222
|
+
});
|
|
2223
|
+
this.setLastResponseMeta({
|
|
2224
|
+
agentName: this.orchestratorRuntime.agentName,
|
|
2225
|
+
botId: this.orchestratorRuntime.botId,
|
|
2226
|
+
modelLabel: this.orchestratorRuntime.modelLabel,
|
|
2227
|
+
});
|
|
2228
|
+
return String(response.content || '').trim();
|
|
2229
|
+
}
|
|
2554
2230
|
inferRequestedWorkflowRoles(prompt, roleAssignments) {
|
|
2555
2231
|
const normalized = String(prompt || '').toLowerCase();
|
|
2556
2232
|
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)
|
|
@@ -2879,31 +2555,6 @@ class OrchestratorAgent {
|
|
|
2879
2555
|
}
|
|
2880
2556
|
return currentProjectId || intentProjectId;
|
|
2881
2557
|
}
|
|
2882
|
-
/**
|
|
2883
|
-
* Handle simple requests — route to best agent, return response.
|
|
2884
|
-
*/
|
|
2885
|
-
async handleSimpleRequest(prompt, conversationId, intent, opts) {
|
|
2886
|
-
const agents = (0, orchestrator_profile_1.filterOutOrchestratorProfiles)(data.listAgentProfiles());
|
|
2887
|
-
if (agents.length === 0)
|
|
2888
|
-
return 'No worker agents configured. Please add an agent in Settings.';
|
|
2889
|
-
const route = await this.clerk.routeTask(prompt, agents);
|
|
2890
|
-
this.reportHiddenRoleProgress(opts, 'dispatch_controller', `Routing request to ${route.agentName}`);
|
|
2891
|
-
this.publishOrchestratorStatus(opts, {
|
|
2892
|
-
phase: 'delegating',
|
|
2893
|
-
steps: [{ index: 0, description: prompt.slice(0, 80), agentName: route.agentName, status: 'running' }],
|
|
2894
|
-
currentStep: 0,
|
|
2895
|
-
totalSteps: 1,
|
|
2896
|
-
});
|
|
2897
|
-
// Execute via workflow engine — disable decomposition for simple mode
|
|
2898
|
-
const result = await this.workflowEngine.execute(prompt, conversationId, route.agentId, {
|
|
2899
|
-
apiKey: this.resolveApiKey(route.provider),
|
|
2900
|
-
disableDecomposition: true,
|
|
2901
|
-
onWorkerChunk: opts.onWorkerChunk,
|
|
2902
|
-
});
|
|
2903
|
-
this.publishOrchestratorStatus(opts, { phase: 'complete', totalSteps: 1, currentStep: 1 });
|
|
2904
|
-
// Strip worker protocol before returning to user
|
|
2905
|
-
return this.stripWorkerProtocol(result.mergedResult);
|
|
2906
|
-
}
|
|
2907
2558
|
/**
|
|
2908
2559
|
* Handle proxy/discussion requests — forward to specific agent.
|
|
2909
2560
|
*/
|
|
@@ -3129,15 +2780,25 @@ class OrchestratorAgent {
|
|
|
3129
2780
|
if (state && workflowTemplate) {
|
|
3130
2781
|
state.preferredWorkflowUsed = true;
|
|
3131
2782
|
}
|
|
3132
|
-
|
|
3133
|
-
// workflow planning should be done by the orchestrator LLM, not the clerk.
|
|
3134
|
-
// The clerk should only do summaries, extraction, and compression.
|
|
2783
|
+
const inferredWorkflowRoles = this.inferRequestedWorkflowRoles(prompt, roleAssignments);
|
|
3135
2784
|
const plannedSteps = !workflowTemplate
|
|
3136
|
-
?
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
2785
|
+
? inferredWorkflowRoles.map((role, index) => {
|
|
2786
|
+
const ownerName = roleAssignments[role] || defaultAgent.name;
|
|
2787
|
+
return {
|
|
2788
|
+
description: role === 'research'
|
|
2789
|
+
? 'Research and pressure-test the request'
|
|
2790
|
+
: role === 'qa'
|
|
2791
|
+
? 'Review and QA the completed work'
|
|
2792
|
+
: 'Implement the requested work',
|
|
2793
|
+
agentName: ownerName,
|
|
2794
|
+
expectedOutput: role === 'qa'
|
|
2795
|
+
? 'PASS if the work meets the request, otherwise FAIL with actionable defects'
|
|
2796
|
+
: role === 'research'
|
|
2797
|
+
? 'Clear research findings and implementation guidance'
|
|
2798
|
+
: 'Completed implementation that satisfies the request',
|
|
2799
|
+
dependsOn: index > 0 ? [index - 1] : [],
|
|
2800
|
+
};
|
|
2801
|
+
})
|
|
3141
2802
|
: null;
|
|
3142
2803
|
let todoTaskId;
|
|
3143
2804
|
if (executionSpec.allowTodoCreation) {
|
|
@@ -3307,6 +2968,36 @@ class OrchestratorAgent {
|
|
|
3307
2968
|
}
|
|
3308
2969
|
return lines.join('\n');
|
|
3309
2970
|
}
|
|
2971
|
+
/**
|
|
2972
|
+
* Run completion validation on a synthesized response and append warnings
|
|
2973
|
+
* if the validation finds error-severity issues. Non-blocking — always
|
|
2974
|
+
* returns a response, possibly with a validation warning appended.
|
|
2975
|
+
*/
|
|
2976
|
+
applyCompletionValidation(synthesized, result, conversationId, executionMode) {
|
|
2977
|
+
try {
|
|
2978
|
+
const auditMessages = conversationId
|
|
2979
|
+
? data.getMessages(conversationId, { limit: 200 }).map((m) => ({
|
|
2980
|
+
role: m.role,
|
|
2981
|
+
content: m.content,
|
|
2982
|
+
tool_calls_json: m.tool_calls_json,
|
|
2983
|
+
}))
|
|
2984
|
+
: [];
|
|
2985
|
+
const validation = (0, validation_1.validateCompletion)(result, synthesized, auditMessages, executionMode);
|
|
2986
|
+
if (!validation.valid) {
|
|
2987
|
+
console.warn(`[orchestrator] Completion validation failed (confidence=${validation.confidence}):`, JSON.stringify(validation.issues));
|
|
2988
|
+
return synthesized + (0, validation_1.formatValidationWarning)(validation);
|
|
2989
|
+
}
|
|
2990
|
+
if (validation.issues.length > 0) {
|
|
2991
|
+
console.info(`[orchestrator] Completion validation passed with warnings (confidence=${validation.confidence}):`, JSON.stringify(validation.issues));
|
|
2992
|
+
}
|
|
2993
|
+
return synthesized;
|
|
2994
|
+
}
|
|
2995
|
+
catch (err) {
|
|
2996
|
+
// Validation must never block the response
|
|
2997
|
+
console.warn('[orchestrator] Completion validation error (non-blocking):', err);
|
|
2998
|
+
return synthesized;
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3310
3001
|
/**
|
|
3311
3002
|
* Publish orchestrator status via MQTT for UI rendering.
|
|
3312
3003
|
*/
|
|
@@ -3474,21 +3165,6 @@ class OrchestratorAgent {
|
|
|
3474
3165
|
}
|
|
3475
3166
|
return assignments;
|
|
3476
3167
|
}
|
|
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
|
-
}
|
|
3492
3168
|
hasRoleAssignments(assignments) {
|
|
3493
3169
|
return Boolean(assignments.coding || assignments.qa || assignments.research);
|
|
3494
3170
|
}
|
|
@@ -3670,128 +3346,14 @@ class OrchestratorAgent {
|
|
|
3670
3346
|
isPolicyConfirmation(prompt) {
|
|
3671
3347
|
return /\b(yes|yes save it|save it|save this policy|apply it|apply this|confirm|confirmed|do that|sounds right)\b/i.test(prompt.trim());
|
|
3672
3348
|
}
|
|
3673
|
-
determinePolicyScope(prompt, projectId) {
|
|
3674
|
-
if (!projectId)
|
|
3675
|
-
return 'user';
|
|
3676
|
-
if (/\b(for|in|on|within)\s+(this|the)\s+project\b/i.test(prompt))
|
|
3677
|
-
return 'project';
|
|
3678
|
-
const project = data.getProject(projectId);
|
|
3679
|
-
if (project && new RegExp(`\\b${project.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i').test(prompt)) {
|
|
3680
|
-
return 'project';
|
|
3681
|
-
}
|
|
3682
|
-
return 'user';
|
|
3683
|
-
}
|
|
3684
|
-
extractPolicyPatch(prompt, assignments, currentPolicy) {
|
|
3685
|
-
const patch = {};
|
|
3686
|
-
if (/\b(?:you|o|orchestrator|project manager)\s+never\s+code(?:s)?\b|\bnot code\b/i.test(prompt))
|
|
3687
|
-
patch.allowOrchestratorCode = false;
|
|
3688
|
-
if (/\b(?:you|o|orchestrator|project manager)\s+never\s+qa\b|\bnot qa\b/i.test(prompt))
|
|
3689
|
-
patch.allowOrchestratorQa = false;
|
|
3690
|
-
if (/\b(?:you|o|orchestrator|project manager)\s+never\s+deploy(?:s)?\b|\bnot deploy\b/i.test(prompt))
|
|
3691
|
-
patch.allowOrchestratorDeploy = false;
|
|
3692
|
-
if (/\b(?:you|o|orchestrator|project manager)\s+never\s+simulate(?:s)?\b|\bdo not simulate\b|\bnot simulate\b/i.test(prompt))
|
|
3693
|
-
patch.allowOrchestratorSimulation = false;
|
|
3694
|
-
if (/\bcasual\b/i.test(prompt) && /\bdirect\b/i.test(prompt))
|
|
3695
|
-
patch.tone = 'casual_direct';
|
|
3696
|
-
else if (/\bcasual\b/i.test(prompt))
|
|
3697
|
-
patch.tone = 'casual';
|
|
3698
|
-
else if (/\bdirect\b/i.test(prompt))
|
|
3699
|
-
patch.tone = 'direct';
|
|
3700
|
-
if (/\bstrict\b/i.test(prompt))
|
|
3701
|
-
patch.policyEnforcementLevel = 'strict';
|
|
3702
|
-
if (/\bno more than (\d+)\s+times\b/i.test(prompt)) {
|
|
3703
|
-
patch.coderRetryLimit = Number(prompt.match(/\bno more than (\d+)\s+times\b/i)?.[1] || currentPolicy.coderRetryLimit || 2);
|
|
3704
|
-
}
|
|
3705
|
-
if (/\b30\s*seconds\b/i.test(prompt))
|
|
3706
|
-
patch.statusReportingMode = 'event_plus_heartbeat';
|
|
3707
|
-
if (assignments.research)
|
|
3708
|
-
patch.defaultIdeaReviewer = assignments.research;
|
|
3709
|
-
if (assignments.coding)
|
|
3710
|
-
patch.defaultCoder = assignments.coding;
|
|
3711
|
-
if (assignments.qa)
|
|
3712
|
-
patch.defaultQa = assignments.qa;
|
|
3713
|
-
return patch;
|
|
3714
|
-
}
|
|
3715
|
-
sanitizeInterpretedPolicyPatch(prompt, patch) {
|
|
3716
|
-
const sanitized = {};
|
|
3717
|
-
const normalized = prompt.toLowerCase();
|
|
3718
|
-
const mentionsCodePolicy = /\b(?:you|o|orchestrator|project manager)\b[\s\S]{0,30}\b(code|codes|coding)\b/.test(normalized);
|
|
3719
|
-
const mentionsQaPolicy = /\b(?:you|o|orchestrator|project manager)\b[\s\S]{0,30}\b(qa|review|test|testing)\b/.test(normalized);
|
|
3720
|
-
const mentionsDeployPolicy = /\b(?:you|o|orchestrator|project manager)\b[\s\S]{0,30}\bdeploy(?:s|ing)?\b/.test(normalized);
|
|
3721
|
-
const mentionsSimulationPolicy = /\b(?:you|o|orchestrator|project manager)\b[\s\S]{0,30}\bsimulat(?:e|es|ing|ion)\b/.test(normalized);
|
|
3722
|
-
if (patch.allowOrchestratorCode !== undefined && mentionsCodePolicy)
|
|
3723
|
-
sanitized.allowOrchestratorCode = patch.allowOrchestratorCode;
|
|
3724
|
-
if (patch.allowOrchestratorQa !== undefined && mentionsQaPolicy)
|
|
3725
|
-
sanitized.allowOrchestratorQa = patch.allowOrchestratorQa;
|
|
3726
|
-
if (patch.allowOrchestratorDeploy !== undefined && mentionsDeployPolicy)
|
|
3727
|
-
sanitized.allowOrchestratorDeploy = patch.allowOrchestratorDeploy;
|
|
3728
|
-
if (patch.allowOrchestratorSimulation !== undefined && mentionsSimulationPolicy)
|
|
3729
|
-
sanitized.allowOrchestratorSimulation = patch.allowOrchestratorSimulation;
|
|
3730
|
-
if (patch.defaultIdeaReviewer)
|
|
3731
|
-
sanitized.defaultIdeaReviewer = patch.defaultIdeaReviewer;
|
|
3732
|
-
if (patch.defaultCoder)
|
|
3733
|
-
sanitized.defaultCoder = patch.defaultCoder;
|
|
3734
|
-
if (patch.defaultBackendCoder)
|
|
3735
|
-
sanitized.defaultBackendCoder = patch.defaultBackendCoder;
|
|
3736
|
-
if (patch.defaultQa)
|
|
3737
|
-
sanitized.defaultQa = patch.defaultQa;
|
|
3738
|
-
if (patch.coderRetryLimit !== undefined)
|
|
3739
|
-
sanitized.coderRetryLimit = patch.coderRetryLimit;
|
|
3740
|
-
if (patch.statusReportingMode)
|
|
3741
|
-
sanitized.statusReportingMode = patch.statusReportingMode;
|
|
3742
|
-
if (patch.tone)
|
|
3743
|
-
sanitized.tone = patch.tone;
|
|
3744
|
-
if (patch.policyEnforcementLevel)
|
|
3745
|
-
sanitized.policyEnforcementLevel = patch.policyEnforcementLevel;
|
|
3746
|
-
return sanitized;
|
|
3747
|
-
}
|
|
3748
|
-
summarizePolicyPatch(patch) {
|
|
3749
|
-
const lines = [];
|
|
3750
|
-
if (patch.allowOrchestratorCode === false)
|
|
3751
|
-
lines.push('O should not code');
|
|
3752
|
-
if (patch.allowOrchestratorQa === false)
|
|
3753
|
-
lines.push('O should not QA');
|
|
3754
|
-
if (patch.allowOrchestratorDeploy === false)
|
|
3755
|
-
lines.push('O should not deploy');
|
|
3756
|
-
if (patch.allowOrchestratorSimulation === false)
|
|
3757
|
-
lines.push('O should not simulate work');
|
|
3758
|
-
if (patch.defaultIdeaReviewer)
|
|
3759
|
-
lines.push(`${patch.defaultIdeaReviewer} should review ideas by default`);
|
|
3760
|
-
if (patch.defaultCoder)
|
|
3761
|
-
lines.push(`${patch.defaultCoder} should code by default`);
|
|
3762
|
-
if (patch.defaultBackendCoder)
|
|
3763
|
-
lines.push(`${patch.defaultBackendCoder} should handle backend coding by default`);
|
|
3764
|
-
if (patch.defaultQa)
|
|
3765
|
-
lines.push(`${patch.defaultQa} should QA by default`);
|
|
3766
|
-
if (patch.coderRetryLimit !== undefined)
|
|
3767
|
-
lines.push(`Coder retry limit should be ${patch.coderRetryLimit}`);
|
|
3768
|
-
if (patch.statusReportingMode)
|
|
3769
|
-
lines.push(`Status reporting mode should be ${patch.statusReportingMode}`);
|
|
3770
|
-
if (patch.tone)
|
|
3771
|
-
lines.push(`Tone should be ${patch.tone.replace(/_/g, ' ')}`);
|
|
3772
|
-
if (patch.policyEnforcementLevel)
|
|
3773
|
-
lines.push(`Enforcement should be ${patch.policyEnforcementLevel}`);
|
|
3774
|
-
return lines;
|
|
3775
|
-
}
|
|
3776
3349
|
async handlePolicyUpdate(prompt, conversationId, intent, assignments, projectId, overview, currentPolicy) {
|
|
3777
|
-
const interpreted = await this.clerk.interpretPolicyUpdate({
|
|
3778
|
-
prompt,
|
|
3779
|
-
currentPolicy,
|
|
3780
|
-
projectName: overview?.project.name,
|
|
3781
|
-
}).catch(() => null);
|
|
3782
3350
|
const agentNames = data.listAgentProfiles().map((agent) => agent.name);
|
|
3783
|
-
const
|
|
3784
|
-
const
|
|
3785
|
-
...(0, policy_detection_1.extractPolicyPatchFromPrompt)(prompt, currentPolicy, { agentNames }),
|
|
3786
|
-
...sanitizedInterpretedPatch,
|
|
3787
|
-
};
|
|
3788
|
-
const changes = interpreted?.summaryLines?.length
|
|
3789
|
-
? interpreted.summaryLines
|
|
3790
|
-
: (0, policy_detection_1.summarizePolicyPatch)(patch);
|
|
3351
|
+
const patch = (0, policy_detection_1.extractPolicyPatchFromPrompt)(prompt, currentPolicy, { agentNames });
|
|
3352
|
+
const changes = (0, policy_detection_1.summarizePolicyPatch)(patch);
|
|
3791
3353
|
if (changes.length === 0) {
|
|
3792
3354
|
return this.formatPolicyUpdateResponse(prompt, intent, assignments, overview);
|
|
3793
3355
|
}
|
|
3794
|
-
const scope =
|
|
3356
|
+
const scope = (0, policy_detection_1.determinePolicyScope)(prompt, overview?.project.name || undefined);
|
|
3795
3357
|
data.setPendingOrchestrationPolicy({
|
|
3796
3358
|
conversationId,
|
|
3797
3359
|
projectId: scope === 'project' ? projectId || null : null,
|