cawdex 1.35.75 → 1.35.77
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/README.md +5 -5
- package/bin/anycode.js +2 -2
- package/bin/cawdex.js +408 -408
- package/bin/ecc-hooks.cjs +11 -11
- package/dist/agents-md.d.ts +31 -0
- package/dist/agents-md.js +340 -0
- package/dist/agents-md.js.map +1 -0
- package/dist/agents.js +1424 -1424
- package/dist/api.d.ts +1 -0
- package/dist/api.js +19 -14
- package/dist/api.js.map +1 -1
- package/dist/autonomous-loops.js +287 -287
- package/dist/benchmark-repos.d.ts +31 -0
- package/dist/benchmark-repos.js +234 -8
- package/dist/benchmark-repos.js.map +1 -1
- package/dist/command-palette.js +5 -2
- package/dist/command-palette.js.map +1 -1
- package/dist/compaction.js +8 -8
- package/dist/config.js +57 -36
- package/dist/config.js.map +1 -1
- package/dist/content-engine.js +543 -543
- package/dist/context-brief.d.ts +4 -0
- package/dist/context-brief.js +230 -0
- package/dist/context-brief.js.map +1 -0
- package/dist/cost-tracker.d.ts +33 -14
- package/dist/cost-tracker.js +81 -19
- package/dist/cost-tracker.js.map +1 -1
- package/dist/coverage.js +39 -39
- package/dist/docs-sync.js +98 -98
- package/dist/evaluation.js +452 -452
- package/dist/fixed-footer.d.ts +11 -2
- package/dist/fixed-footer.js +115 -26
- package/dist/fixed-footer.js.map +1 -1
- package/dist/git-workflow.js +49 -49
- package/dist/imports.d.ts +126 -0
- package/dist/imports.js +611 -0
- package/dist/imports.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +367 -66
- package/dist/index.js.map +1 -1
- package/dist/inline-suggest.js +136 -12
- package/dist/inline-suggest.js.map +1 -1
- package/dist/live-queue.js +1 -1
- package/dist/live-queue.js.map +1 -1
- package/dist/model-aliases.d.ts +37 -0
- package/dist/model-aliases.js +203 -0
- package/dist/model-aliases.js.map +1 -0
- package/dist/orchestration.js +15 -15
- package/dist/permissions.d.ts +6 -0
- package/dist/permissions.js +53 -0
- package/dist/permissions.js.map +1 -1
- package/dist/pm2-manager.js +26 -26
- package/dist/query.d.ts +0 -1
- package/dist/query.js +105 -41
- package/dist/query.js.map +1 -1
- package/dist/refactor.js +87 -87
- package/dist/repo-command.js +7 -1
- package/dist/repo-command.js.map +1 -1
- package/dist/search-first.js +92 -92
- package/dist/skill-create.js +100 -100
- package/dist/stitch.js +1 -1
- package/dist/system-prompt.d.ts +2 -1
- package/dist/system-prompt.js +10 -5
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/github-repo-digest.d.ts +1 -1
- package/dist/tools/github-repo-digest.js +38 -6
- package/dist/tools/github-repo-digest.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.js.map +1 -1
- package/dist/verification.js +55 -55
- package/package.json +1 -1
- package/resources/__init__.py +1 -1
- package/resources/exgentic/cawdex_agent/README.md +114 -114
- package/resources/exgentic/cawdex_agent/__init__.py +5 -5
- package/resources/exgentic/cawdex_agent/agent.py +605 -605
- package/resources/exgentic/cawdex_agent/requirements.txt +2 -2
- package/resources/exgentic/cawdex_agent/setup.sh +21 -21
- package/resources/exgentic/cawdex_agent/utils.py +1061 -1061
- package/resources/hal/cawdex_agent/README.md +24 -24
- package/resources/hal/cawdex_agent/__init__.py +1 -1
- package/resources/hal/cawdex_agent/main.py +550 -550
- package/resources/hal/cawdex_agent/requirements.txt +2 -2
- package/resources/kbench/cawdex_agent/README.md +107 -107
- package/resources/kbench/cawdex_agent/adapter.manifest.json +19 -19
- package/resources/kbench/cawdex_agent/runner.mjs +753 -753
- package/resources/open_agent_leaderboard/cawdex-agent-card.md +119 -119
- package/resources/terminal_bench/__init__.py +1 -1
- package/resources/terminal_bench/cawdex_agent.py +174 -174
- package/resources/terminal_bench/setup.sh +121 -121
package/dist/permissions.js
CHANGED
|
@@ -1,6 +1,59 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { saveConfig } from './config.js';
|
|
3
3
|
import { evaluateCommand } from './execpolicy.js';
|
|
4
|
+
export function explainPermission(tool, input, config) {
|
|
5
|
+
const lines = [
|
|
6
|
+
`tool: ${tool.name}`,
|
|
7
|
+
`permission mode: ${config.permissionMode}`,
|
|
8
|
+
];
|
|
9
|
+
if (tool.name === 'bash') {
|
|
10
|
+
const command = String(input.command || '').trim();
|
|
11
|
+
const policy = evaluateCommand(command);
|
|
12
|
+
lines.push(`execpolicy: ${policy.decision}${policy.ruleId ? ` (${policy.ruleId})` : ''}`);
|
|
13
|
+
if (policy.reason)
|
|
14
|
+
lines.push(`execpolicy reason: ${policy.reason}`);
|
|
15
|
+
if (policy.decision === 'forbidden') {
|
|
16
|
+
lines.push('result: blocked before the normal permission prompt');
|
|
17
|
+
return {
|
|
18
|
+
decision: 'deny',
|
|
19
|
+
reason: policy.reason || 'blocked by execpolicy',
|
|
20
|
+
lines,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (policy.decision === 'allow') {
|
|
24
|
+
lines.push('result: allowed by execpolicy before mode checks');
|
|
25
|
+
return {
|
|
26
|
+
decision: 'allow',
|
|
27
|
+
reason: `execpolicy${policy.ruleId ? ` ${policy.ruleId}` : ''}`,
|
|
28
|
+
lines,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (config.permissionMode === 'yolo') {
|
|
33
|
+
lines.push('result: allowed because yolo permits all non-forbidden tools');
|
|
34
|
+
return { decision: 'allow', reason: 'permission mode yolo', lines };
|
|
35
|
+
}
|
|
36
|
+
if (tool.isReadOnly) {
|
|
37
|
+
lines.push('result: allowed because read-only tools do not prompt');
|
|
38
|
+
return { decision: 'allow', reason: 'read-only tool', lines };
|
|
39
|
+
}
|
|
40
|
+
if (config.alwaysAllowedTools?.includes(tool.name)) {
|
|
41
|
+
lines.push('result: allowed by the per-tool always-allow list');
|
|
42
|
+
return { decision: 'allow', reason: 'always-allow list', lines };
|
|
43
|
+
}
|
|
44
|
+
if (config.permissionMode === 'auto' && !tool.isDestructive) {
|
|
45
|
+
lines.push('result: allowed because auto permits non-destructive tools');
|
|
46
|
+
return { decision: 'allow', reason: 'auto mode non-destructive tool', lines };
|
|
47
|
+
}
|
|
48
|
+
lines.push('result: prompt required before execution');
|
|
49
|
+
return {
|
|
50
|
+
decision: 'prompt',
|
|
51
|
+
reason: config.permissionMode === 'auto'
|
|
52
|
+
? 'destructive tool in auto mode'
|
|
53
|
+
: 'ask mode requires confirmation',
|
|
54
|
+
lines,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
4
57
|
/**
|
|
5
58
|
* Check if a tool call is allowed under the current permission mode.
|
|
6
59
|
* Returns true if allowed, false if denied.
|
package/dist/permissions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
|
package/dist/pm2-manager.js
CHANGED
|
@@ -63,32 +63,32 @@ export function listPM2Services(cwd) {
|
|
|
63
63
|
* @returns A prompt with guidance on creating ecosystem.config.js
|
|
64
64
|
*/
|
|
65
65
|
export function buildEcosystemPrompt(cwd) {
|
|
66
|
-
return `To manage multiple services with PM2, create an ecosystem.config.js file in ${cwd}.
|
|
67
|
-
|
|
68
|
-
Example structure:
|
|
69
|
-
module.exports = {
|
|
70
|
-
apps: [
|
|
71
|
-
{
|
|
72
|
-
name: 'api-server',
|
|
73
|
-
script: './dist/server.js',
|
|
74
|
-
instances: 2,
|
|
75
|
-
exec_mode: 'cluster',
|
|
76
|
-
max_memory_restart: '500M',
|
|
77
|
-
env: {
|
|
78
|
-
NODE_ENV: 'production'
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
name: 'worker',
|
|
83
|
-
script: './dist/worker.js',
|
|
84
|
-
instances: 1,
|
|
85
|
-
max_memory_restart: '300M'
|
|
86
|
-
}
|
|
87
|
-
]
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
Then start all services with: pm2 start ecosystem.config.js
|
|
91
|
-
Monitor logs with: pm2 logs
|
|
66
|
+
return `To manage multiple services with PM2, create an ecosystem.config.js file in ${cwd}.
|
|
67
|
+
|
|
68
|
+
Example structure:
|
|
69
|
+
module.exports = {
|
|
70
|
+
apps: [
|
|
71
|
+
{
|
|
72
|
+
name: 'api-server',
|
|
73
|
+
script: './dist/server.js',
|
|
74
|
+
instances: 2,
|
|
75
|
+
exec_mode: 'cluster',
|
|
76
|
+
max_memory_restart: '500M',
|
|
77
|
+
env: {
|
|
78
|
+
NODE_ENV: 'production'
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'worker',
|
|
83
|
+
script: './dist/worker.js',
|
|
84
|
+
instances: 1,
|
|
85
|
+
max_memory_restart: '300M'
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
Then start all services with: pm2 start ecosystem.config.js
|
|
91
|
+
Monitor logs with: pm2 logs
|
|
92
92
|
View status with: pm2 status`;
|
|
93
93
|
}
|
|
94
94
|
/**
|
package/dist/query.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type { Mode } from './modes.js';
|
|
|
4
4
|
export declare function resolveFirstTokenTimeoutMs(config: Pick<CawdexConfig, 'model' | 'provider'>): number;
|
|
5
5
|
export declare function resolveStreamIdleTimeoutMs(fastDirect?: boolean): number;
|
|
6
6
|
export declare function isKnownFlakyOpenRouterModel(config: Pick<CawdexConfig, 'model' | 'provider'>): boolean;
|
|
7
|
-
export declare function fallbackModelForKnownFlakyTurn(config: CawdexConfig, usedFallbackModel?: boolean): string | null;
|
|
8
7
|
export declare function isTurnCancelKeySequence(chunk: Buffer): boolean;
|
|
9
8
|
export declare function shouldUseFastDirectReply(userQuery: string | undefined, mode: Mode, env?: NodeJS.ProcessEnv): boolean;
|
|
10
9
|
export interface WorkingIndicator {
|
package/dist/query.js
CHANGED
|
@@ -9,7 +9,7 @@ import { buildSystemPrompt } from './system-prompt.js';
|
|
|
9
9
|
import { runHooks } from './hooks.js';
|
|
10
10
|
import { scanToolCall, printSecurityWarning } from './security.js';
|
|
11
11
|
import { trackUsage } from './cost-tracker.js';
|
|
12
|
-
import { shouldCompact, compactMessages, quickCompact, buildCompactionConfig, contextCapTokens, enforceContextCap, inferContextWindowTokens, } from './compaction.js';
|
|
12
|
+
import { shouldCompact, compactMessages, quickCompact, estimateTokens, buildCompactionConfig, contextCapTokens, enforceContextCap, inferContextWindowTokens, } from './compaction.js';
|
|
13
13
|
import { assistantTranscriptPrefix, theme, sym, printToolRun, printToolResult, printThinkingOpen, printThinkingText, printThinkingClose, printCost, printApiError, formatDuration, categorizeApiError } from './theme.js';
|
|
14
14
|
import { isVoiceEnabled, getTtsConfig, getAccessibilityConfig, speakAssistantResponse, speak, speakUserEcho, } from './voice.js';
|
|
15
15
|
import { isLikelyDestructive, describeDestructive, countWords, summarize } from './accessibility.js';
|
|
@@ -20,6 +20,7 @@ import * as liveQueue from './live-queue.js';
|
|
|
20
20
|
import { isFooterActive, setFooterActivity, setFooterCost, writeScrollableLine } from './fixed-footer.js';
|
|
21
21
|
import { applyQueuedInputChunk, drainQueuedInputBytes, queuedInputBytesToText } from './prompt-buffer.js';
|
|
22
22
|
import { emit as dbgEmit } from './debug.js';
|
|
23
|
+
import { applyAgentToolInstructions } from './agents-md.js';
|
|
23
24
|
import { buildBenchmarkCompletionReminder, buildBenchmarkTrajectorySystemBlock, makeBenchmarkInvalidToolActionEvent, makeBenchmarkTraceEvent, writeBenchmarkTrace, } from './benchmark-trace.js';
|
|
24
25
|
import { buildTodoStateBlock } from './tools/todo.js';
|
|
25
26
|
import { buildRuntimeInfoBlock } from './runtime-info.js';
|
|
@@ -30,11 +31,11 @@ import { archiveLargeToolOutput } from './tool-output-archive.js';
|
|
|
30
31
|
// not persisted — restart, see hint again. Keyed by sessionId so different
|
|
31
32
|
// sessions get fresh hints.
|
|
32
33
|
const _thinkingHintShownForSession = new Set();
|
|
33
|
-
const INTERACTIVE_FIRST_TOKEN_TIMEOUT_MS =
|
|
34
|
+
const INTERACTIVE_FIRST_TOKEN_TIMEOUT_MS = 8_000;
|
|
34
35
|
const INTERACTIVE_FLAKY_FIRST_TOKEN_TIMEOUT_MS = 6_000;
|
|
35
36
|
const NON_INTERACTIVE_FIRST_TOKEN_TIMEOUT_MS = 60_000;
|
|
36
37
|
const NON_INTERACTIVE_FLAKY_FIRST_TOKEN_TIMEOUT_MS = 20_000;
|
|
37
|
-
const FAST_DIRECT_FIRST_TOKEN_TIMEOUT_MS =
|
|
38
|
+
const FAST_DIRECT_FIRST_TOKEN_TIMEOUT_MS = 5_000;
|
|
38
39
|
const INTERACTIVE_STREAM_IDLE_TIMEOUT_MS = 45_000;
|
|
39
40
|
const NON_INTERACTIVE_STREAM_IDLE_TIMEOUT_MS = 120_000;
|
|
40
41
|
const FAST_DIRECT_STREAM_IDLE_TIMEOUT_MS = 20_000;
|
|
@@ -84,13 +85,6 @@ function fallbackModelForTurn(config, usedFallbackModel) {
|
|
|
84
85
|
return null;
|
|
85
86
|
return fallback;
|
|
86
87
|
}
|
|
87
|
-
export function fallbackModelForKnownFlakyTurn(config, usedFallbackModel = false) {
|
|
88
|
-
if (process.env.CAWDEX_ALLOW_FLAKY_MODELS === '1')
|
|
89
|
-
return null;
|
|
90
|
-
if (!isKnownFlakyOpenRouterModel(config))
|
|
91
|
-
return null;
|
|
92
|
-
return fallbackModelForTurn(config, usedFallbackModel);
|
|
93
|
-
}
|
|
94
88
|
export function isTurnCancelKeySequence(chunk) {
|
|
95
89
|
const seq = chunk.toString('utf8');
|
|
96
90
|
return (seq === '\x1b' ||
|
|
@@ -122,6 +116,7 @@ const FAST_DIRECT_POSITIVE = [
|
|
|
122
116
|
];
|
|
123
117
|
const FAST_DIRECT_REPO_OR_TOOL = /\b(repo|repository|codebase|workspace|file|folder|directory|path|terminal|powershell|shell|command|npm|node|python|typescript|javascript|git|commit|diff|pr|pull request|branch|test|build|lint|install|package|debug|error|stack trace|log|fix|implement|refactor|review|audit|security|benchmark|run|execute|read|search|grep|edit|patch|write[- ]file|website|web\s*site|site|portfolio|landing\s+page|web\s+page|html|css|react|vue|svelte|vite|next\.?js|single[- ]file|dashboard|component|form|desktop|save|hireable|resume)\b/i;
|
|
124
118
|
const FAST_DIRECT_CONTEXTUAL = /^(?:(?:please\s+)?(?:can|could|would|will)\s+you\s+(?:please\s+)?|please\s+)?(continue|carry on|resume|do it|same|again|that|this|those|these|it)\b/i;
|
|
119
|
+
const FAST_DIRECT_REVISION_CONTEXT = /^(?:(?:please\s+)?(?:can|could|would|will)\s+you\s+(?:please\s+)?)?(?:make|change|update|revise|rewrite|redo|adjust|convert|turn)\s+(?:it|that|this|him|her|them|he|she)\b/i;
|
|
125
120
|
export function shouldUseFastDirectReply(userQuery, mode, env = process.env) {
|
|
126
121
|
if (env.CAWDEX_FAST_DIRECT === '0')
|
|
127
122
|
return false;
|
|
@@ -136,6 +131,8 @@ export function shouldUseFastDirectReply(userQuery, mode, env = process.env) {
|
|
|
136
131
|
return false;
|
|
137
132
|
if (FAST_DIRECT_CONTEXTUAL.test(text))
|
|
138
133
|
return false;
|
|
134
|
+
if (FAST_DIRECT_REVISION_CONTEXT.test(text))
|
|
135
|
+
return false;
|
|
139
136
|
if (FAST_DIRECT_REPO_OR_TOOL.test(text))
|
|
140
137
|
return false;
|
|
141
138
|
return FAST_DIRECT_POSITIVE.some((pattern) => pattern.test(text));
|
|
@@ -154,9 +151,9 @@ function printInteractiveTurnAccepted(config) {
|
|
|
154
151
|
}
|
|
155
152
|
}
|
|
156
153
|
export function formatWorkingIndicatorFrame(elapsedMs, frameIndex = 0, message = 'Working') {
|
|
157
|
-
const frames = ['\
|
|
154
|
+
const frames = ['\u25dc', '\u25dd', '\u25de', '\u25df'];
|
|
158
155
|
const frame = frames[Math.abs(frameIndex) % frames.length];
|
|
159
|
-
return ` ${frame} ${message} (${formatDuration(elapsedMs)} \u2022
|
|
156
|
+
return ` ${frame} ${message} (${formatDuration(elapsedMs)} \u2022 Esc/F5 to interrupt)`;
|
|
160
157
|
}
|
|
161
158
|
function startWorkingIndicator(startedAtMs, screenReader, turn = 0) {
|
|
162
159
|
if (screenReader)
|
|
@@ -166,11 +163,11 @@ function startWorkingIndicator(startedAtMs, screenReader, turn = 0) {
|
|
|
166
163
|
if (!process.stdout.isTTY)
|
|
167
164
|
return null;
|
|
168
165
|
const messages = [
|
|
169
|
-
'
|
|
170
|
-
'
|
|
171
|
-
'
|
|
172
|
-
'
|
|
173
|
-
'
|
|
166
|
+
'Sumi ink moving',
|
|
167
|
+
'Edo lanterns cycling',
|
|
168
|
+
'Kamon crest pulsing',
|
|
169
|
+
'Neon shoji breathing',
|
|
170
|
+
'Signal blade drawn',
|
|
174
171
|
];
|
|
175
172
|
let frame = 0;
|
|
176
173
|
let stopped = false;
|
|
@@ -1129,18 +1126,21 @@ export async function runQuery(ctx) {
|
|
|
1129
1126
|
// user's configured fallbackModel. After we use it, this latches so we
|
|
1130
1127
|
// don't bounce back and forth between failing models in a single chain.
|
|
1131
1128
|
let usedFallbackModel = false;
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1129
|
+
// Reliability path when fallback is intentionally disabled:
|
|
1130
|
+
// retry the SAME configured model once on transient empty/timeout/unknown
|
|
1131
|
+
// provider failures before surfacing a hard error.
|
|
1132
|
+
let usedPrimaryRetry = false;
|
|
1133
|
+
const retryPrimaryModelOnce = (reason) => {
|
|
1134
|
+
if (usedPrimaryRetry)
|
|
1135
|
+
return false;
|
|
1136
|
+
usedPrimaryRetry = true;
|
|
1137
|
+
const model = ctx.config.model;
|
|
1137
1138
|
resetClient();
|
|
1138
|
-
console.log(theme.warning(` ${sym.warn} ${
|
|
1139
|
-
console.log(theme.dim('
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
}
|
|
1139
|
+
console.log(theme.warning(` ${sym.warn} ${reason} — retrying once on the same model ${model}.`));
|
|
1140
|
+
console.log(theme.dim(' Fallback is disabled; this is a transient-recovery retry only.'));
|
|
1141
|
+
turns--;
|
|
1142
|
+
return true;
|
|
1143
|
+
};
|
|
1144
1144
|
// Tracks whether ANY reasoning tokens arrived across the entire chain.
|
|
1145
1145
|
// Used at chain-end to print a one-time "/thinking is ON but this model
|
|
1146
1146
|
// doesn't emit reasoning" hint. Hoisted to chain scope (not per-turn)
|
|
@@ -1210,8 +1210,11 @@ export async function runQuery(ctx) {
|
|
|
1210
1210
|
// as new text, drowning the actual response).
|
|
1211
1211
|
const isScreenReader = ctx.config.voice?.accessibility?.screenReader === true;
|
|
1212
1212
|
const inputGuard = startInputSuppression(isScreenReader);
|
|
1213
|
+
let earlyWorkingIndicator = null;
|
|
1213
1214
|
try {
|
|
1214
1215
|
if (!chainFastDirect) {
|
|
1216
|
+
printInteractiveTurnAccepted(ctx.config);
|
|
1217
|
+
earlyWorkingIndicator = startWorkingIndicator(chainStart, isScreenReader, 0);
|
|
1215
1218
|
// Turn-boundary collapse runs BEFORE compaction. Every completed prior
|
|
1216
1219
|
// turn becomes [user, "<final text>\n[Completed: used X, Y]"] — the
|
|
1217
1220
|
// model no longer sees stale tool_calls that it might mistake for
|
|
@@ -1263,9 +1266,12 @@ export async function runQuery(ctx) {
|
|
|
1263
1266
|
if (!fastDirect) {
|
|
1264
1267
|
replaceMessagesInPlace(ctx.messages, quickCompact(ctx.messages));
|
|
1265
1268
|
}
|
|
1269
|
+
const requestTools = fastDirect
|
|
1270
|
+
? []
|
|
1271
|
+
: applyAgentToolInstructions(ALL_TOOLS, ctx.cwd, ctx.config.model);
|
|
1266
1272
|
const systemPrompt = fastDirect
|
|
1267
1273
|
? FAST_DIRECT_SYSTEM_PROMPT
|
|
1268
|
-
: buildSystemPrompt(ctx.config, ctx.cwd, ctx.mode, userQuery);
|
|
1274
|
+
: buildSystemPrompt(ctx.config, ctx.cwd, ctx.mode, userQuery, requestTools);
|
|
1269
1275
|
let visibleMessages = fastDirect
|
|
1270
1276
|
? (userQuery ? [{ role: 'user', content: userQuery }] : [])
|
|
1271
1277
|
: maskOldToolResults(ctx.messages);
|
|
@@ -1292,7 +1298,9 @@ export async function runQuery(ctx) {
|
|
|
1292
1298
|
// override.
|
|
1293
1299
|
const stateBlock = fastDirect ? null : buildStateBlock(visibleMessages);
|
|
1294
1300
|
const runtimeInfoBlock = fastDirect ? null : buildRuntimeInfoBlock(ctx.cwd);
|
|
1295
|
-
const repoMapBlock = fastDirect
|
|
1301
|
+
const repoMapBlock = fastDirect || ctx.mode === 'design'
|
|
1302
|
+
? null
|
|
1303
|
+
: buildAutoRepoMapBlock(ctx.cwd, userQuery);
|
|
1296
1304
|
const globalPlanBlock = fastDirect ? null : buildGlobalPlanBlock(visibleMessages);
|
|
1297
1305
|
const todoStateBlock = fastDirect ? null : buildTodoStateBlock(ctx.cwd);
|
|
1298
1306
|
const benchmarkTrajectoryBlock = !fastDirect && ctx.mode === 'benchmark'
|
|
@@ -1326,6 +1334,7 @@ export async function runQuery(ctx) {
|
|
|
1326
1334
|
let lastCharWasNewline = false; // collapse 3+ consecutive newlines down to 2
|
|
1327
1335
|
let consecutiveNewlines = 0;
|
|
1328
1336
|
const turnStart = Date.now();
|
|
1337
|
+
let usageRecorded = false;
|
|
1329
1338
|
// Loop detection state: a stuck model can stream the SAME N-char
|
|
1330
1339
|
// window of text 50+ times in a single API call (observed in the
|
|
1331
1340
|
// wild with openrouter/owl-alpha emitting tool-call JSON as text).
|
|
@@ -1446,16 +1455,22 @@ export async function runQuery(ctx) {
|
|
|
1446
1455
|
// line and then announce every subsequent token as "after the
|
|
1447
1456
|
// waiting line", which is noisier than helpful).
|
|
1448
1457
|
let firstTokenSeen = false;
|
|
1458
|
+
let firstTokenLatencyMs = null;
|
|
1449
1459
|
// Note: the outer `isScreenReader` declared at the top of runQuery
|
|
1450
1460
|
// (line ~340) is in scope here via closure — no need for a second
|
|
1451
1461
|
// declaration. Previously this re-declared inside the while loop
|
|
1452
1462
|
// and TypeScript tolerated it as a different block scope, but it
|
|
1453
1463
|
// was confusing and the audit flagged it as bug-bait.
|
|
1454
1464
|
//
|
|
1455
|
-
// Live waiting indicator on the response line. It keeps
|
|
1456
|
-
//
|
|
1457
|
-
//
|
|
1458
|
-
let workingIndicator =
|
|
1465
|
+
// Live waiting indicator on the response line. It keeps motion and the
|
|
1466
|
+
// interrupt hint visible while still clearing itself before the first
|
|
1467
|
+
// model event writes real output.
|
|
1468
|
+
let workingIndicator = turns === 1 ? earlyWorkingIndicator : null;
|
|
1469
|
+
if (turns === 1)
|
|
1470
|
+
earlyWorkingIndicator = null;
|
|
1471
|
+
if (!workingIndicator) {
|
|
1472
|
+
workingIndicator = startWorkingIndicator(turnStart, isScreenReader, turns);
|
|
1473
|
+
}
|
|
1459
1474
|
// Slow-model warning and first-token watchdog. The warning is a
|
|
1460
1475
|
// UX hint; the watchdog is the hard recovery path for providers
|
|
1461
1476
|
// that accept a request but then never produce a stream event.
|
|
@@ -1482,7 +1497,6 @@ export async function runQuery(ctx) {
|
|
|
1482
1497
|
const requestConfig = fastDirect
|
|
1483
1498
|
? { ...ctx.config, maxTokens: Math.min(ctx.config.maxTokens ?? 700, 700) }
|
|
1484
1499
|
: ctx.config;
|
|
1485
|
-
const requestTools = fastDirect ? [] : ALL_TOOLS;
|
|
1486
1500
|
const stream = streamChat(requestConfig, apiMessages, requestTools, streamAbort.signal);
|
|
1487
1501
|
const iterator = stream[Symbol.asyncIterator]();
|
|
1488
1502
|
while (true) {
|
|
@@ -1529,6 +1543,7 @@ export async function runQuery(ctx) {
|
|
|
1529
1543
|
// model warning timer; subsequent events are normal streaming.
|
|
1530
1544
|
if (!firstTokenSeen) {
|
|
1531
1545
|
firstTokenSeen = true;
|
|
1546
|
+
firstTokenLatencyMs = Date.now() - turnStart;
|
|
1532
1547
|
clearTimeout(slowTimer);
|
|
1533
1548
|
if (streamWaitTimer)
|
|
1534
1549
|
clearTimeout(streamWaitTimer);
|
|
@@ -1573,7 +1588,12 @@ export async function runQuery(ctx) {
|
|
|
1573
1588
|
else if (event.type === 'done') {
|
|
1574
1589
|
if (event.usage) {
|
|
1575
1590
|
const u = event.usage;
|
|
1576
|
-
const
|
|
1591
|
+
const turnDurationMs = Date.now() - turnStart;
|
|
1592
|
+
const { cost, warning } = trackUsage(ctx.sessionId, ctx.config.model, u.prompt, u.completion, {
|
|
1593
|
+
provider: ctx.config.provider,
|
|
1594
|
+
firstTokenMs: firstTokenLatencyMs,
|
|
1595
|
+
durationMs: turnDurationMs,
|
|
1596
|
+
});
|
|
1577
1597
|
chainStats.benchmarkUsageEvents.push({
|
|
1578
1598
|
model: ctx.config.model,
|
|
1579
1599
|
promptTokens: u.prompt,
|
|
@@ -1581,12 +1601,16 @@ export async function runQuery(ctx) {
|
|
|
1581
1601
|
totalTokens: u.total || u.prompt + u.completion,
|
|
1582
1602
|
estimatedCostUsd: cost,
|
|
1583
1603
|
});
|
|
1584
|
-
|
|
1604
|
+
usageRecorded = true;
|
|
1605
|
+
setFooterCost(cost, u.prompt, u.completion, {
|
|
1606
|
+
firstTokenMs: firstTokenLatencyMs,
|
|
1607
|
+
durationMs: turnDurationMs,
|
|
1608
|
+
});
|
|
1585
1609
|
// Single newline separator if we just streamed text, then the
|
|
1586
1610
|
// compact telemetry line.
|
|
1587
1611
|
if (hasOutput && !lastCharWasNewline)
|
|
1588
1612
|
process.stdout.write('\n');
|
|
1589
|
-
printCost(u.prompt, u.completion, cost, warning,
|
|
1613
|
+
printCost(u.prompt, u.completion, cost, warning, turnDurationMs);
|
|
1590
1614
|
}
|
|
1591
1615
|
try {
|
|
1592
1616
|
streamAbort.abort();
|
|
@@ -1596,6 +1620,28 @@ export async function runQuery(ctx) {
|
|
|
1596
1620
|
break;
|
|
1597
1621
|
}
|
|
1598
1622
|
}
|
|
1623
|
+
if (!usageRecorded && (hasOutput || (toolCalls && toolCalls.length > 0))) {
|
|
1624
|
+
const promptEstimate = Math.max(1, estimateTokens(apiMessages));
|
|
1625
|
+
const completionEstimate = Math.max(1, Math.ceil(((fullText || '') + (toolCalls ? JSON.stringify(toolCalls) : '')).length / 3.5));
|
|
1626
|
+
const turnDurationMs = Date.now() - turnStart;
|
|
1627
|
+
const { cost } = trackUsage(ctx.sessionId, ctx.config.model, promptEstimate, completionEstimate, {
|
|
1628
|
+
provider: ctx.config.provider,
|
|
1629
|
+
firstTokenMs: firstTokenLatencyMs,
|
|
1630
|
+
durationMs: turnDurationMs,
|
|
1631
|
+
});
|
|
1632
|
+
chainStats.benchmarkUsageEvents.push({
|
|
1633
|
+
model: ctx.config.model,
|
|
1634
|
+
promptTokens: promptEstimate,
|
|
1635
|
+
completionTokens: completionEstimate,
|
|
1636
|
+
totalTokens: promptEstimate + completionEstimate,
|
|
1637
|
+
estimatedCostUsd: cost,
|
|
1638
|
+
});
|
|
1639
|
+
setFooterCost(cost, promptEstimate, completionEstimate, {
|
|
1640
|
+
firstTokenMs: firstTokenLatencyMs,
|
|
1641
|
+
durationMs: turnDurationMs,
|
|
1642
|
+
});
|
|
1643
|
+
usageRecorded = true;
|
|
1644
|
+
}
|
|
1599
1645
|
clearTimeout(slowTimer);
|
|
1600
1646
|
if (streamWaitTimer)
|
|
1601
1647
|
clearTimeout(streamWaitTimer);
|
|
@@ -1629,9 +1675,12 @@ export async function runQuery(ctx) {
|
|
|
1629
1675
|
turns--;
|
|
1630
1676
|
continue;
|
|
1631
1677
|
}
|
|
1678
|
+
if (retryPrimaryModelOnce(`${failedModel} produced no stream events for ${formatDuration(firstTokenTimeoutMs)}`)) {
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1632
1681
|
const timeoutMsg = `${failedModel} produced no stream events for ${formatDuration(firstTokenTimeoutMs)}`;
|
|
1633
1682
|
console.log(theme.error(` ${sym.warn} ${timeoutMsg}.`));
|
|
1634
|
-
console.log(theme.dim('
|
|
1683
|
+
console.log(theme.dim(' Fallback is disabled and the same-model retry already ran; cancelling so the prompt can recover.'));
|
|
1635
1684
|
console.log(theme.dim(' Use /fallback <model-id> to enable automatic retry, or /model <known-good-model> to switch primary models.'));
|
|
1636
1685
|
ctx.messages.push({ role: 'assistant', content: `[Provider timeout: ${timeoutMsg}]` });
|
|
1637
1686
|
break;
|
|
@@ -1651,6 +1700,9 @@ export async function runQuery(ctx) {
|
|
|
1651
1700
|
turns--;
|
|
1652
1701
|
continue;
|
|
1653
1702
|
}
|
|
1703
|
+
if (!hasOutput && !toolCalls && retryPrimaryModelOnce(timeoutMsg)) {
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1654
1706
|
if (fullText.trim()) {
|
|
1655
1707
|
const partial = `${fullText.trimEnd()}\n[Provider timeout: stream stalled before completion]`;
|
|
1656
1708
|
console.log(theme.warning(` ${sym.warn} stream stalled before completion; returning the partial response.`));
|
|
@@ -1659,7 +1711,7 @@ export async function runQuery(ctx) {
|
|
|
1659
1711
|
}
|
|
1660
1712
|
else {
|
|
1661
1713
|
console.log(theme.error(` ${sym.warn} ${timeoutMsg}.`));
|
|
1662
|
-
console.log(theme.dim('
|
|
1714
|
+
console.log(theme.dim(' Same-model retry already ran; cancelling so the prompt can recover instead of hanging indefinitely.'));
|
|
1663
1715
|
console.log(theme.dim(' Use /fallback <model-id> to enable automatic retry, or /model <known-good-model> to switch primary models.'));
|
|
1664
1716
|
ctx.messages.push({ role: 'assistant', content: `[Provider timeout: ${timeoutMsg}]` });
|
|
1665
1717
|
}
|
|
@@ -1732,6 +1784,9 @@ export async function runQuery(ctx) {
|
|
|
1732
1784
|
turns--; // this retry doesn't burn a turn slot from the max-turns budget
|
|
1733
1785
|
continue;
|
|
1734
1786
|
}
|
|
1787
|
+
if (cat.category === 'unknown' && retryPrimaryModelOnce(`${ctx.config.model} returned a cryptic provider error`)) {
|
|
1788
|
+
continue;
|
|
1789
|
+
}
|
|
1735
1790
|
printApiError(msg, {
|
|
1736
1791
|
baseURL: ctx.config.baseURL,
|
|
1737
1792
|
provider: ctx.config.provider,
|
|
@@ -1765,9 +1820,12 @@ export async function runQuery(ctx) {
|
|
|
1765
1820
|
turns--;
|
|
1766
1821
|
continue;
|
|
1767
1822
|
}
|
|
1823
|
+
if (retryPrimaryModelOnce(`${failedModel} returned an empty response`)) {
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1768
1826
|
const emptyMsg = `${failedModel} returned an empty response`;
|
|
1769
1827
|
console.log(theme.error(` ${sym.warn} ${emptyMsg}.`));
|
|
1770
|
-
console.log(theme.dim('
|
|
1828
|
+
console.log(theme.dim(' Fallback is disabled and the same-model retry already ran.'));
|
|
1771
1829
|
console.log(theme.dim(' Use /fallback <model-id> to enable automatic retry, or /model <known-good-model> to switch primary models.'));
|
|
1772
1830
|
ctx.messages.push({ role: 'assistant', content: `[Provider empty response: ${emptyMsg}]` });
|
|
1773
1831
|
break;
|
|
@@ -1873,6 +1931,8 @@ export async function runQuery(ctx) {
|
|
|
1873
1931
|
}
|
|
1874
1932
|
// Chain ended; back to idle so F1 reports the correct state.
|
|
1875
1933
|
setStatus({ state: 'idle' });
|
|
1934
|
+
if (isFooterActive())
|
|
1935
|
+
setFooterActivity('Ready', 0, null);
|
|
1876
1936
|
// ── Voice: read the assistant's final response ────────────
|
|
1877
1937
|
// Off the hot path — fire-and-forget so the next prompt appears
|
|
1878
1938
|
// immediately. The playback runs in background; F2 pauses, F4 skips.
|
|
@@ -1972,6 +2032,8 @@ export async function runQuery(ctx) {
|
|
|
1972
2032
|
}
|
|
1973
2033
|
}
|
|
1974
2034
|
finally {
|
|
2035
|
+
earlyWorkingIndicator?.stop();
|
|
2036
|
+
earlyWorkingIndicator = null;
|
|
1975
2037
|
// Drain any queued user input typed during streaming. Stash on
|
|
1976
2038
|
// globalThis for the REPL loop in index.ts to restore into the
|
|
1977
2039
|
// next editable prompt. Enter typed mid-stream is preserved as
|
|
@@ -1985,6 +2047,8 @@ export async function runQuery(ctx) {
|
|
|
1985
2047
|
// can't be aborted between turns by Shift+F5 (soft-cancel).
|
|
1986
2048
|
globalThis.__turnAbortCtl = null;
|
|
1987
2049
|
globalThis.__turnCancelCurrent = null;
|
|
2050
|
+
if (isFooterActive())
|
|
2051
|
+
setFooterActivity('Ready', 0, null);
|
|
1988
2052
|
}
|
|
1989
2053
|
}
|
|
1990
2054
|
const TOOL_CALL_LOOP_THRESHOLD = 3;
|