autodev-cli 1.4.30 β 1.4.32
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/out/agentBackup/index.d.ts +1 -1
- package/out/agentBackup/index.js +2 -1
- package/out/agentBackup/index.js.map +1 -1
- package/out/agentBackup/upload.d.ts +9 -0
- package/out/agentBackup/upload.js +24 -0
- package/out/agentBackup/upload.js.map +1 -1
- package/out/core/projectMcp.d.ts +20 -0
- package/out/core/projectMcp.js +68 -0
- package/out/core/projectMcp.js.map +1 -1
- package/out/core/settingsLoader.d.ts +8 -0
- package/out/core/settingsLoader.js +1 -0
- package/out/core/settingsLoader.js.map +1 -1
- package/out/messageBuilder.js +15 -3
- package/out/messageBuilder.js.map +1 -1
- package/out/rateLimit.d.ts +16 -0
- package/out/rateLimit.js +57 -3
- package/out/rateLimit.js.map +1 -1
- package/out/taskLoop.js +105 -31
- package/out/taskLoop.js.map +1 -1
- package/out/todo.js +51 -8
- package/out/todo.js.map +1 -1
- package/out/webSocketPoller.d.ts +15 -0
- package/out/webSocketPoller.js +139 -25
- package/out/webSocketPoller.js.map +1 -1
- package/out/webhook.d.ts +1 -1
- package/out/webhookPoller.d.ts +3 -0
- package/out/webhookPoller.js +15 -0
- package/out/webhookPoller.js.map +1 -1
- package/package.json +1 -1
package/out/taskLoop.js
CHANGED
|
@@ -445,6 +445,11 @@ class TaskLoopRunner {
|
|
|
445
445
|
}
|
|
446
446
|
if (this._webhookPoller) {
|
|
447
447
|
this._webhookPoller.setGitEnabled(settings.gitEnabled ?? false);
|
|
448
|
+
this._webhookPoller.setFileBrowserEnabled(settings.enableFileBrowser ?? false);
|
|
449
|
+
// VNC / RDP session bridges are honored only when explicitly enabled β
|
|
450
|
+
// an ungated frame would let a remote party open outbound bridges.
|
|
451
|
+
this._webhookPoller.setVncEnabled(settings.vncEnabled ?? false);
|
|
452
|
+
this._webhookPoller.setRdpEnabled(settings.rdpEnabled ?? false);
|
|
448
453
|
// Wake the idle no-task sleep instantly when a WS-pushed task arrives.
|
|
449
454
|
this._webhookPoller.setOnTaskAppend(() => this._wakeIdleSleep());
|
|
450
455
|
this._webhookPoller.setOnSteer((text) => this._handleSteer(text));
|
|
@@ -636,11 +641,25 @@ class TaskLoopRunner {
|
|
|
636
641
|
const root = this._workspaceRoot;
|
|
637
642
|
if (!root)
|
|
638
643
|
return;
|
|
639
|
-
|
|
644
|
+
// Opt-in gate: writing remote-supplied MCP config spawns stdio child
|
|
645
|
+
// processes on restart (code-execution surface). Ignore unless explicitly
|
|
646
|
+
// enabled, mirroring enableFileBrowser / gitEnabled.
|
|
647
|
+
if (!this._settings?.mcpUpdateEnabled) {
|
|
648
|
+
this._cb?.log('π mcp_update ignored β mcpUpdateEnabled is off (set it in .autodev/settings.json to allow)');
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
this._cb?.log('π§ mcp_update received β validating and writing .mcp.jsonβ¦');
|
|
652
|
+
// Reject entries that would execute an arbitrary shell / path command.
|
|
653
|
+
const { safe, rejected } = (0, projectMcp_1.sanitizeRemoteMcpEntries)(entries);
|
|
654
|
+
if (rejected.length) {
|
|
655
|
+
this._cb?.log(`β οΈ mcp_update dropped ${rejected.length} unsafe entr${rejected.length === 1 ? 'y' : 'ies'}: ${rejected.join(', ')}`);
|
|
656
|
+
}
|
|
657
|
+
if (Object.keys(safe).length === 0) {
|
|
658
|
+
this._cb?.log('β οΈ mcp_update had no safe entries β not writing config or restarting.');
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
640
661
|
try {
|
|
641
|
-
|
|
642
|
-
const typed = entries;
|
|
643
|
-
(0, projectMcp_1.saveProjectUserMcp)(root, typed);
|
|
662
|
+
(0, projectMcp_1.saveProjectUserMcp)(root, safe);
|
|
644
663
|
configManager_1.ConfigManager.syncProjectMcpServers(root, (m) => this._cb?.log(m));
|
|
645
664
|
void configManager_1.ConfigManager.reportProjectMcp(root, (m) => this._cb?.log(m));
|
|
646
665
|
this._cb?.log('β
MCP config synced to .mcp.json, opencode.json, .vscode/mcp.json β restarting loopβ¦');
|
|
@@ -686,6 +705,13 @@ class TaskLoopRunner {
|
|
|
686
705
|
this._cb?.log('β οΈ restore_request ignored β serverApiKey not configured');
|
|
687
706
|
return;
|
|
688
707
|
}
|
|
708
|
+
// The download carries the agent's Bearer credential and overwrites the
|
|
709
|
+
// workspace, so the URL must point at the configured server origin β never
|
|
710
|
+
// an attacker-chosen host from the WS frame.
|
|
711
|
+
if (!(0, agentBackup_1.isTrustedDownloadUrl)(downloadUrl, settings.serverBaseUrl)) {
|
|
712
|
+
this._cb?.log(`π restore_request refused β downloadUrl origin does not match the configured server: ${downloadUrl}`);
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
689
715
|
this._cb?.log(`π restore_request received β downloading backup from ${downloadUrl}β¦`);
|
|
690
716
|
const tmpPath = path.join(os.tmpdir(), `agent-restore-${agentId}-${Date.now()}.zip`);
|
|
691
717
|
try {
|
|
@@ -1686,6 +1712,33 @@ class TaskLoopRunner {
|
|
|
1686
1712
|
}
|
|
1687
1713
|
}
|
|
1688
1714
|
catch (err) {
|
|
1715
|
+
// --- Auth required: pause INDEFINITELY, never mark the task done ----
|
|
1716
|
+
// A logged-out / out-of-credit CLI is not a task failure. Put the task
|
|
1717
|
+
// back to [ ], flip the office to a distinct 'needs reauth' state, and
|
|
1718
|
+
// block until the operator re-authenticates and clicks Retry.
|
|
1719
|
+
if (err instanceof rateLimit_1.AuthError) {
|
|
1720
|
+
const rawMsg = err.rawMessage.slice(0, 300);
|
|
1721
|
+
const currentProvider = this._cb?.getActiveProvider() ?? 'unknown';
|
|
1722
|
+
this._cb?.log(`π Authentication required (${currentProvider}) β pausing loop until re-auth. ${rawMsg}`);
|
|
1723
|
+
this._notifyDiscord(`π **Authentication required** (${currentProvider}) β loop paused until you re-authenticate.\n\`\`\`\n${rawMsg}\n\`\`\``);
|
|
1724
|
+
this._notifyWebhook('reauth_required', {
|
|
1725
|
+
iteration: this._iterations,
|
|
1726
|
+
task: { text: task.text },
|
|
1727
|
+
message: rawMsg,
|
|
1728
|
+
provider: currentProvider,
|
|
1729
|
+
workDir: this._workspaceRoot,
|
|
1730
|
+
gitRepo: this._gitRepo,
|
|
1731
|
+
gitBranch: this._gitBranch,
|
|
1732
|
+
});
|
|
1733
|
+
// Never markDone β restore the task so it is retried after re-auth.
|
|
1734
|
+
await todoWriteManager_1.todoWriter.resetToTodo(todoPath, task).catch(() => { });
|
|
1735
|
+
// Pause indefinitely (no auto-resume) β operator must re-auth + Retry.
|
|
1736
|
+
await this._pauseLoop();
|
|
1737
|
+
if (this._state !== 'running') {
|
|
1738
|
+
break;
|
|
1739
|
+
}
|
|
1740
|
+
continue;
|
|
1741
|
+
}
|
|
1689
1742
|
// --- Rate limit: pause loop, schedule auto-resume -----------------
|
|
1690
1743
|
if (err instanceof rateLimit_1.RateLimitError) {
|
|
1691
1744
|
// Two flavours:
|
|
@@ -1978,6 +2031,13 @@ class TaskLoopRunner {
|
|
|
1978
2031
|
const iscopilotSdkProvider = this._cb?.getActiveProvider() === 'copilot-sdk';
|
|
1979
2032
|
const isOpenCodeCli = this._cb?.getActiveProvider() === 'opencode-cli';
|
|
1980
2033
|
const isOpencodeSdk = this._cb?.getActiveProvider() === 'opencode-sdk';
|
|
2034
|
+
// grok-cli / grok-tui / copilot-cli all tee stdout too β without these flags
|
|
2035
|
+
// the rate-limit / context-length / thrash detectors never ran for them, so
|
|
2036
|
+
// a throttled or context-blown turn was silently walked to give_up and the
|
|
2037
|
+
// task was falsely marked [x].
|
|
2038
|
+
const isGrokCli = this._cb?.getActiveProvider() === 'grok-cli';
|
|
2039
|
+
const isGrokTui = this._cb?.getActiveProvider() === 'grok-tui';
|
|
2040
|
+
const isCopilotCli = this._cb?.getActiveProvider() === 'copilot-cli';
|
|
1981
2041
|
return new Promise((resolve, reject) => {
|
|
1982
2042
|
if (this._state !== 'running') {
|
|
1983
2043
|
resolve();
|
|
@@ -2111,10 +2171,15 @@ class TaskLoopRunner {
|
|
|
2111
2171
|
// Track how many characters of the stdout file we've already forwarded
|
|
2112
2172
|
let lastStdoutLen = 0;
|
|
2113
2173
|
// Check stdout file: forward any new content to Discord/webhook, detect rate limit / context errors
|
|
2174
|
+
// Providers that write a stdout capture file we can scan for failure
|
|
2175
|
+
// banners (rate-limit / context-length / thrash). Every CLI/TUI provider
|
|
2176
|
+
// tees stdout; the two Claude ones and OpenCode also stream/parse extra.
|
|
2177
|
+
const teesStdout = isClaudeCli || isClaudeTui || iscopilotSdkProvider || isOpenCodeCli
|
|
2178
|
+
|| isGrokCli || isGrokTui || isCopilotCli;
|
|
2114
2179
|
const checkStdout = () => {
|
|
2115
|
-
if (!
|
|
2180
|
+
if (!teesStdout) {
|
|
2116
2181
|
return;
|
|
2117
|
-
}
|
|
2182
|
+
}
|
|
2118
2183
|
const content = readStdoutFile();
|
|
2119
2184
|
// Forward new output lines to Discord / webhook.
|
|
2120
2185
|
// claude-cli: stream partial chunks so the operator can see live progress.
|
|
@@ -2139,8 +2204,22 @@ class TaskLoopRunner {
|
|
|
2139
2204
|
else {
|
|
2140
2205
|
lastStdoutLen = content.length; // keep cursor up to date
|
|
2141
2206
|
}
|
|
2142
|
-
//
|
|
2143
|
-
|
|
2207
|
+
// Auth failure detection β highest priority. A logged-out / out-of-credit
|
|
2208
|
+
// CLI exits fast with no rate-limit or context signal, so without this it
|
|
2209
|
+
// was walked remind->give_up and the whole backlog was falsely marked [x].
|
|
2210
|
+
{
|
|
2211
|
+
const authErr = rateLimit_1.AuthDetector.detect(content);
|
|
2212
|
+
if (authErr) {
|
|
2213
|
+
cleanup();
|
|
2214
|
+
reject(authErr);
|
|
2215
|
+
return;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
// Rate limit detection β run for every stdout-teeing provider. The
|
|
2219
|
+
// RateLimitDetector phrases are scoped to error/banner forms, so this
|
|
2220
|
+
// is safe to apply provider-agnostically (grok/copilot were previously
|
|
2221
|
+
// excluded, letting a throttle slip through to a false [x]).
|
|
2222
|
+
{
|
|
2144
2223
|
const rlErr = rateLimit_1.RateLimitDetector.detect(content);
|
|
2145
2224
|
if (rlErr) {
|
|
2146
2225
|
cleanup();
|
|
@@ -2157,28 +2236,13 @@ class TaskLoopRunner {
|
|
|
2157
2236
|
return;
|
|
2158
2237
|
}
|
|
2159
2238
|
}
|
|
2160
|
-
// Context
|
|
2239
|
+
// Context-length / thrash detection for the remaining CLI+TUI providers.
|
|
2240
|
+
// Patterns observed:
|
|
2161
2241
|
// "prompt is too long: 1018289 tokens > 1000000 maximum"
|
|
2162
2242
|
// "Prompt is too long" (last_assistant_message in StopFailure hook)
|
|
2163
|
-
// "context_length_exceeded"
|
|
2243
|
+
// "context_length_exceeded" / "maximum context length"
|
|
2164
2244
|
// "Autocompact is thrashing" (context refills immediately after compact)
|
|
2165
|
-
if (isClaudeCli) {
|
|
2166
|
-
const lc = content.toLowerCase();
|
|
2167
|
-
if (lc.includes('autocompact is thrashing')) {
|
|
2168
|
-
cleanup();
|
|
2169
|
-
reject(new ThrashingError(content.trim()));
|
|
2170
|
-
return;
|
|
2171
|
-
}
|
|
2172
|
-
if (lc.includes('prompt is too long')
|
|
2173
|
-
|| lc.includes('context_length_exceeded')
|
|
2174
|
-
|| /tokens?\s*>\s*\d+\s*maximum/.test(lc)) {
|
|
2175
|
-
cleanup();
|
|
2176
|
-
reject(new ContextLengthError(content.trim()));
|
|
2177
|
-
return;
|
|
2178
|
-
}
|
|
2179
|
-
}
|
|
2180
|
-
// Context length detection for claude-tui (same patterns)
|
|
2181
|
-
if (isClaudeTui) {
|
|
2245
|
+
if (isClaudeCli || isClaudeTui || isGrokCli || isGrokTui || isCopilotCli) {
|
|
2182
2246
|
const lc = content.toLowerCase();
|
|
2183
2247
|
if (lc.includes('autocompact is thrashing')) {
|
|
2184
2248
|
cleanup();
|
|
@@ -2187,6 +2251,7 @@ class TaskLoopRunner {
|
|
|
2187
2251
|
}
|
|
2188
2252
|
if (lc.includes('prompt is too long')
|
|
2189
2253
|
|| lc.includes('context_length_exceeded')
|
|
2254
|
+
|| lc.includes('maximum context length')
|
|
2190
2255
|
|| /tokens?\s*>\s*\d+\s*maximum/.test(lc)) {
|
|
2191
2256
|
cleanup();
|
|
2192
2257
|
reject(new ContextLengthError(content.trim()));
|
|
@@ -2226,9 +2291,17 @@ class TaskLoopRunner {
|
|
|
2226
2291
|
return;
|
|
2227
2292
|
}
|
|
2228
2293
|
// Fast-path: if the stdout capture file already contains a rate-limit
|
|
2229
|
-
// or context-length phrase at exit time, raise immediately.
|
|
2294
|
+
// or context-length phrase at exit time, raise immediately. Applies to
|
|
2295
|
+
// every stdout-teeing provider so a throttled grok/copilot exit pauses
|
|
2296
|
+
// instead of being force-marked done.
|
|
2230
2297
|
const exitStdout = readStdoutFile();
|
|
2231
|
-
if (
|
|
2298
|
+
if (teesStdout) {
|
|
2299
|
+
const authFromStdout = rateLimit_1.AuthDetector.detect(exitStdout);
|
|
2300
|
+
if (authFromStdout) {
|
|
2301
|
+
cleanup();
|
|
2302
|
+
reject(authFromStdout);
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2232
2305
|
const rlFromStdout = rateLimit_1.RateLimitDetector.detect(exitStdout);
|
|
2233
2306
|
if (rlFromStdout) {
|
|
2234
2307
|
cleanup();
|
|
@@ -2440,8 +2513,9 @@ class TaskLoopRunner {
|
|
|
2440
2513
|
return;
|
|
2441
2514
|
}
|
|
2442
2515
|
}
|
|
2443
|
-
//
|
|
2444
|
-
checkStdout();
|
|
2516
|
+
// (stdout capture file is already checked once at the top of this tick via
|
|
2517
|
+
// checkStdout() at line ~2215; a second scan here was redundant and doubled
|
|
2518
|
+
// the per-tick cost of re-reading the growing stdout file.)
|
|
2445
2519
|
// Track JSONL activity
|
|
2446
2520
|
const currentSize = (0, dispatcher_1.getClaudeSessionCursor)(this._workspaceRoot);
|
|
2447
2521
|
if (currentSize !== lastJSONLSize) {
|