@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 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(marker) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tjamescouch/gro",
3
- "version": "1.3.10",
3
+ "version": "1.3.11",
4
4
  "description": "Provider-agnostic LLM runtime with context management",
5
5
  "bin": {
6
6
  "gro": "./dist/main.js"
@@ -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 = "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tjamescouch/gro",
3
- "version": "1.3.10",
3
+ "version": "1.3.11",
4
4
  "description": "Provider-agnostic LLM runtime with context management",
5
5
  "bin": {
6
6
  "gro": "./dist/main.js"