@visorcraft/idlehands 2.2.7 → 2.2.9
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/agent/compaction-scoring.js +113 -0
- package/dist/agent/compaction-scoring.js.map +1 -0
- package/dist/agent.js +20 -0
- package/dist/agent.js.map +1 -1
- package/dist/bot/command-logic.js +2 -0
- package/dist/bot/command-logic.js.map +1 -1
- package/dist/bot/commands.js +23 -1
- package/dist/bot/commands.js.map +1 -1
- package/dist/bot/discord-commands.js +9 -1
- package/dist/bot/discord-commands.js.map +1 -1
- package/dist/bot/mcp-discover-command.js +35 -0
- package/dist/bot/mcp-discover-command.js.map +1 -0
- package/dist/bot/metrics-command.js +51 -0
- package/dist/bot/metrics-command.js.map +1 -0
- package/dist/bot/telegram.js +3 -1
- package/dist/bot/telegram.js.map +1 -1
- package/dist/mcp-discovery.js +104 -0
- package/dist/mcp-discovery.js.map +1 -0
- package/dist/tools/transaction.js +60 -0
- package/dist/tools/transaction.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Information density scoring for smart compaction.
|
|
3
|
+
*
|
|
4
|
+
* Scores messages by how valuable they are to keep in context,
|
|
5
|
+
* so compaction can prioritize dropping low-value messages first.
|
|
6
|
+
*
|
|
7
|
+
* Higher score = more valuable = keep longer.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Score a conversation message for compaction priority.
|
|
11
|
+
* Returns 0-100 where higher = more important to keep.
|
|
12
|
+
*/
|
|
13
|
+
export function scoreMessage(msg, index, totalMessages, opts) {
|
|
14
|
+
let score = 50; // baseline
|
|
15
|
+
const reasons = [];
|
|
16
|
+
const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content ?? '');
|
|
17
|
+
const contentLen = content.length;
|
|
18
|
+
// ── Role-based scoring ──
|
|
19
|
+
if (msg.role === 'system') {
|
|
20
|
+
return { index, score: 100, reason: 'system prompt (never drop)' };
|
|
21
|
+
}
|
|
22
|
+
if (msg.role === 'user') {
|
|
23
|
+
score += 15; // User messages are important context
|
|
24
|
+
reasons.push('user');
|
|
25
|
+
// Last user message is critical
|
|
26
|
+
if (index >= totalMessages - 3) {
|
|
27
|
+
score += 30;
|
|
28
|
+
reasons.push('recent');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (msg.role === 'assistant') {
|
|
32
|
+
// Assistant messages with code are high-value
|
|
33
|
+
if (content.includes('```')) {
|
|
34
|
+
score += 20;
|
|
35
|
+
reasons.push('has_code');
|
|
36
|
+
}
|
|
37
|
+
// Assistant messages with substantive text (not just tool calls)
|
|
38
|
+
if (contentLen > 200 && !msg.tool_calls?.length) {
|
|
39
|
+
score += 10;
|
|
40
|
+
reasons.push('substantive');
|
|
41
|
+
}
|
|
42
|
+
// Planning/thinking text is lower value once executed
|
|
43
|
+
if (msg.tool_calls?.length && contentLen < 100) {
|
|
44
|
+
score -= 15;
|
|
45
|
+
reasons.push('thin_planning');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (msg.role === 'tool') {
|
|
49
|
+
// Tool results for read operations are generally low-value (can be re-read)
|
|
50
|
+
if (content.includes('[read_file]') || content.includes('[list_dir]')) {
|
|
51
|
+
score -= 20;
|
|
52
|
+
reasons.push('read_result');
|
|
53
|
+
}
|
|
54
|
+
// Error messages are important (prevent re-attempting)
|
|
55
|
+
if (content.includes('ERROR') || content.includes('error:') || content.includes('failed')) {
|
|
56
|
+
score += 15;
|
|
57
|
+
reasons.push('has_error');
|
|
58
|
+
}
|
|
59
|
+
// Very long tool results are candidates for dropping (bulky)
|
|
60
|
+
if (contentLen > 3000) {
|
|
61
|
+
score -= 10;
|
|
62
|
+
reasons.push('bulky');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// ── Recency bonus ──
|
|
66
|
+
// Messages near the end of the conversation are more valuable
|
|
67
|
+
const recencyRatio = index / totalMessages;
|
|
68
|
+
if (recencyRatio > 0.8) {
|
|
69
|
+
score += 25;
|
|
70
|
+
reasons.push('very_recent');
|
|
71
|
+
}
|
|
72
|
+
else if (recencyRatio > 0.5) {
|
|
73
|
+
score += 10;
|
|
74
|
+
reasons.push('recent_half');
|
|
75
|
+
}
|
|
76
|
+
// ── Active file relevance ──
|
|
77
|
+
if (opts?.activeFiles?.size) {
|
|
78
|
+
for (const file of opts.activeFiles) {
|
|
79
|
+
const basename = file.split('/').pop() ?? '';
|
|
80
|
+
if (content.includes(basename)) {
|
|
81
|
+
score += 15;
|
|
82
|
+
reasons.push('active_file');
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// ── Instruction relevance ──
|
|
88
|
+
if (opts?.lastInstruction) {
|
|
89
|
+
const keywords = opts.lastInstruction.toLowerCase().split(/\s+/).filter(w => w.length > 3);
|
|
90
|
+
const contentLower = content.toLowerCase();
|
|
91
|
+
const hits = keywords.filter(k => contentLower.includes(k)).length;
|
|
92
|
+
if (hits > 2) {
|
|
93
|
+
score += 10;
|
|
94
|
+
reasons.push('relevant');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
index,
|
|
99
|
+
score: Math.max(0, Math.min(100, score)),
|
|
100
|
+
reason: reasons.join(', ') || 'baseline',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Given scored messages, return indices to drop (sorted lowest score first)
|
|
105
|
+
* until the token budget target is met.
|
|
106
|
+
*/
|
|
107
|
+
export function selectDropCandidates(scored, opts) {
|
|
108
|
+
const candidates = scored
|
|
109
|
+
.filter(s => s.index >= opts.minIndex && s.index <= opts.maxIndex && s.score < 100)
|
|
110
|
+
.sort((a, b) => a.score - b.score); // lowest score first
|
|
111
|
+
return candidates.slice(0, opts.targetDrop).map(c => c.index);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=compaction-scoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction-scoring.js","sourceRoot":"","sources":["../../src/agent/compaction-scoring.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAwD,EACxD,KAAa,EACb,aAAqB,EACrB,IAKC;IAED,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC,WAAW;IAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAClG,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAElC,2BAA2B;IAE3B,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC;IACrE,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACxB,KAAK,IAAI,EAAE,CAAC,CAAC,sCAAsC;QACnD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAErB,gCAAgC;QAChC,IAAI,KAAK,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;YAC/B,KAAK,IAAI,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC7B,8CAA8C;QAC9C,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,KAAK,IAAI,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3B,CAAC;QAED,iEAAiE;QACjE,IAAI,UAAU,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;YAChD,KAAK,IAAI,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,sDAAsD;QACtD,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,IAAI,UAAU,GAAG,GAAG,EAAE,CAAC;YAC/C,KAAK,IAAI,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACxB,4EAA4E;QAC5E,IAAI,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACtE,KAAK,IAAI,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9B,CAAC;QAED,uDAAuD;QACvD,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1F,KAAK,IAAI,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5B,CAAC;QAED,6DAA6D;QAC7D,IAAI,UAAU,GAAG,IAAI,EAAE,CAAC;YACtB,KAAK,IAAI,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,sBAAsB;IACtB,8DAA8D;IAC9D,MAAM,YAAY,GAAG,KAAK,GAAG,aAAa,CAAC;IAC3C,IAAI,YAAY,GAAG,GAAG,EAAE,CAAC;QACvB,KAAK,IAAI,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC9B,CAAC;SAAM,IAAI,YAAY,GAAG,GAAG,EAAE,CAAC;QAC9B,KAAK,IAAI,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC9B,CAAC;IAED,8BAA8B;IAC9B,IAAI,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QAC5B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YAC7C,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC/B,KAAK,IAAI,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBAC5B,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,IAAI,IAAI,EAAE,eAAe,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC3F,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACnE,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACb,KAAK,IAAI,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK;QACL,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACxC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU;KACzC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAuB,EACvB,IAOC;IAED,MAAM,UAAU,GAAG,MAAM;SACtB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC;SAClF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,qBAAqB;IAE3D,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|
package/dist/agent.js
CHANGED
|
@@ -41,6 +41,7 @@ import { normalizeApprovalMode } from './shared/config-utils.js';
|
|
|
41
41
|
import { collectSnapshot } from './sys/context.js';
|
|
42
42
|
import { ToolError, ValidationError } from './tools/tool-error.js';
|
|
43
43
|
import * as tools from './tools.js';
|
|
44
|
+
import { EditTransaction } from './tools/transaction.js';
|
|
44
45
|
import { stateDir, timestampedId } from './utils.js';
|
|
45
46
|
import { VaultStore } from './vault.js';
|
|
46
47
|
export { parseToolCallsFromContent };
|
|
@@ -489,6 +490,7 @@ export async function createSession(opts) {
|
|
|
489
490
|
let inFlight = null;
|
|
490
491
|
let initialConnectionProbeDone = false;
|
|
491
492
|
let lastEditedPath;
|
|
493
|
+
let lastTurnTransaction;
|
|
492
494
|
// Plan mode state (Phase 8)
|
|
493
495
|
let planSteps = [];
|
|
494
496
|
// Sub-agent queue state (Phase 18): enforce sequential execution on single-GPU setups.
|
|
@@ -3002,6 +3004,8 @@ export async function createSession(opts) {
|
|
|
3002
3004
|
const absPath = args.path.startsWith('/')
|
|
3003
3005
|
? args.path
|
|
3004
3006
|
: path.resolve(projectDir, args.path);
|
|
3007
|
+
// Track in turn transaction for potential atomic rollback.
|
|
3008
|
+
turnTransaction.track(absPath);
|
|
3005
3009
|
// ── Pre-dispatch: block edits to files in a mutation spiral ──
|
|
3006
3010
|
if (fileMutationBlocked.has(absPath)) {
|
|
3007
3011
|
const basename = path.basename(absPath);
|
|
@@ -3493,6 +3497,7 @@ export async function createSession(opts) {
|
|
|
3493
3497
|
return { id: callId, content: truncated.content };
|
|
3494
3498
|
};
|
|
3495
3499
|
const results = [];
|
|
3500
|
+
const turnTransaction = new EditTransaction();
|
|
3496
3501
|
let invalidArgsThisTurn = false;
|
|
3497
3502
|
// Helper: catch tool errors but re-throw AgentLoopBreak (those must break the outer loop)
|
|
3498
3503
|
const catchToolError = async (e, tc) => {
|
|
@@ -3647,6 +3652,11 @@ export async function createSession(opts) {
|
|
|
3647
3652
|
});
|
|
3648
3653
|
}
|
|
3649
3654
|
}
|
|
3655
|
+
// Store the turn transaction for potential post-turn rollback.
|
|
3656
|
+
if (turnTransaction.hasChanges) {
|
|
3657
|
+
turnTransaction.commit();
|
|
3658
|
+
lastTurnTransaction = turnTransaction;
|
|
3659
|
+
}
|
|
3650
3660
|
// Bail immediately if cancelled during tool execution
|
|
3651
3661
|
if (ac.signal.aborted)
|
|
3652
3662
|
break;
|
|
@@ -3918,6 +3928,16 @@ export async function createSession(opts) {
|
|
|
3918
3928
|
return currentContextTokens > 0 ? currentContextTokens : estimateTokensFromMessages(messages);
|
|
3919
3929
|
},
|
|
3920
3930
|
ask,
|
|
3931
|
+
rollbackLastTurnEdits: async () => {
|
|
3932
|
+
if (!lastTurnTransaction || !lastTurnTransaction.hasChanges) {
|
|
3933
|
+
return { ok: false, error: 'No file edits to roll back.' };
|
|
3934
|
+
}
|
|
3935
|
+
const tx = lastTurnTransaction;
|
|
3936
|
+
lastTurnTransaction = undefined;
|
|
3937
|
+
const callCtx = { cwd: projectDir, noConfirm: true, dryRun: false };
|
|
3938
|
+
const results = await tx.rollback(callCtx);
|
|
3939
|
+
return { ok: true, results };
|
|
3940
|
+
},
|
|
3921
3941
|
rollback: () => {
|
|
3922
3942
|
const cp = conversationBranch.rollback();
|
|
3923
3943
|
if (!cp)
|