funolio-agent 0.17.8 → 1.0.3
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 +7 -6
- package/dist/approval.d.ts.map +1 -1
- package/dist/approval.js +40 -10
- package/dist/approval.js.map +1 -1
- package/dist/auth/anthropic-subscription.d.ts +9 -29
- package/dist/auth/anthropic-subscription.d.ts.map +1 -1
- package/dist/auth/anthropic-subscription.js +12 -133
- package/dist/auth/anthropic-subscription.js.map +1 -1
- package/dist/auth/auto-detect.d.ts +28 -6
- package/dist/auth/auto-detect.d.ts.map +1 -1
- package/dist/auth/auto-detect.js +52 -205
- package/dist/auth/auto-detect.js.map +1 -1
- package/dist/auth/credential-reader.d.ts +24 -4
- package/dist/auth/credential-reader.d.ts.map +1 -1
- package/dist/auth/credential-reader.js +31 -256
- package/dist/auth/credential-reader.js.map +1 -1
- package/dist/auth/credential-status.d.ts +7 -27
- package/dist/auth/credential-status.d.ts.map +1 -1
- package/dist/auth/credential-status.js +7 -95
- package/dist/auth/credential-status.js.map +1 -1
- package/dist/auth/index.d.ts +9 -2
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +10 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/subscription-runtime.d.ts +19 -23
- package/dist/auth/subscription-runtime.d.ts.map +1 -1
- package/dist/auth/subscription-runtime.js +24 -292
- package/dist/auth/subscription-runtime.js.map +1 -1
- package/dist/auth/token-refresh.d.ts +19 -28
- package/dist/auth/token-refresh.d.ts.map +1 -1
- package/dist/auth/token-refresh.js +26 -464
- package/dist/auth/token-refresh.js.map +1 -1
- package/dist/backfill.js +2 -2
- package/dist/backfill.js.map +1 -1
- package/dist/bot-manager.d.ts +5 -6
- package/dist/bot-manager.d.ts.map +1 -1
- package/dist/bot-manager.js +20 -62
- package/dist/bot-manager.js.map +1 -1
- package/dist/clerk-model.d.ts +0 -1
- package/dist/clerk-model.d.ts.map +1 -1
- package/dist/clerk-model.js +24 -50
- package/dist/clerk-model.js.map +1 -1
- package/dist/commands/configure-provider.js +2 -2
- package/dist/commands/configure-provider.js.map +1 -1
- package/dist/commands/configure.d.ts.map +1 -1
- package/dist/commands/configure.js +10 -14
- package/dist/commands/configure.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +51 -229
- package/dist/commands/start.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts +6 -1
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +87 -18
- package/dist/context-compressor.js.map +1 -1
- package/dist/context-window.d.ts +2 -8
- package/dist/context-window.d.ts.map +1 -1
- package/dist/context-window.js +4 -4
- package/dist/context-window.js.map +1 -1
- package/dist/eval/orchestrator-front-door-replay.js +43 -2
- package/dist/eval/orchestrator-front-door-replay.js.map +1 -1
- package/dist/eval/orchestrator-todo-dispatch-replay.d.ts +2 -0
- package/dist/eval/orchestrator-todo-dispatch-replay.d.ts.map +1 -0
- package/dist/eval/orchestrator-todo-dispatch-replay.js +253 -0
- package/dist/eval/orchestrator-todo-dispatch-replay.js.map +1 -0
- package/dist/eval/orchestrator-todo-planning-replay.d.ts +2 -0
- package/dist/eval/orchestrator-todo-planning-replay.d.ts.map +1 -0
- package/dist/eval/orchestrator-todo-planning-replay.js +247 -0
- package/dist/eval/orchestrator-todo-planning-replay.js.map +1 -0
- package/dist/eval/policy-detection-replay.d.ts +2 -0
- package/dist/eval/policy-detection-replay.d.ts.map +1 -0
- package/dist/eval/policy-detection-replay.js +122 -0
- package/dist/eval/policy-detection-replay.js.map +1 -0
- package/dist/eval/todo-worker-runtime-replay.d.ts +2 -0
- package/dist/eval/todo-worker-runtime-replay.d.ts.map +1 -0
- package/dist/eval/todo-worker-runtime-replay.js +520 -0
- package/dist/eval/todo-worker-runtime-replay.js.map +1 -0
- package/dist/integration-tokens.d.ts +6 -0
- package/dist/integration-tokens.d.ts.map +1 -1
- package/dist/integration-tokens.js +43 -0
- package/dist/integration-tokens.js.map +1 -1
- package/dist/local-data.d.ts +134 -1
- package/dist/local-data.d.ts.map +1 -1
- package/dist/local-data.js +711 -18
- package/dist/local-data.js.map +1 -1
- package/dist/local-db.d.ts.map +1 -1
- package/dist/local-db.js +216 -12
- package/dist/local-db.js.map +1 -1
- package/dist/local-funnel.d.ts.map +1 -1
- package/dist/local-funnel.js +7 -0
- package/dist/local-funnel.js.map +1 -1
- package/dist/local-server.d.ts.map +1 -1
- package/dist/local-server.js +119 -96
- package/dist/local-server.js.map +1 -1
- package/dist/mcp/bridge-server.d.ts.map +1 -1
- package/dist/mcp/bridge-server.js +8 -2
- package/dist/mcp/bridge-server.js.map +1 -1
- package/dist/mcp/manager.d.ts +5 -0
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +131 -2
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/sync-cli-config.d.ts +5 -0
- package/dist/mcp/sync-cli-config.d.ts.map +1 -1
- package/dist/mcp/sync-cli-config.js +10 -2
- package/dist/mcp/sync-cli-config.js.map +1 -1
- package/dist/message-loop.d.ts +1 -10
- package/dist/message-loop.d.ts.map +1 -1
- package/dist/message-loop.js +179 -250
- package/dist/message-loop.js.map +1 -1
- package/dist/mqtt-client.d.ts +44 -0
- 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/orchestration/front-door-policy.d.ts +26 -9
- package/dist/orchestration/front-door-policy.d.ts.map +1 -1
- package/dist/orchestration/front-door-policy.js +242 -69
- package/dist/orchestration/front-door-policy.js.map +1 -1
- package/dist/orchestration/orchestrator-blocked-prompt.d.ts +18 -0
- package/dist/orchestration/orchestrator-blocked-prompt.d.ts.map +1 -0
- package/dist/orchestration/orchestrator-blocked-prompt.js +46 -0
- package/dist/orchestration/orchestrator-blocked-prompt.js.map +1 -0
- package/dist/orchestration/orchestrator-final-response-prompt.d.ts +10 -0
- package/dist/orchestration/orchestrator-final-response-prompt.d.ts.map +1 -0
- package/dist/orchestration/orchestrator-final-response-prompt.js +39 -0
- package/dist/orchestration/orchestrator-final-response-prompt.js.map +1 -0
- 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 +106 -36
- package/dist/orchestration/orchestrator-operating-prompt.js.map +1 -1
- package/dist/orchestration/policy-prompt.d.ts +6 -0
- package/dist/orchestration/policy-prompt.d.ts.map +1 -0
- package/dist/orchestration/policy-prompt.js +40 -0
- package/dist/orchestration/policy-prompt.js.map +1 -0
- package/dist/orchestration/worker-operating-prompt.d.ts +16 -0
- package/dist/orchestration/worker-operating-prompt.d.ts.map +1 -0
- package/dist/orchestration/worker-operating-prompt.js +75 -0
- package/dist/orchestration/worker-operating-prompt.js.map +1 -0
- package/dist/orchestrator.d.ts +19 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +614 -54
- package/dist/orchestrator.js.map +1 -1
- package/dist/policy-detection.d.ts +40 -0
- package/dist/policy-detection.d.ts.map +1 -0
- package/dist/policy-detection.js +298 -0
- package/dist/policy-detection.js.map +1 -0
- package/dist/providers/anthropic.d.ts +0 -5
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +34 -51
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/claude-cli.d.ts.map +1 -1
- package/dist/providers/claude-cli.js +11 -2
- 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 +121 -2
- package/dist/providers/codex-cli.js.map +1 -1
- package/dist/providers/index.d.ts +6 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/summarization-pipeline.d.ts +1 -4
- package/dist/summarization-pipeline.d.ts.map +1 -1
- package/dist/summarization-pipeline.js +43 -56
- package/dist/summarization-pipeline.js.map +1 -1
- package/dist/tools/analyze-image.js +2 -2
- package/dist/tools/analyze-image.js.map +1 -1
- package/dist/tools/edit-file.d.ts.map +1 -1
- package/dist/tools/edit-file.js +16 -1
- package/dist/tools/edit-file.js.map +1 -1
- package/dist/tools/git-tools.d.ts.map +1 -1
- package/dist/tools/git-tools.js +32 -1
- package/dist/tools/git-tools.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/list-directory.d.ts.map +1 -1
- package/dist/tools/list-directory.js +23 -0
- package/dist/tools/list-directory.js.map +1 -1
- package/dist/tools/notify-user.d.ts.map +1 -1
- package/dist/tools/notify-user.js +15 -1
- package/dist/tools/notify-user.js.map +1 -1
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +3 -1
- package/dist/tools/read-file.js.map +1 -1
- package/dist/tools/request-mcp-install.js +5 -5
- package/dist/tools/request-mcp-install.js.map +1 -1
- package/dist/tools/run-command.d.ts.map +1 -1
- package/dist/tools/run-command.js +16 -4
- package/dist/tools/run-command.js.map +1 -1
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +6 -0
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/schedule-task.d.ts.map +1 -1
- package/dist/tools/schedule-task.js +37 -0
- package/dist/tools/schedule-task.js.map +1 -1
- package/dist/tools/search-codebase.d.ts.map +1 -1
- package/dist/tools/search-codebase.js +251 -32
- package/dist/tools/search-codebase.js.map +1 -1
- package/dist/tools/todo-tasks.d.ts +2 -0
- package/dist/tools/todo-tasks.d.ts.map +1 -1
- package/dist/tools/todo-tasks.js +203 -6
- package/dist/tools/todo-tasks.js.map +1 -1
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +21 -2
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +38 -34
- package/dist/tools/web-search.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/wizard-state.d.ts.map +1 -1
- package/dist/wizard-state.js +30 -8
- 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 +80 -93
- package/dist/wizard-support.js.map +1 -1
- package/dist/workflow-engine.d.ts +3 -0
- package/dist/workflow-engine.d.ts.map +1 -1
- package/dist/workflow-engine.js +111 -82
- package/dist/workflow-engine.js.map +1 -1
- package/package.json +6 -1
package/dist/message-loop.js
CHANGED
|
@@ -55,8 +55,6 @@ const response_guard_1 = require("./response-guard");
|
|
|
55
55
|
const context_compressor_1 = require("./context-compressor");
|
|
56
56
|
const crypto = __importStar(require("crypto"));
|
|
57
57
|
const data = __importStar(require("./local-data"));
|
|
58
|
-
const agent_config_1 = require("./agent-config");
|
|
59
|
-
const subscription_runtime_1 = require("./auth/subscription-runtime");
|
|
60
58
|
const prompt_template_1 = require("./prompt-template");
|
|
61
59
|
/** Determine priority from an AgentCommand */
|
|
62
60
|
function getCommandPriority(command) {
|
|
@@ -72,6 +70,47 @@ function getCommandPriority(command) {
|
|
|
72
70
|
return 'medium';
|
|
73
71
|
}
|
|
74
72
|
const PRIORITY_ORDER = { high: 0, medium: 1, low: 2 };
|
|
73
|
+
function classifyToolName(toolName) {
|
|
74
|
+
switch (toolName) {
|
|
75
|
+
case 'read_file':
|
|
76
|
+
return 'file_read';
|
|
77
|
+
case 'list_directory':
|
|
78
|
+
return 'directory_scan';
|
|
79
|
+
case 'write_file':
|
|
80
|
+
case 'edit_file':
|
|
81
|
+
return 'file_write';
|
|
82
|
+
case 'run_command':
|
|
83
|
+
case 'git_status':
|
|
84
|
+
case 'git_diff':
|
|
85
|
+
case 'git_commit':
|
|
86
|
+
return 'command_run';
|
|
87
|
+
case 'web_fetch':
|
|
88
|
+
return 'web_fetch';
|
|
89
|
+
default:
|
|
90
|
+
return 'workspace_hint';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function extractToolLocation(toolName, args, projectDir) {
|
|
94
|
+
const path = typeof args.path === 'string'
|
|
95
|
+
? args.path
|
|
96
|
+
: typeof args.filePath === 'string'
|
|
97
|
+
? args.filePath
|
|
98
|
+
: typeof args.directory === 'string'
|
|
99
|
+
? args.directory
|
|
100
|
+
: undefined;
|
|
101
|
+
const cwd = typeof args.cwd === 'string'
|
|
102
|
+
? args.cwd
|
|
103
|
+
: toolName === 'run_command' || toolName.startsWith('git_')
|
|
104
|
+
? projectDir
|
|
105
|
+
: undefined;
|
|
106
|
+
const url = typeof args.url === 'string' ? args.url : undefined;
|
|
107
|
+
return { path, cwd, url };
|
|
108
|
+
}
|
|
109
|
+
function summarizeToolResult(output, isError) {
|
|
110
|
+
const prefix = isError ? 'Tool failed: ' : 'Tool completed: ';
|
|
111
|
+
const compact = output.replace(/\s+/g, ' ').trim();
|
|
112
|
+
return `${prefix}${compact}`.slice(0, 240);
|
|
113
|
+
}
|
|
75
114
|
class MessageLoop {
|
|
76
115
|
options;
|
|
77
116
|
llmProvider;
|
|
@@ -97,23 +136,16 @@ class MessageLoop {
|
|
|
97
136
|
static SCHEDULED_TASK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
98
137
|
scheduledTaskTimer = null;
|
|
99
138
|
static IDLE_THRESHOLD_MS = 15 * 60 * 1000; // 15 minutes
|
|
100
|
-
static SERVER_SYNC_RETRY_MAX = 1;
|
|
101
139
|
constructor(options) {
|
|
102
140
|
this.options = options;
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
const effectiveKey = options.
|
|
141
|
+
// Use API key directly — subscription OAuth has been removed.
|
|
142
|
+
// CLI providers (claude-cli, codex-cli) don't use this path at all.
|
|
143
|
+
const effectiveKey = options.apiKey || '';
|
|
106
144
|
const effectiveProvider = options.provider;
|
|
107
|
-
const isOAuthToken = effectiveKey.startsWith('sk-ant-oat01-');
|
|
108
145
|
this.llmProvider = (0, index_1.createProvider)(effectiveProvider, {
|
|
109
146
|
apiKey: effectiveKey,
|
|
110
147
|
model: options.model,
|
|
111
|
-
...(isOAuthToken ? { authMode: 'oauth-bearer' } : {}),
|
|
112
148
|
});
|
|
113
|
-
// Async: attempt to resolve Anthropic subscription auth (create_api_key exchange)
|
|
114
|
-
if (isOAuthToken && effectiveProvider === 'anthropic') {
|
|
115
|
-
this.resolveAndUpgradeAuth(effectiveKey, options.model);
|
|
116
|
-
}
|
|
117
149
|
// Resolve Funolio API credentials for bot status reporting
|
|
118
150
|
const cfg = (0, config_1.loadConfig)();
|
|
119
151
|
const funoliApiKey = cfg.auth?.token || process.env.FUNOLIO_API_KEY || '';
|
|
@@ -124,7 +156,7 @@ class MessageLoop {
|
|
|
124
156
|
llmProvider: effectiveProvider,
|
|
125
157
|
llmModel: options.model,
|
|
126
158
|
llmApiKey: effectiveKey,
|
|
127
|
-
llmAuthMode:
|
|
159
|
+
llmAuthMode: 'api-key',
|
|
128
160
|
apiKey: funoliApiKey,
|
|
129
161
|
apiBaseUrl: funoliApiBaseUrl,
|
|
130
162
|
botName: options.agentName || 'funolio-agent',
|
|
@@ -161,147 +193,6 @@ class MessageLoop {
|
|
|
161
193
|
// Session idle detection timer (checks every 60s)
|
|
162
194
|
this.idleTimer = setInterval(() => this.checkIdleSession(), 60_000);
|
|
163
195
|
}
|
|
164
|
-
/**
|
|
165
|
-
* Attempt to resolve Anthropic subscription auth via create_api_key exchange.
|
|
166
|
-
* If successful, recreates the LLM provider with the exchanged API key (which
|
|
167
|
-
* works with standard x-api-key auth and unlocks all models on the subscription).
|
|
168
|
-
* Falls back to bearer mode silently if exchange fails.
|
|
169
|
-
*/
|
|
170
|
-
async resolveAndUpgradeAuth(oauthToken, model) {
|
|
171
|
-
try {
|
|
172
|
-
const runtime = await (0, subscription_runtime_1.resolveClaudeSubscriptionRuntime)({
|
|
173
|
-
preferredModel: model,
|
|
174
|
-
inputCredential: {
|
|
175
|
-
provider: 'anthropic',
|
|
176
|
-
accessToken: oauthToken,
|
|
177
|
-
refreshToken: this.options.resolvedAuth?.credential?.refreshToken || '',
|
|
178
|
-
expiresAt: this.options.resolvedAuth?.credential?.expiresAt || 0,
|
|
179
|
-
},
|
|
180
|
-
inputSource: 'request:anthropic',
|
|
181
|
-
persistInputCredential: true,
|
|
182
|
-
});
|
|
183
|
-
if (!runtime)
|
|
184
|
-
return;
|
|
185
|
-
if (this.options.resolvedAuth?.credential && runtime.authMode === 'oauth-bearer') {
|
|
186
|
-
this.options.resolvedAuth.credential.accessToken = runtime.apiKey;
|
|
187
|
-
}
|
|
188
|
-
if (this.resolvedAuth?.credential && runtime.authMode === 'oauth-bearer') {
|
|
189
|
-
this.resolvedAuth.credential.accessToken = runtime.apiKey;
|
|
190
|
-
this.resolvedAuth.apiKey = runtime.apiKey;
|
|
191
|
-
}
|
|
192
|
-
if (runtime.authMode !== 'oauth-bearer') {
|
|
193
|
-
// Successfully exchanged OAuth token for a temporary API key
|
|
194
|
-
console.log('[message-loop] Anthropic subscription auth: using exchanged API key');
|
|
195
|
-
this.llmProvider = (0, index_1.createProvider)('anthropic', {
|
|
196
|
-
apiKey: runtime.apiKey,
|
|
197
|
-
model,
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
// Bearer fallback — recreate provider with the (possibly refreshed) token
|
|
202
|
-
const currentToken = runtime.apiKey || this.options.resolvedAuth?.credential?.accessToken || oauthToken;
|
|
203
|
-
console.log('[message-loop] Anthropic subscription auth: using bearer fallback' +
|
|
204
|
-
(currentToken !== oauthToken ? ' (with refreshed token)' : ''));
|
|
205
|
-
this.llmProvider = (0, index_1.createProvider)('anthropic', {
|
|
206
|
-
apiKey: currentToken,
|
|
207
|
-
model,
|
|
208
|
-
authMode: 'oauth-bearer',
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
catch (err) {
|
|
213
|
-
console.warn('[message-loop] Anthropic subscription auth resolution failed, keeping bearer mode:', err?.message || err);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
async syncOAuthFromServer(reason) {
|
|
217
|
-
const providerId = this.options.provider;
|
|
218
|
-
if (!providerId || providerId.endsWith('-cli'))
|
|
219
|
-
return false;
|
|
220
|
-
const cfg = (0, config_1.loadConfig)();
|
|
221
|
-
const authToken = cfg.auth?.token;
|
|
222
|
-
if (!authToken)
|
|
223
|
-
return false;
|
|
224
|
-
try {
|
|
225
|
-
const res = await fetch(`${config_1.FUNOLIO_API_URL}/api/v1/agent/config`, {
|
|
226
|
-
headers: { Authorization: `Bearer ${authToken}` },
|
|
227
|
-
});
|
|
228
|
-
if (!res.ok)
|
|
229
|
-
return false;
|
|
230
|
-
const body = await res.json();
|
|
231
|
-
const provider = (body.providers || []).find((p) => p.id === providerId && p.connectionType === 'oauth');
|
|
232
|
-
if (!provider?.access_token)
|
|
233
|
-
return false;
|
|
234
|
-
const currentAccess = this.resolvedAuth?.credential?.accessToken || this.options.oauthToken || '';
|
|
235
|
-
const currentRefresh = this.resolvedAuth?.credential?.refreshToken || this.options.resolvedAuth?.credential?.refreshToken || '';
|
|
236
|
-
const currentExpiry = this.resolvedAuth?.credential?.expiresAt || this.options.resolvedAuth?.credential?.expiresAt || 0;
|
|
237
|
-
const nextExpiry = provider.expires_at || 0;
|
|
238
|
-
const changed = provider.access_token !== currentAccess
|
|
239
|
-
|| (provider.refresh_token || '') !== currentRefresh
|
|
240
|
-
|| nextExpiry > currentExpiry;
|
|
241
|
-
if (!changed)
|
|
242
|
-
return false;
|
|
243
|
-
if (this.resolvedAuth) {
|
|
244
|
-
this.resolvedAuth.apiKey = provider.access_token;
|
|
245
|
-
this.resolvedAuth.expired = false;
|
|
246
|
-
this.resolvedAuth.error = undefined;
|
|
247
|
-
if (this.resolvedAuth.credential) {
|
|
248
|
-
this.resolvedAuth.credential.accessToken = provider.access_token;
|
|
249
|
-
if (provider.refresh_token)
|
|
250
|
-
this.resolvedAuth.credential.refreshToken = provider.refresh_token;
|
|
251
|
-
if (provider.expires_at)
|
|
252
|
-
this.resolvedAuth.credential.expiresAt = provider.expires_at;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
if (this.options.resolvedAuth) {
|
|
256
|
-
this.options.resolvedAuth.apiKey = provider.access_token;
|
|
257
|
-
this.options.resolvedAuth.expired = false;
|
|
258
|
-
this.options.resolvedAuth.error = undefined;
|
|
259
|
-
if (this.options.resolvedAuth.credential) {
|
|
260
|
-
this.options.resolvedAuth.credential.accessToken = provider.access_token;
|
|
261
|
-
if (provider.refresh_token)
|
|
262
|
-
this.options.resolvedAuth.credential.refreshToken = provider.refresh_token;
|
|
263
|
-
if (provider.expires_at)
|
|
264
|
-
this.options.resolvedAuth.credential.expiresAt = provider.expires_at;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
this.options.oauthToken = provider.access_token;
|
|
268
|
-
const local = (0, config_1.loadConfig)();
|
|
269
|
-
if (!local.providers)
|
|
270
|
-
local.providers = [];
|
|
271
|
-
const idx = local.providers.findIndex((p) => p.id === providerId);
|
|
272
|
-
if (idx >= 0) {
|
|
273
|
-
local.providers[idx].authType = 'oauth';
|
|
274
|
-
local.providers[idx].oauthToken = provider.access_token;
|
|
275
|
-
if (provider.refresh_token)
|
|
276
|
-
local.providers[idx].oauthRefreshToken = provider.refresh_token;
|
|
277
|
-
if (provider.expires_at)
|
|
278
|
-
local.providers[idx].oauthExpiresAt = provider.expires_at;
|
|
279
|
-
if (provider.defaultModel)
|
|
280
|
-
local.providers[idx].defaultModel = provider.defaultModel;
|
|
281
|
-
}
|
|
282
|
-
(0, config_1.saveConfig)(local);
|
|
283
|
-
if (this.options.agentName) {
|
|
284
|
-
const agentLocal = (0, agent_config_1.loadAgentConfig)(this.options.agentName);
|
|
285
|
-
if (agentLocal && agentLocal.provider === providerId) {
|
|
286
|
-
agentLocal.oauthToken = provider.access_token;
|
|
287
|
-
if (provider.refresh_token)
|
|
288
|
-
agentLocal.oauthRefreshToken = provider.refresh_token;
|
|
289
|
-
if (provider.expires_at)
|
|
290
|
-
agentLocal.oauthExpiresAt = provider.expires_at;
|
|
291
|
-
if (provider.defaultModel)
|
|
292
|
-
agentLocal.model = provider.defaultModel;
|
|
293
|
-
(0, agent_config_1.saveAgentConfig)(this.options.agentName, agentLocal);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
this.updateToken(provider.access_token);
|
|
297
|
-
console.log(chalk_1.default.green(` [auth] Synced updated ${providerId} OAuth credentials from server (${reason})`));
|
|
298
|
-
return true;
|
|
299
|
-
}
|
|
300
|
-
catch (err) {
|
|
301
|
-
console.log(chalk_1.default.gray(` [auth] Server OAuth sync skipped (${reason}): ${err?.message || err}`));
|
|
302
|
-
return false;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
196
|
/** Check if a session has gone idle and trigger auto-organize */
|
|
306
197
|
async checkIdleSession() {
|
|
307
198
|
if (!this.lastMessageAt || !(0, auto_organizer_1.isAutoOrganizeEnabled)())
|
|
@@ -419,48 +310,13 @@ class MessageLoop {
|
|
|
419
310
|
const overrideModel = command.model?.model;
|
|
420
311
|
let provider = overrideProvider || this.options.provider;
|
|
421
312
|
const model = overrideModel || this.options.model;
|
|
422
|
-
const apiKey = this.options.
|
|
313
|
+
const apiKey = this.options.apiKey;
|
|
423
314
|
// Only create a new provider if provider or model actually changed (ignore apiKey overrides from server)
|
|
424
315
|
const hasOverride = (overrideProvider && overrideProvider !== this.options.provider) ||
|
|
425
316
|
(overrideModel && overrideModel !== this.options.model);
|
|
426
317
|
let llm;
|
|
427
318
|
if (hasOverride) {
|
|
428
|
-
|
|
429
|
-
if ((apiKey || '').startsWith('sk-ant-oat01-') && provider === 'anthropic') {
|
|
430
|
-
try {
|
|
431
|
-
const runtime = await (0, subscription_runtime_1.resolveClaudeSubscriptionRuntime)({
|
|
432
|
-
preferredModel: model,
|
|
433
|
-
inputCredential: {
|
|
434
|
-
provider: 'anthropic',
|
|
435
|
-
accessToken: apiKey,
|
|
436
|
-
refreshToken: this.options.resolvedAuth?.credential?.refreshToken || '',
|
|
437
|
-
expiresAt: this.options.resolvedAuth?.credential?.expiresAt || 0,
|
|
438
|
-
},
|
|
439
|
-
inputSource: 'request:anthropic',
|
|
440
|
-
persistInputCredential: true,
|
|
441
|
-
});
|
|
442
|
-
const resolvedKey = runtime?.apiKey || apiKey;
|
|
443
|
-
const authMode = runtime?.authMode;
|
|
444
|
-
if (this.options.resolvedAuth?.credential && authMode === 'oauth-bearer') {
|
|
445
|
-
this.options.resolvedAuth.credential.accessToken = resolvedKey;
|
|
446
|
-
}
|
|
447
|
-
if (this.resolvedAuth?.credential && authMode === 'oauth-bearer') {
|
|
448
|
-
this.resolvedAuth.credential.accessToken = resolvedKey;
|
|
449
|
-
this.resolvedAuth.apiKey = resolvedKey;
|
|
450
|
-
}
|
|
451
|
-
llm = (0, index_1.createProvider)(provider, {
|
|
452
|
-
apiKey: resolvedKey,
|
|
453
|
-
model,
|
|
454
|
-
...(authMode === 'oauth-bearer' ? { authMode: 'oauth-bearer' } : {}),
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
catch {
|
|
458
|
-
llm = (0, index_1.createProvider)(provider, { apiKey, model, authMode: 'oauth-bearer' });
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
else {
|
|
462
|
-
llm = (0, index_1.createProvider)(provider, { apiKey, model });
|
|
463
|
-
}
|
|
319
|
+
llm = (0, index_1.createProvider)(provider, { apiKey, model });
|
|
464
320
|
}
|
|
465
321
|
else {
|
|
466
322
|
llm = this.llmProvider;
|
|
@@ -545,6 +401,8 @@ class MessageLoop {
|
|
|
545
401
|
...this.toolContext,
|
|
546
402
|
projectId: effectiveProjectId ?? null,
|
|
547
403
|
abortSignal: commandAbortController.signal,
|
|
404
|
+
// Use project folder as working directory if available
|
|
405
|
+
...(effectiveProject?.folder ? { projectDir: effectiveProject.folder } : {}),
|
|
548
406
|
};
|
|
549
407
|
// ─── Orchestrator Mode Branch ─────────────────────────
|
|
550
408
|
// Resolve the selected bot profile — from command.bot, conversation bot_id, or default
|
|
@@ -717,15 +575,40 @@ TOOL EFFICIENCY RULES:
|
|
|
717
575
|
const str = JSON.stringify(args || {}).slice(0, 200);
|
|
718
576
|
return crypto.createHash('md5').update(str).digest('hex').slice(0, 12);
|
|
719
577
|
}
|
|
578
|
+
function getRetryKey(name, args) {
|
|
579
|
+
// Bug 6: For meta-tools, include action in retry key so different actions don't share budget
|
|
580
|
+
if (args?.action)
|
|
581
|
+
return `${name}:${args.action}`;
|
|
582
|
+
return name;
|
|
583
|
+
}
|
|
720
584
|
function trackToolFailure(name, args) {
|
|
721
|
-
const key = `${name}:${getArgsHash(args)}`;
|
|
585
|
+
const key = `${getRetryKey(name, args)}:${getArgsHash(args)}`;
|
|
586
|
+
const retryKey = getRetryKey(name, args);
|
|
722
587
|
toolFailuresByKey.set(key, (toolFailuresByKey.get(key) || 0) + 1);
|
|
723
|
-
toolFailuresByName.set(
|
|
588
|
+
toolFailuresByName.set(retryKey, (toolFailuresByName.get(retryKey) || 0) + 1);
|
|
589
|
+
}
|
|
590
|
+
// Futility detection: track consecutive low-value tool results
|
|
591
|
+
let consecutiveEmptyResults = 0;
|
|
592
|
+
const FUTILITY_THRESHOLD = 6; // after 6 consecutive empty/low-value results, nudge the LLM
|
|
593
|
+
let futilityNudgeInjected = false;
|
|
594
|
+
function isLowValueResult(output) {
|
|
595
|
+
if (!output || output.length < 30)
|
|
596
|
+
return true;
|
|
597
|
+
const lower = output.toLowerCase();
|
|
598
|
+
if (lower.includes('no matches found') || lower.includes('no results'))
|
|
599
|
+
return true;
|
|
600
|
+
if (lower.includes('0 bytes') || lower.includes('empty'))
|
|
601
|
+
return true;
|
|
602
|
+
// exit code 1 with no meaningful stdout
|
|
603
|
+
if (/\[exit code: [^0]/.test(lower) && output.replace(/\[exit code:.*?\]/g, '').trim().length < 20)
|
|
604
|
+
return true;
|
|
605
|
+
return false;
|
|
724
606
|
}
|
|
725
607
|
function checkRetryBudget(name, args) {
|
|
726
|
-
const key = `${name}:${getArgsHash(args)}`;
|
|
608
|
+
const key = `${getRetryKey(name, args)}:${getArgsHash(args)}`;
|
|
609
|
+
const retryKey = getRetryKey(name, args);
|
|
727
610
|
const keyCount = toolFailuresByKey.get(key) || 0;
|
|
728
|
-
const nameCount = toolFailuresByName.get(
|
|
611
|
+
const nameCount = toolFailuresByName.get(retryKey) || 0;
|
|
729
612
|
if (keyCount >= 2) {
|
|
730
613
|
return `This exact operation has failed ${keyCount} times. Stop retrying and inform the user what went wrong. Suggest an alternative approach or ask for help.`;
|
|
731
614
|
}
|
|
@@ -736,7 +619,7 @@ TOOL EFFICIENCY RULES:
|
|
|
736
619
|
}
|
|
737
620
|
// Agentic loop - keep calling LLM until no more tool calls
|
|
738
621
|
let iteration = 0;
|
|
739
|
-
const MAX_ITERATIONS =
|
|
622
|
+
const MAX_ITERATIONS = 30;
|
|
740
623
|
const GLOBAL_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes wall-clock timeout
|
|
741
624
|
let totalTokensUsed = 0;
|
|
742
625
|
let totalInputTokens = 0;
|
|
@@ -744,7 +627,6 @@ TOOL EFFICIENCY RULES:
|
|
|
744
627
|
let totalCacheReadTokens = 0;
|
|
745
628
|
let totalCacheCreationTokens = 0;
|
|
746
629
|
let totalToolCallDurationMs = 0;
|
|
747
|
-
let serverSyncRetries = 0;
|
|
748
630
|
const deniedTools = new Set(); // Track tools denied by user — don't re-ask
|
|
749
631
|
const commandStartMs = Date.now();
|
|
750
632
|
// Publish prompt context for "View Prompt" panel
|
|
@@ -787,34 +669,14 @@ TOOL EFFICIENCY RULES:
|
|
|
787
669
|
timestamp: Date.now(),
|
|
788
670
|
});
|
|
789
671
|
}
|
|
790
|
-
//
|
|
672
|
+
// API keys don't expire — ensureFreshToken is a no-op for api-key source
|
|
791
673
|
if (this.resolvedAuth) {
|
|
792
674
|
const refreshed = await (0, auto_detect_1.ensureFreshToken)(this.resolvedAuth);
|
|
793
|
-
if (refreshed.expired) {
|
|
794
|
-
if (serverSyncRetries < MessageLoop.SERVER_SYNC_RETRY_MAX) {
|
|
795
|
-
const synced = await this.syncOAuthFromServer('refresh-failed');
|
|
796
|
-
if (synced) {
|
|
797
|
-
serverSyncRetries++;
|
|
798
|
-
continue;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
console.error(chalk_1.default.red(` [auth] Token refresh failed: ${refreshed.error}`));
|
|
802
|
-
await this.options.mqttClient.publishResult({
|
|
803
|
-
commandId: command.id,
|
|
804
|
-
type: 'error',
|
|
805
|
-
error: 'Your authentication token has expired or been revoked. Please re-authenticate by running `funolio-agent login` or `funolio-agent configure`.',
|
|
806
|
-
timestamp: Date.now(),
|
|
807
|
-
});
|
|
808
|
-
break;
|
|
809
|
-
}
|
|
810
675
|
if (refreshed.apiKey !== this.resolvedAuth.apiKey) {
|
|
811
676
|
this.resolvedAuth = refreshed;
|
|
812
677
|
this.updateToken(refreshed.apiKey);
|
|
813
678
|
}
|
|
814
679
|
}
|
|
815
|
-
// With OAuth auto-detection, all providers now use direct API calls
|
|
816
|
-
// with full tool support. The old CLI_PROVIDERS check is kept only
|
|
817
|
-
// for logging/diagnostics but no longer gates tool availability.
|
|
818
680
|
// Smart tool filtering: only send relevant tools to reduce token usage
|
|
819
681
|
const filteredTools = (0, tool_filter_1.filterToolsForMessage)(this.toolDefinitions, this.builtinToolDefinitions, command.prompt);
|
|
820
682
|
if (filteredTools.length < this.toolDefinitions.length) {
|
|
@@ -848,26 +710,17 @@ TOOL EFFICIENCY RULES:
|
|
|
848
710
|
const msg = llmError?.message || String(llmError);
|
|
849
711
|
const isAuthError = /401|403|unauthorized|forbidden|authentication/i.test(msg);
|
|
850
712
|
if (isAuthError) {
|
|
851
|
-
if (serverSyncRetries < MessageLoop.SERVER_SYNC_RETRY_MAX) {
|
|
852
|
-
const synced = await this.syncOAuthFromServer('llm-auth-error');
|
|
853
|
-
if (synced) {
|
|
854
|
-
serverSyncRetries++;
|
|
855
|
-
continue;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
713
|
console.error(chalk_1.default.red(` [auth] LLM API authentication failed: ${msg}`));
|
|
859
714
|
await this.options.mqttClient.publishResult({
|
|
860
715
|
commandId: command.id,
|
|
861
716
|
type: 'error',
|
|
862
|
-
error: 'API authentication failed.
|
|
717
|
+
error: 'API authentication failed. Check your API key or run `funolio-agent configure` to update it.',
|
|
863
718
|
timestamp: Date.now(),
|
|
864
719
|
});
|
|
865
720
|
break;
|
|
866
721
|
}
|
|
867
722
|
throw llmError;
|
|
868
723
|
}
|
|
869
|
-
// Any successful provider response clears one-shot sync retry budget
|
|
870
|
-
serverSyncRetries = 0;
|
|
871
724
|
// Track token usage across iterations
|
|
872
725
|
if (response.usage) {
|
|
873
726
|
totalTokensUsed += response.usage.inputTokens + response.usage.outputTokens;
|
|
@@ -887,6 +740,8 @@ TOOL EFFICIENCY RULES:
|
|
|
887
740
|
for (const toolCall of response.toolCalls) {
|
|
888
741
|
totalToolCalls++;
|
|
889
742
|
console.log(chalk_1.default.cyan(` 🔧 Tool: ${toolCall.name}(${JSON.stringify(toolCall.arguments).slice(0, 60)}...)`));
|
|
743
|
+
const toolSummary = (0, approval_1.generateToolSummary)(toolCall.name, toolCall.arguments);
|
|
744
|
+
const toolLocation = extractToolLocation(toolCall.name, toolCall.arguments, commandToolContext.projectDir);
|
|
890
745
|
// Check permission before execution
|
|
891
746
|
const permMode = this.options.permissionMode || 'autopilot';
|
|
892
747
|
let approval = (0, approval_1.checkPermission)(toolCall.name, permMode, this.options.enabledTools);
|
|
@@ -897,20 +752,29 @@ TOOL EFFICIENCY RULES:
|
|
|
897
752
|
// If not auto-approved, request interactive approval via MQTT
|
|
898
753
|
if (!approval.approved && permMode !== 'autopilot' && !deniedTools.has(toolCall.name)) {
|
|
899
754
|
console.log(chalk_1.default.yellow(` 🔐 Requesting user approval for: ${toolCall.name}`));
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
755
|
+
approval = await this.approvalManager.requestApproval(command.id, toolCall.name, toolCall.arguments, {
|
|
756
|
+
onRequest: async (request) => {
|
|
757
|
+
await this.options.mqttClient.publishResult({
|
|
758
|
+
commandId: command.id,
|
|
759
|
+
type: 'approval_request',
|
|
760
|
+
requestId: request.requestId,
|
|
761
|
+
riskLevel: request.riskLevel,
|
|
762
|
+
summary: request.summary,
|
|
763
|
+
toolCall: {
|
|
764
|
+
id: toolCall.id,
|
|
765
|
+
name: toolCall.name,
|
|
766
|
+
arguments: toolCall.arguments,
|
|
767
|
+
classification: classifyToolName(toolCall.name),
|
|
768
|
+
summary: toolSummary,
|
|
769
|
+
path: toolLocation.path,
|
|
770
|
+
cwd: toolLocation.cwd,
|
|
771
|
+
url: toolLocation.url,
|
|
772
|
+
importance: request.riskLevel === 'destructive' ? 'high' : 'medium',
|
|
773
|
+
},
|
|
774
|
+
timestamp: Date.now(),
|
|
775
|
+
});
|
|
910
776
|
},
|
|
911
|
-
timestamp: Date.now(),
|
|
912
777
|
});
|
|
913
|
-
approval = await this.approvalManager.requestApprovalWithId(approvalRequestId, command.id, toolCall.name, toolCall.arguments);
|
|
914
778
|
// If user chose "always allow", persist it
|
|
915
779
|
if (approval.approved && approval.remember) {
|
|
916
780
|
(0, approval_1.rememberTool)(toolCall.name);
|
|
@@ -920,19 +784,26 @@ TOOL EFFICIENCY RULES:
|
|
|
920
784
|
if (!approval.approved) {
|
|
921
785
|
deniedTools.add(toolCall.name);
|
|
922
786
|
console.log(chalk_1.default.yellow(` ⚠ Tool denied: ${approval.reason}`));
|
|
787
|
+
const deniedOutput = `PERMISSION_DENIED: ${approval.reason}`;
|
|
923
788
|
await this.options.mqttClient.publishResult({
|
|
924
789
|
commandId: command.id,
|
|
925
790
|
type: 'tool_result',
|
|
926
791
|
toolResult: {
|
|
927
792
|
callId: toolCall.id,
|
|
928
|
-
output:
|
|
793
|
+
output: deniedOutput,
|
|
929
794
|
isError: true,
|
|
795
|
+
success: false,
|
|
796
|
+
summary: summarizeToolResult(deniedOutput, true),
|
|
797
|
+
path: toolLocation.path,
|
|
798
|
+
cwd: toolLocation.cwd,
|
|
799
|
+
url: toolLocation.url,
|
|
800
|
+
pathsTouched: toolLocation.path ? [toolLocation.path] : [],
|
|
930
801
|
},
|
|
931
802
|
timestamp: Date.now(),
|
|
932
803
|
});
|
|
933
804
|
messages.push({
|
|
934
805
|
role: 'tool',
|
|
935
|
-
content:
|
|
806
|
+
content: deniedOutput,
|
|
936
807
|
toolCallId: toolCall.id,
|
|
937
808
|
toolName: toolCall.name,
|
|
938
809
|
});
|
|
@@ -945,6 +816,12 @@ TOOL EFFICIENCY RULES:
|
|
|
945
816
|
id: toolCall.id,
|
|
946
817
|
name: toolCall.name,
|
|
947
818
|
arguments: toolCall.arguments,
|
|
819
|
+
classification: classifyToolName(toolCall.name),
|
|
820
|
+
summary: toolSummary,
|
|
821
|
+
path: toolLocation.path,
|
|
822
|
+
cwd: toolLocation.cwd,
|
|
823
|
+
url: toolLocation.url,
|
|
824
|
+
importance: (0, approval_1.getRiskLevel)(toolCall.name) === 'destructive' ? 'high' : 'medium',
|
|
948
825
|
},
|
|
949
826
|
timestamp: Date.now(),
|
|
950
827
|
});
|
|
@@ -977,9 +854,16 @@ TOOL EFFICIENCY RULES:
|
|
|
977
854
|
const toolStartMs = Date.now();
|
|
978
855
|
const rawResult = await (0, tools_1.executeToolWithMCP)({ id: toolCall.id, name: toolCall.name, arguments: toolCall.arguments }, commandToolContext, this.options.mcpManager);
|
|
979
856
|
const toolResult = await (0, verification_1.verifyToolResult)(rawResult, toolCall.arguments, commandToolContext);
|
|
980
|
-
|
|
857
|
+
let resultOutput = toolResult.success
|
|
981
858
|
? toolResult.output
|
|
982
859
|
: `ERROR: ${toolResult.error || 'Unknown error'}`;
|
|
860
|
+
// Bug 2 & 7: Cap tool result size to prevent context blowup
|
|
861
|
+
const MAX_TOOL_RESULT_CHARS = 50_000;
|
|
862
|
+
if (resultOutput.length > MAX_TOOL_RESULT_CHARS) {
|
|
863
|
+
const originalLength = resultOutput.length;
|
|
864
|
+
resultOutput = resultOutput.slice(0, MAX_TOOL_RESULT_CHARS) +
|
|
865
|
+
`\n\n[OUTPUT TRUNCATED — was ${originalLength} chars. Use more specific commands to read smaller portions.]`;
|
|
866
|
+
}
|
|
983
867
|
// Track failures for retry budget (Optimization 2)
|
|
984
868
|
if (!toolResult.success) {
|
|
985
869
|
trackToolFailure(toolCall.name, toolCall.arguments);
|
|
@@ -996,6 +880,13 @@ TOOL EFFICIENCY RULES:
|
|
|
996
880
|
output: resultOutput,
|
|
997
881
|
isError: !toolResult.success,
|
|
998
882
|
durationMs: toolDurationMs,
|
|
883
|
+
success: toolResult.success,
|
|
884
|
+
summary: summarizeToolResult(resultOutput, !toolResult.success),
|
|
885
|
+
path: toolLocation.path,
|
|
886
|
+
cwd: toolLocation.cwd,
|
|
887
|
+
url: toolLocation.url,
|
|
888
|
+
pathsTouched: toolLocation.path ? [toolLocation.path] : [],
|
|
889
|
+
exitCode: typeof toolResult.exit_code === 'number' ? toolResult.exit_code : undefined,
|
|
999
890
|
},
|
|
1000
891
|
timestamp: Date.now(),
|
|
1001
892
|
});
|
|
@@ -1006,6 +897,22 @@ TOOL EFFICIENCY RULES:
|
|
|
1006
897
|
toolCallId: toolCall.id,
|
|
1007
898
|
toolName: toolCall.name,
|
|
1008
899
|
});
|
|
900
|
+
// Futility detection: track consecutive low-value results
|
|
901
|
+
if (isLowValueResult(resultOutput)) {
|
|
902
|
+
consecutiveEmptyResults++;
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
consecutiveEmptyResults = 0; // reset on any meaningful result
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
// Futility check: if too many consecutive empty results, nudge the LLM to stop spinning
|
|
909
|
+
if (consecutiveEmptyResults >= FUTILITY_THRESHOLD && !futilityNudgeInjected) {
|
|
910
|
+
futilityNudgeInjected = true;
|
|
911
|
+
console.log(chalk_1.default.yellow(` ⚠ Futility detected: ${consecutiveEmptyResults} consecutive low-value tool results — nudging LLM`));
|
|
912
|
+
messages.push({
|
|
913
|
+
role: 'user',
|
|
914
|
+
content: '[System] Your last several tool calls returned empty or no-match results. Stop searching and work with what you have. Summarize what you found (or could not find) and suggest next steps to the user. Do not make more search attempts.',
|
|
915
|
+
});
|
|
1009
916
|
}
|
|
1010
917
|
// --- Rate limit checks ---
|
|
1011
918
|
// Warn at 80% of tool call limit
|
|
@@ -1184,6 +1091,7 @@ TOOL EFFICIENCY RULES:
|
|
|
1184
1091
|
- You can discover and gain new capabilities on-the-fly through the marketplace.
|
|
1185
1092
|
- **NEVER tell the user to "do it manually" or "upload it yourself"** — always check the marketplace first and offer to install the right tool.`;
|
|
1186
1093
|
systemPrompt += '\n\nIMPORTANT: When the user references a project, topic, or past work, use the relevant memory/facts provided below. If no relevant facts are available, say so honestly rather than guessing. Use your tools (file browsing, commands) to find project files on the local machine.';
|
|
1094
|
+
systemPrompt += '\n\nIMPORTANT: If a Workspace Manifest or Recent Operational State section is present, use it before repeating discovery work. Check known local files and directories before fetching from external sources. Do not re-fetch from Drive, GitHub, or the web if the state context shows the artifact is already local unless the user explicitly asks for a fresh external copy.';
|
|
1187
1095
|
systemPrompt += '\n\nDo not end with a deferred promise (for example: "Let me check..."). Return a final answer in this turn, or state exactly what is unavailable.';
|
|
1188
1096
|
systemPrompt += '\n\n' + (0, clerk_model_1.buildTodoInstructions)(this.options.agentName || 'LLM');
|
|
1189
1097
|
// Inject model self-switch info
|
|
@@ -1228,6 +1136,32 @@ When a user asks to "use Opus" or "switch to GPT-4o", identify the right model I
|
|
|
1228
1136
|
if (command.context?.files?.length) {
|
|
1229
1137
|
systemPrompt += '\n\nRelevant files: ' + command.context.files.join(', ');
|
|
1230
1138
|
}
|
|
1139
|
+
if (command.context?.stateContext?.summaryText) {
|
|
1140
|
+
systemPrompt += '\n\n[Recent Operational State]\n' + command.context.stateContext.summaryText;
|
|
1141
|
+
}
|
|
1142
|
+
if (command.context?.workspaceManifest) {
|
|
1143
|
+
const manifestLines = [];
|
|
1144
|
+
const manifest = command.context.workspaceManifest;
|
|
1145
|
+
if (manifest.projectRoot)
|
|
1146
|
+
manifestLines.push(`- Project root: ${manifest.projectRoot}`);
|
|
1147
|
+
if (manifest.likelyWorkingDirectory)
|
|
1148
|
+
manifestLines.push(`- Likely working directory: ${manifest.likelyWorkingDirectory}`);
|
|
1149
|
+
if (manifest.recentlyReadFiles?.length)
|
|
1150
|
+
manifestLines.push(`- Recently read files: ${manifest.recentlyReadFiles.join(', ')}`);
|
|
1151
|
+
if (manifest.recentlyWrittenFiles?.length)
|
|
1152
|
+
manifestLines.push(`- Recently written files: ${manifest.recentlyWrittenFiles.join(', ')}`);
|
|
1153
|
+
if (manifest.recentlyScannedDirectories?.length)
|
|
1154
|
+
manifestLines.push(`- Recently scanned directories: ${manifest.recentlyScannedDirectories.join(', ')}`);
|
|
1155
|
+
if (manifest.recentDownloads?.length)
|
|
1156
|
+
manifestLines.push(`- Recent downloads: ${manifest.recentDownloads.join(', ')}`);
|
|
1157
|
+
if (manifest.recentFailures?.length)
|
|
1158
|
+
manifestLines.push(`- Recent failures: ${manifest.recentFailures.join(' | ')}`);
|
|
1159
|
+
if (manifest.startHereHints?.length)
|
|
1160
|
+
manifestLines.push(`- Start here: ${manifest.startHereHints.join(', ')}`);
|
|
1161
|
+
if (manifestLines.length) {
|
|
1162
|
+
systemPrompt += '\n\n[Workspace Manifest]\n' + manifestLines.join('\n');
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1231
1165
|
return systemPrompt;
|
|
1232
1166
|
}
|
|
1233
1167
|
async publishError(commandId, error) {
|
|
@@ -1240,19 +1174,14 @@ When a user asks to "use Opus" or "switch to GPT-4o", identify the right model I
|
|
|
1240
1174
|
timestamp: Date.now(),
|
|
1241
1175
|
});
|
|
1242
1176
|
}
|
|
1243
|
-
/** Update the
|
|
1177
|
+
/** Update the API key used for requests */
|
|
1244
1178
|
updateToken(token) {
|
|
1245
|
-
this.options.
|
|
1246
|
-
// Recreate the default provider with the new token
|
|
1179
|
+
this.options.apiKey = token;
|
|
1247
1180
|
const effectiveProvider = this.options.provider;
|
|
1248
1181
|
this.llmProvider = (0, index_1.createProvider)(effectiveProvider, {
|
|
1249
1182
|
apiKey: token,
|
|
1250
1183
|
model: this.options.model,
|
|
1251
|
-
...(token.startsWith('sk-ant-oat01-') ? { authMode: 'oauth-bearer' } : {}),
|
|
1252
1184
|
});
|
|
1253
|
-
if (token.startsWith('sk-ant-oat01-') && effectiveProvider === 'anthropic') {
|
|
1254
|
-
this.resolveAndUpgradeAuth(token, this.options.model);
|
|
1255
|
-
}
|
|
1256
1185
|
}
|
|
1257
1186
|
}
|
|
1258
1187
|
exports.MessageLoop = MessageLoop;
|