@tjamescouch/gro 1.3.10 → 1.3.11
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/main.js +23 -13
- package/dist/package.json +1 -1
- package/dist/stream-markers.js +25 -0
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -22,7 +22,7 @@ import { groError, asError, isGroError, errorLogFields } from "./errors.js";
|
|
|
22
22
|
import { bashToolDefinition, executeBash } from "./tools/bash.js";
|
|
23
23
|
import { agentpatchToolDefinition, executeAgentpatch } from "./tools/agentpatch.js";
|
|
24
24
|
import { groVersionToolDefinition, executeGroVersion, getGroVersion } from "./tools/version.js";
|
|
25
|
-
import { createMarkerParser } from "./stream-markers.js";
|
|
25
|
+
import { createMarkerParser, extractMarkers } from "./stream-markers.js";
|
|
26
26
|
const VERSION = getGroVersion();
|
|
27
27
|
// ---------------------------------------------------------------------------
|
|
28
28
|
// Graceful shutdown state — module-level so signal handlers can save sessions.
|
|
@@ -520,21 +520,23 @@ async function executeTurn(driver, memory, mcp, cfg, sessionId) {
|
|
|
520
520
|
let brokeCleanly = false;
|
|
521
521
|
let idleNudges = 0;
|
|
522
522
|
for (let round = 0; round < cfg.maxToolRounds; round++) {
|
|
523
|
+
// Shared marker handler — used by both streaming parser and tool-arg scanner
|
|
524
|
+
const handleMarker = (marker) => {
|
|
525
|
+
if (marker.name === "model-change") {
|
|
526
|
+
const newModel = resolveModelAlias(marker.arg);
|
|
527
|
+
Logger.info(`Stream marker: model-change '${marker.arg}' → ${newModel}`);
|
|
528
|
+
activeModel = newModel;
|
|
529
|
+
cfg.model = newModel; // persist across turns
|
|
530
|
+
memory.setModel(newModel); // persist in session metadata on save
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
Logger.debug(`Stream marker: ${marker.name}('${marker.arg}')`);
|
|
534
|
+
}
|
|
535
|
+
};
|
|
523
536
|
// Create a fresh marker parser per round so partial state doesn't leak
|
|
524
537
|
const markerParser = createMarkerParser({
|
|
525
538
|
onToken: rawOnToken,
|
|
526
|
-
onMarker
|
|
527
|
-
if (marker.name === "model-change") {
|
|
528
|
-
const newModel = resolveModelAlias(marker.arg);
|
|
529
|
-
Logger.info(`Stream marker: model-change '${marker.arg}' → ${newModel}`);
|
|
530
|
-
activeModel = newModel;
|
|
531
|
-
cfg.model = newModel; // persist across turns
|
|
532
|
-
memory.setModel(newModel); // persist in session metadata on save
|
|
533
|
-
}
|
|
534
|
-
else {
|
|
535
|
-
Logger.debug(`Stream marker: ${marker.name}('${marker.arg}')`);
|
|
536
|
-
}
|
|
537
|
-
},
|
|
539
|
+
onMarker: handleMarker,
|
|
538
540
|
});
|
|
539
541
|
const output = await driver.chat(memory.messages(), {
|
|
540
542
|
model: activeModel,
|
|
@@ -595,6 +597,14 @@ async function executeTurn(driver, memory, mcp, cfg, sessionId) {
|
|
|
595
597
|
Logger.debug(`Failed to parse args for ${fnName}: ${asError(e).message}, using empty args`);
|
|
596
598
|
fnArgs = {};
|
|
597
599
|
}
|
|
600
|
+
// Scan tool call string args for stream markers (e.g. model sends
|
|
601
|
+
// @@model-change('haiku')@@ inside an agentchat_send message).
|
|
602
|
+
// Strip markers from args so they don't leak into tool output.
|
|
603
|
+
for (const key of Object.keys(fnArgs)) {
|
|
604
|
+
if (typeof fnArgs[key] === "string") {
|
|
605
|
+
fnArgs[key] = extractMarkers(fnArgs[key], handleMarker);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
598
608
|
Logger.debug(`Tool call: ${fnName}(${JSON.stringify(fnArgs)})`);
|
|
599
609
|
let result;
|
|
600
610
|
try {
|
package/dist/package.json
CHANGED
package/dist/stream-markers.js
CHANGED
|
@@ -27,6 +27,31 @@ import { Logger } from "./logger.js";
|
|
|
27
27
|
const MARKER_RE = /@@([a-zA-Z][a-zA-Z0-9_-]*)\((?:'([^']*)'|"([^"]*)"|([^)]*?))\)@@/g;
|
|
28
28
|
/** Partial marker detection — we might be mid-stream in a marker */
|
|
29
29
|
const PARTIAL_MARKER_RE = /@@[a-zA-Z][a-zA-Z0-9_-]*(?:\([^)]*)?$/;
|
|
30
|
+
/**
|
|
31
|
+
* Scan a string for markers, fire the handler for each, and return cleaned text.
|
|
32
|
+
* Unlike the streaming parser, this operates on a complete string (e.g. tool call arguments).
|
|
33
|
+
*/
|
|
34
|
+
export function extractMarkers(text, onMarker) {
|
|
35
|
+
let cleaned = "";
|
|
36
|
+
let lastIndex = 0;
|
|
37
|
+
const regex = new RegExp(MARKER_RE.source, "g");
|
|
38
|
+
let match;
|
|
39
|
+
while ((match = regex.exec(text)) !== null) {
|
|
40
|
+
cleaned += text.slice(lastIndex, match.index);
|
|
41
|
+
const marker = {
|
|
42
|
+
name: match[1],
|
|
43
|
+
arg: match[2] ?? match[3] ?? match[4] ?? "",
|
|
44
|
+
raw: match[0],
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
onMarker(marker);
|
|
48
|
+
}
|
|
49
|
+
catch { /* handled by caller */ }
|
|
50
|
+
lastIndex = match.index + match[0].length;
|
|
51
|
+
}
|
|
52
|
+
cleaned += text.slice(lastIndex);
|
|
53
|
+
return cleaned;
|
|
54
|
+
}
|
|
30
55
|
export function createMarkerParser(opts) {
|
|
31
56
|
const { onMarker, onToken } = opts;
|
|
32
57
|
let buffer = "";
|