cawdex 1.35.86 → 1.35.88

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.
@@ -0,0 +1,37 @@
1
+ import type { CawdexConfig, Message } from './types.js';
2
+ export type LifespanLevel = 'low' | 'medium' | 'high';
3
+ export interface LifespanDimension {
4
+ id: 'compression' | 'interference' | 'revision' | 'maintenance';
5
+ label: string;
6
+ score: number;
7
+ level: LifespanLevel;
8
+ evidence: string[];
9
+ actions: string[];
10
+ }
11
+ export interface LifespanReport {
12
+ format: 'cawdex-lifespan-v1';
13
+ version: 1;
14
+ generatedAt: string;
15
+ cwd: string;
16
+ summary: {
17
+ overallScore: number;
18
+ level: LifespanLevel;
19
+ estimatedTokens: number;
20
+ contextWindowTokens: number;
21
+ contextPercent: number;
22
+ messageCount: number;
23
+ userTurns: number;
24
+ assistantTurns: number;
25
+ toolMessages: number;
26
+ toolCalls: number;
27
+ toolErrors: number;
28
+ };
29
+ dimensions: LifespanDimension[];
30
+ nextActions: string[];
31
+ }
32
+ export interface LifespanFormatOptions {
33
+ json: boolean;
34
+ }
35
+ export declare function parseLifespanArgs(args: string): LifespanFormatOptions;
36
+ export declare function buildLifespanReport(messages: Message[], config: Pick<CawdexConfig, 'model' | 'provider' | 'baseURL' | 'contextWindowTokens' | 'fallbackModel' | 'memory'>, cwd: string, now?: Date): LifespanReport;
37
+ export declare function formatLifespanReport(report: LifespanReport, options?: LifespanFormatOptions): string;
@@ -0,0 +1,229 @@
1
+ import { estimateTokens, inferContextWindowTokens } from './compaction.js';
2
+ function clampScore(value) {
3
+ if (!Number.isFinite(value))
4
+ return 0;
5
+ return Math.max(0, Math.min(100, Math.round(value)));
6
+ }
7
+ function levelFor(score) {
8
+ if (score >= 67)
9
+ return 'high';
10
+ if (score >= 34)
11
+ return 'medium';
12
+ return 'low';
13
+ }
14
+ function textOf(message) {
15
+ return typeof message.content === 'string' ? message.content : '';
16
+ }
17
+ function countMatches(messages, pattern) {
18
+ let count = 0;
19
+ for (const message of messages) {
20
+ const text = textOf(message);
21
+ if (text && pattern.test(text))
22
+ count++;
23
+ }
24
+ return count;
25
+ }
26
+ function countAllMatches(messages, pattern) {
27
+ let count = 0;
28
+ for (const message of messages) {
29
+ const text = textOf(message);
30
+ if (!text)
31
+ continue;
32
+ const matches = text.match(pattern);
33
+ if (matches)
34
+ count += matches.length;
35
+ }
36
+ return count;
37
+ }
38
+ function lastUserMessage(messages) {
39
+ for (let i = messages.length - 1; i >= 0; i--) {
40
+ if (messages[i].role === 'user')
41
+ return textOf(messages[i]);
42
+ }
43
+ return '';
44
+ }
45
+ function firstLines(values, limit) {
46
+ return [...new Set(values.filter(Boolean))].slice(0, limit);
47
+ }
48
+ function compactOneLine(value, limit = 110) {
49
+ const oneLine = value.replace(/\s+/g, ' ').trim();
50
+ return oneLine.length <= limit ? oneLine : `${oneLine.slice(0, Math.max(0, limit - 3))}...`;
51
+ }
52
+ function uniq(values) {
53
+ return [...new Set(values)];
54
+ }
55
+ function dimension(id, label, score, evidence, actions) {
56
+ const normalized = clampScore(score);
57
+ return {
58
+ id,
59
+ label,
60
+ score: normalized,
61
+ level: levelFor(normalized),
62
+ evidence: firstLines(evidence, 5),
63
+ actions: firstLines(actions, 5),
64
+ };
65
+ }
66
+ export function parseLifespanArgs(args) {
67
+ const parts = args.split(/\s+/).map((part) => part.trim()).filter(Boolean);
68
+ return {
69
+ json: parts.some((part, index) => part === '--json'
70
+ || part === 'json'
71
+ || part === 'format=json'
72
+ || part === '--format=json'
73
+ || (part === '--format' && parts[index + 1] === 'json')),
74
+ };
75
+ }
76
+ export function buildLifespanReport(messages, config, cwd, now = new Date()) {
77
+ const estimatedTokens = estimateTokens(messages);
78
+ const contextWindowTokens = inferContextWindowTokens(config);
79
+ const contextPercent = contextWindowTokens > 0 ? estimatedTokens / contextWindowTokens : 0;
80
+ const userTurns = messages.filter((message) => message.role === 'user').length;
81
+ const assistantTurns = messages.filter((message) => message.role === 'assistant').length;
82
+ const toolMessages = messages.filter((message) => message.role === 'tool').length;
83
+ const toolCalls = messages.reduce((sum, message) => sum + (message.tool_calls?.length ?? 0), 0);
84
+ const toolErrors = countMatches(messages, /\b(error|failed|failure|timeout|timed out|exception|traceback|rate limit|429)\b/i);
85
+ const compactedMarkers = countMatches(messages, /(CONVERSATION SUMMARY|context cap|omitted \d+ older messages|compacted \d+)/i);
86
+ const commandTurns = messages.filter((message) => message.role === 'user' && textOf(message).trim().startsWith('/')).length;
87
+ const correctionTurns = countMatches(messages, /\b(actually|instead|change|revise|make it|not that|wrong|should have|shouldn't|doesn't|isn't)\b/i);
88
+ const oldTaskReferences = countAllMatches(messages, /\b(poem|essay|game|config|resume|history|logo|swarm|oauth|openrouter|benchmark|theme)\b/gi);
89
+ const recentUser = compactOneLine(lastUserMessage(messages), 140);
90
+ const providerText = `${config.provider || ''} ${config.baseURL || ''}`.toLowerCase();
91
+ const model = (config.model || '').toLowerCase();
92
+ const isOpenRouter = providerText.includes('openrouter');
93
+ const memoryEnabled = config.memory?.enabled !== false;
94
+ const fallbackEnabled = Boolean(config.fallbackModel && config.fallbackModel !== config.model);
95
+ const dimensions = [];
96
+ const compressionEvidence = [
97
+ `${estimatedTokens.toLocaleString()} estimated tokens across ${messages.length} messages.`,
98
+ `${Math.round(contextPercent * 100)}% of the inferred ${contextWindowTokens.toLocaleString()} token context window is in use.`,
99
+ ];
100
+ if (compactedMarkers > 0)
101
+ compressionEvidence.push(`${compactedMarkers} compaction/context-cap marker${compactedMarkers === 1 ? '' : 's'} detected.`);
102
+ if (messages.length > 80)
103
+ compressionEvidence.push('Long message history increases stale-context and cursor recovery risk.');
104
+ const compressionScore = contextPercent * 90
105
+ + Math.max(0, messages.length - 40) * 0.7
106
+ + compactedMarkers * 12;
107
+ dimensions.push(dimension('compression', 'Compression aging', compressionScore, compressionEvidence, [
108
+ '/context dossier <current task> before a large patch or benchmark run.',
109
+ '/fork <name> when starting an unrelated task from an old session.',
110
+ '/history to confirm token growth before another provider call.',
111
+ 'Use /clear only when the current thread is no longer needed.',
112
+ ]));
113
+ const interferenceEvidence = [
114
+ `${userTurns} user turn${userTurns === 1 ? '' : 's'}, ${assistantTurns} assistant turn${assistantTurns === 1 ? '' : 's'}, ${toolMessages} tool message${toolMessages === 1 ? '' : 's'}.`,
115
+ ];
116
+ if (oldTaskReferences >= 10)
117
+ interferenceEvidence.push(`Repeated prior task nouns detected (${oldTaskReferences} hits), which can bleed into the next answer.`);
118
+ if (recentUser)
119
+ interferenceEvidence.push(`Latest user turn: "${recentUser}"`);
120
+ if (toolCalls > 20)
121
+ interferenceEvidence.push(`${toolCalls} tool call records are still in the active history.`);
122
+ const interferenceScore = Math.max(0, userTurns - 6) * 4
123
+ + Math.max(0, oldTaskReferences - 8) * 2
124
+ + Math.max(0, toolCalls - 12) * 1.5
125
+ + (recentUser.length > 0 && recentUser.length < 30 && userTurns > 4 ? 12 : 0);
126
+ dimensions.push(dimension('interference', 'Interference aging', interferenceScore, interferenceEvidence, [
127
+ '/fork <name> before switching product goals or benchmark targets.',
128
+ '/back to remove accidental or provider-corrupted turns.',
129
+ 'Restate the exact target in one sentence before asking for a long generation.',
130
+ '/context brief to re-anchor on the current repo instead of old chat content.',
131
+ ]));
132
+ const revisionEvidence = [
133
+ `${commandTurns} slash-command turn${commandTurns === 1 ? '' : 's'} and ${correctionTurns} correction/revision cue${correctionTurns === 1 ? '' : 's'} detected.`,
134
+ ];
135
+ if (correctionTurns > 4)
136
+ revisionEvidence.push('Several correction turns suggest the active state may differ from earlier instructions.');
137
+ if (commandTurns > 8)
138
+ revisionEvidence.push('Many local command turns can make the next model call inherit stale operational context.');
139
+ const revisionScore = correctionTurns * 8 + Math.max(0, commandTurns - 4) * 3;
140
+ dimensions.push(dimension('revision', 'Revision aging', revisionScore, revisionEvidence, [
141
+ '/manifest <target> to state expected files, risks, and verification before edits.',
142
+ '/context dossier <current task> to rebuild a focused local file map.',
143
+ 'For UI or behavior rewrites, name the current desired behavior explicitly.',
144
+ '/export md before major rewrites if the prior decisions must be preserved.',
145
+ ]));
146
+ const maintenanceEvidence = [
147
+ `Provider: ${config.provider || 'unknown'}; model: ${config.model || 'unknown'}.`,
148
+ `MemPalace memory is ${memoryEnabled ? 'enabled' : 'disabled'}.`,
149
+ ];
150
+ if (toolErrors > 0)
151
+ maintenanceEvidence.push(`${toolErrors} error/timeout/rate-limit marker${toolErrors === 1 ? '' : 's'} found in active history.`);
152
+ if (isOpenRouter && !fallbackEnabled)
153
+ maintenanceEvidence.push('OpenRouter is active without a distinct fallback model.');
154
+ if (isOpenRouter && /:free|openrouter\/free|owl-alpha/.test(model))
155
+ maintenanceEvidence.push('Free or experimental OpenRouter model detected; latency and empty responses are more likely.');
156
+ const maintenanceScore = toolErrors * 10
157
+ + (memoryEnabled ? 0 : 18)
158
+ + (isOpenRouter && !fallbackEnabled ? 18 : 0)
159
+ + (isOpenRouter && /:free|openrouter\/free|owl-alpha/.test(model) ? 12 : 0);
160
+ dimensions.push(dimension('maintenance', 'Maintenance aging', maintenanceScore, maintenanceEvidence, [
161
+ '/doctor no-registry for local install/config readiness without npm traffic.',
162
+ '/fallback <model-id> when using OpenRouter for interactive work.',
163
+ '/memory status to confirm project/global memory is available.',
164
+ '/openai-login smoke when Codex OAuth should be the primary provider.',
165
+ ]));
166
+ const overallScore = clampScore(dimensions.reduce((sum, item) => sum + item.score, 0) / dimensions.length);
167
+ const nextActions = uniq(dimensions
168
+ .filter((item) => item.level !== 'low')
169
+ .sort((a, b) => b.score - a.score)
170
+ .flatMap((item) => item.actions.slice(0, 2))).slice(0, 6);
171
+ return {
172
+ format: 'cawdex-lifespan-v1',
173
+ version: 1,
174
+ generatedAt: now.toISOString(),
175
+ cwd,
176
+ summary: {
177
+ overallScore,
178
+ level: levelFor(overallScore),
179
+ estimatedTokens,
180
+ contextWindowTokens,
181
+ contextPercent,
182
+ messageCount: messages.length,
183
+ userTurns,
184
+ assistantTurns,
185
+ toolMessages,
186
+ toolCalls,
187
+ toolErrors,
188
+ },
189
+ dimensions,
190
+ nextActions,
191
+ };
192
+ }
193
+ function formatPercent(value) {
194
+ return `${Math.round(value * 100)}%`;
195
+ }
196
+ function formatActionBlock(actions) {
197
+ if (actions.length === 0)
198
+ return [' Actions: none required right now.'];
199
+ return [' Actions:', ...actions.map((action) => ` - ${action}`)];
200
+ }
201
+ export function formatLifespanReport(report, options = { json: false }) {
202
+ if (options.json)
203
+ return JSON.stringify(report, null, 2);
204
+ const lines = [
205
+ '',
206
+ ' Cawdex Lifespan Diagnostic',
207
+ '',
208
+ ` Overall: ${report.summary.level} (${report.summary.overallScore}/100)`,
209
+ ` Window: ~${report.summary.estimatedTokens.toLocaleString()} / ${report.summary.contextWindowTokens.toLocaleString()} tokens (${formatPercent(report.summary.contextPercent)})`,
210
+ ` Turns: ${report.summary.userTurns} user / ${report.summary.assistantTurns} assistant / ${report.summary.toolMessages} tool`,
211
+ '',
212
+ ];
213
+ for (const item of report.dimensions) {
214
+ lines.push(` ${item.label}: ${item.level} (${item.score}/100)`);
215
+ for (const evidence of item.evidence)
216
+ lines.push(` - ${evidence}`);
217
+ lines.push(...formatActionBlock(item.actions.slice(0, item.level === 'low' ? 1 : 3)));
218
+ lines.push('');
219
+ }
220
+ if (report.nextActions.length > 0) {
221
+ lines.push(' Next actions:');
222
+ for (const action of report.nextActions)
223
+ lines.push(` - ${action}`);
224
+ lines.push('');
225
+ }
226
+ lines.push(' Frame: compression, interference, revision, and maintenance aging for long-running agent sessions.');
227
+ return lines.join('\n');
228
+ }
229
+ //# sourceMappingURL=lifespan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifespan.js","sourceRoot":"","sources":["../src/lifespan.ts"],"names":[],"mappings":"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"}
package/dist/query.js CHANGED
@@ -17,7 +17,7 @@ import { audioCue } from './audio.js';
17
17
  import { setStatus } from './status.js';
18
18
  import { collapseCompletedTurns } from './turn-context.js';
19
19
  import * as liveQueue from './live-queue.js';
20
- import { isFooterActive, setFooterActivity, setFooterCost, writeScrollableLine } from './fixed-footer.js';
20
+ import { activateFooter, buildFooterSnapshot, isFooterActive, setFooterActivity, setFooterCost, shouldUseFixedFooter, 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
23
  import { applyAgentToolInstructions } from './agents-md.js';
@@ -203,6 +203,18 @@ function startWorkingIndicator(startedAtMs, screenReader, turn = 0) {
203
203
  ];
204
204
  let frame = 0;
205
205
  let stopped = false;
206
+ if (isFooterActive()) {
207
+ setFooterActivity(messages[0], turn, startedAtMs);
208
+ return {
209
+ stop: () => {
210
+ if (stopped)
211
+ return;
212
+ stopped = true;
213
+ if (isFooterActive())
214
+ setFooterActivity('Receiving response', turn, startedAtMs);
215
+ },
216
+ };
217
+ }
206
218
  const paint = () => {
207
219
  const message = messages[Math.floor(frame / 8) % messages.length];
208
220
  if (isFooterActive()) {
@@ -232,6 +244,22 @@ function startWorkingIndicator(startedAtMs, screenReader, turn = 0) {
232
244
  },
233
245
  };
234
246
  }
247
+ function ensureTurnFooterActive(ctx, screenReader) {
248
+ if (screenReader)
249
+ return;
250
+ if (isFooterActive())
251
+ return;
252
+ if (!shouldUseFixedFooter(ctx.config))
253
+ return;
254
+ activateFooter(buildFooterSnapshot(ctx.config, ctx.mode, { id: ctx.sessionId }, ctx.cwd));
255
+ }
256
+ function clearActiveTurnHandle(streamAbort) {
257
+ const g = globalThis;
258
+ if (g.__turnAbortCtl === streamAbort) {
259
+ g.__turnAbortCtl = null;
260
+ g.__turnCancelCurrent = null;
261
+ }
262
+ }
235
263
  /**
236
264
  * Count how many NON-OVERLAPPING occurrences of the last `windowSize`
237
265
  * characters of `fullText` appear in the full string. Bails as soon as
@@ -1242,6 +1270,7 @@ export async function runQuery(ctx) {
1242
1270
  // skips the live queue box (NVDA/JAWS would re-read every cursor move
1243
1271
  // as new text, drowning the actual response).
1244
1272
  const isScreenReader = ctx.config.voice?.accessibility?.screenReader === true;
1273
+ ensureTurnFooterActive(ctx, isScreenReader);
1245
1274
  const inputGuard = startInputSuppression(isScreenReader);
1246
1275
  let earlyWorkingIndicator = null;
1247
1276
  try {
@@ -1524,12 +1553,20 @@ export async function runQuery(ctx) {
1524
1553
  const firstTokenTimeoutMs = resolveTurnFirstTokenTimeoutMs(ctx.config, fastDirect);
1525
1554
  let streamIdleTimedOut = false;
1526
1555
  const streamIdleTimeoutMs = resolveStreamIdleTimeoutMs(fastDirect);
1556
+ let closeCurrentStream = null;
1527
1557
  try {
1528
1558
  const requestConfig = fastDirect
1529
1559
  ? { ...ctx.config, maxTokens: Math.min(ctx.config.maxTokens ?? 700, 700) }
1530
1560
  : ctx.config;
1531
1561
  const stream = streamChat(requestConfig, apiMessages, requestTools, streamAbort.signal);
1532
1562
  const iterator = stream[Symbol.asyncIterator]();
1563
+ closeCurrentStream = () => {
1564
+ const close = iterator.return;
1565
+ closeCurrentStream = null;
1566
+ if (typeof close === 'function') {
1567
+ void close.call(iterator, undefined).catch(() => { });
1568
+ }
1569
+ };
1533
1570
  async function waitForNextEvent(nextPromise, waitTimeoutMs) {
1534
1571
  if (waitTimeoutMs <= 0)
1535
1572
  return nextPromise;
@@ -1657,10 +1694,11 @@ export async function runQuery(ctx) {
1657
1694
  streamAbort.abort();
1658
1695
  }
1659
1696
  catch { /* close any provider socket left open after done */ }
1660
- void iterator.return?.(undefined).catch(() => { });
1697
+ closeCurrentStream?.();
1661
1698
  break;
1662
1699
  }
1663
1700
  }
1701
+ closeCurrentStream?.();
1664
1702
  if (!usageRecorded && (hasOutput || (toolCalls && toolCalls.length > 0))) {
1665
1703
  const promptEstimate = Math.max(1, estimateTokens(apiMessages));
1666
1704
  const completionEstimate = Math.max(1, Math.ceil(((fullText || '') + (toolCalls ? JSON.stringify(toolCalls) : '')).length / 3.5));
@@ -1696,6 +1734,8 @@ export async function runQuery(ctx) {
1696
1734
  // print below shouldn't trail an animated row.
1697
1735
  workingIndicator?.stop();
1698
1736
  workingIndicator = null;
1737
+ closeCurrentStream?.();
1738
+ clearActiveTurnHandle(streamAbort);
1699
1739
  const msg = err instanceof Error ? err.message : String(err);
1700
1740
  // Always close the streaming line first so the error doesn't glue to text.
1701
1741
  if (hasOutput && !lastCharWasNewline)
@@ -1845,6 +1885,7 @@ export async function runQuery(ctx) {
1845
1885
  ctx.messages.push({ role: 'assistant', content: `[API error: ${msg}]` });
1846
1886
  break;
1847
1887
  }
1888
+ clearActiveTurnHandle(streamAbort);
1848
1889
  if (!hasOutput && (!toolCalls || toolCalls.length === 0)) {
1849
1890
  const failedModel = ctx.config.model;
1850
1891
  const fallback = fallbackModelForTurn(ctx.config, usedFallbackModel);