zames_pro 2.40.0 → 2.41.1
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-loop.js +19 -6
- package/dist/attachments.js +6 -0
- package/dist/commands.js +24 -0
- package/dist/gitTools.js +0 -1
- package/package.json +1 -1
package/dist/agent-loop.js
CHANGED
|
@@ -3,6 +3,7 @@ import { loadProjectContext } from './context.js';
|
|
|
3
3
|
import { getGitContext, formatGitContext } from './gitTools.js';
|
|
4
4
|
import { parseXmlToolCalls } from './xml-toolcall.js';
|
|
5
5
|
import { translate } from './i18n.js';
|
|
6
|
+
import { substituteAttachmentMarkers } from './commands.js';
|
|
6
7
|
import { normText } from './browser.js';
|
|
7
8
|
export async function runAgentLoop({ browser, tools, task, workdir, maxIterations = 0, freshChat = false, sendSystemPrompt = false, transcript = null, attachments = [], onThinking = () => { }, onSendPause = () => { }, onSendState = () => { }, onNotice = () => { }, onAssistantThought = () => { }, onToolCall = () => { }, onToolResult = () => { }, onAssistantMessage = () => { }, onChatReady = () => { }, onWarning = () => { }, debugLog = false, locale = 'ru', askDeadlineMs = 240_000, maxAfterToolRetries = 6, onAutoCompact = null, autoCompactPct = 95, contextLimit = 1_000_000, getTokenUsage = null, }) {
|
|
8
9
|
// UI callbacks must NEVER break the agent loop. A rendering error (a huge
|
|
@@ -117,20 +118,32 @@ export async function runAgentLoop({ browser, tools, task, workdir, maxIteration
|
|
|
117
118
|
// Tell the model what the markers mean even when the system-prompt is not
|
|
118
119
|
// (re)sent (resumed chat). The files are uploaded to the chat by the
|
|
119
120
|
// browser; this note just explains the [image#N] / [file#N] markers.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
//
|
|
122
|
+
// The task text keeps its COMPACT markers in the terminal, but what goes
|
|
123
|
+
// to the model is expanded to the real file paths (substituteAttachment
|
|
124
|
+
// Markers) so it can actually read them — a bare [file#1] is useless to
|
|
125
|
+
// the model. The note below still explains the marker→file mapping.
|
|
126
|
+
//
|
|
127
|
+
// Numbering matches AttachmentStore: images and files are counted
|
|
128
|
+
// SEPARATELY (image#1 and file#1 coexist), so the note lists them the
|
|
129
|
+
// same way the substitution resolves them.
|
|
130
|
+
let imgN = 0;
|
|
131
|
+
let fileN = 0;
|
|
132
|
+
const markers = attachments.map((a) => {
|
|
133
|
+
const isImg = String(a.mime || '').startsWith('image/') ||
|
|
134
|
+
/\.(png|jpe?g|gif|webp|bmp|svg|ico)$/i.test(String(a.name || ''));
|
|
135
|
+
return isImg ? '[image#' + ++imgN + ']' : '[file#' + ++fileN + ']';
|
|
124
136
|
});
|
|
125
137
|
message =
|
|
126
|
-
task +
|
|
138
|
+
substituteAttachmentMarkers(task, attachments) +
|
|
127
139
|
String.fromCharCode(10) +
|
|
128
140
|
String.fromCharCode(10) +
|
|
129
141
|
'(The user attached ' +
|
|
130
142
|
attachments.length +
|
|
131
143
|
' file(s) to this message: ' +
|
|
132
144
|
markers.join(', ') +
|
|
133
|
-
'.
|
|
145
|
+
'. The [image#N]/[file#N] markers were replaced above by the real file paths. ' +
|
|
146
|
+
'Look at the uploaded images in the chat; file copies are in <project>/tmp.)';
|
|
134
147
|
}
|
|
135
148
|
transcript?.log('task', { task });
|
|
136
149
|
let malformedRetries = 0;
|
package/dist/attachments.js
CHANGED
|
@@ -139,6 +139,12 @@ export class AttachmentStore {
|
|
|
139
139
|
this.fileCount = 0;
|
|
140
140
|
}
|
|
141
141
|
add(item) {
|
|
142
|
+
// De-duplicate by real path: pasting the SAME file twice must reuse one
|
|
143
|
+
// attachment (one marker, one upload), not create [file#1] and [file#2].
|
|
144
|
+
// The operator saw exactly that when inserting the same file repeatedly.
|
|
145
|
+
const existing = this.items.find((a) => a.path === item.path);
|
|
146
|
+
if (existing)
|
|
147
|
+
return existing;
|
|
142
148
|
const image = isImageName(item.name) || String(item.mime).startsWith('image/');
|
|
143
149
|
const index = image ? ++this.imageCount : ++this.fileCount;
|
|
144
150
|
const marker = image ? '[image#' + index + ']' : '[file#' + index + ']';
|
package/dist/commands.js
CHANGED
|
@@ -508,6 +508,30 @@ export function parseGoalCommand(text) {
|
|
|
508
508
|
return { sub: 'clear' };
|
|
509
509
|
return { sub: 'set', goal: rest };
|
|
510
510
|
}
|
|
511
|
+
// Replace [image#N] / [file#N] markers in a task with the REAL file path, so
|
|
512
|
+
// the model (reading the message in the DeepSeek chat) gets a usable path
|
|
513
|
+
// instead of a bare marker. The terminal keeps showing the compact markers;
|
|
514
|
+
// only the text sent to the browser is expanded. Numbering must match
|
|
515
|
+
// AttachmentStore.add(): images and files are counted SEPARATELY, in the order
|
|
516
|
+
// the attachments were added. A marker with no matching attachment is left
|
|
517
|
+
// as-is (never silently dropped). Pure, so it is unit-tested.
|
|
518
|
+
export function substituteAttachmentMarkers(text, attachments) {
|
|
519
|
+
let imageCount = 0;
|
|
520
|
+
let fileCount = 0;
|
|
521
|
+
const imagePath = new Map();
|
|
522
|
+
const filePath = new Map();
|
|
523
|
+
for (const a of attachments || []) {
|
|
524
|
+
const isImg = String(a.mime || '').startsWith('image/') || isImageName(a.name);
|
|
525
|
+
if (isImg)
|
|
526
|
+
imagePath.set(++imageCount, a.path);
|
|
527
|
+
else
|
|
528
|
+
filePath.set(++fileCount, a.path);
|
|
529
|
+
}
|
|
530
|
+
let out = String(text ?? '');
|
|
531
|
+
out = out.replace(/\[image#(\d+)\]/g, (whole, n) => imagePath.get(Number(n)) ?? whole);
|
|
532
|
+
out = out.replace(/\[file#(\d+)\]/g, (whole, n) => filePath.get(Number(n)) ?? whole);
|
|
533
|
+
return out;
|
|
534
|
+
}
|
|
511
535
|
// True when a `/config ...` line can be handled LIVE (while the agent is busy):
|
|
512
536
|
// the text subcommands only touch config values, never the chat. The bare
|
|
513
537
|
// `/config` and `/config menu` (which pause the editor and read keys) are NOT
|
package/dist/gitTools.js
CHANGED
|
@@ -46,7 +46,6 @@ function quoteArgs(input) {
|
|
|
46
46
|
.map((v) => JSON.stringify(v))
|
|
47
47
|
.join(' ');
|
|
48
48
|
}
|
|
49
|
-
// Checks whether the directory is a git repository and gathers the context.
|
|
50
49
|
export async function getGitContext(workdir) {
|
|
51
50
|
const probe = await runGit('git rev-parse --is-inside-work-tree', workdir, 5000);
|
|
52
51
|
if (probe.trim() !== 'true')
|