@sayknow-cli/agent-core 0.3.2 → 0.3.4

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.7.7] - 2026-06-28
6
+
7
+ ### Fixed
8
+
9
+ - Mitigate leaked Anthropic-style `<invoke name="…">` tool-call envelopes across providers, not only `openai-codex`, while keeping Codex `to=functions.*` harmony-header mitigation provider-scoped.
10
+
5
11
  ## [0.7.4] - 2026-06-27
6
12
 
7
13
  ### Added
@@ -48,6 +48,7 @@ export interface HarmonyRecoveredToolCall {
48
48
  * itself is cheap; the cost of missing a leak on a new model is not.
49
49
  */
50
50
  export declare function isHarmonyLeakMitigationTarget(model: Model): boolean;
51
+ export declare function shouldMitigateHarmonyLeak(model: Model, detection: HarmonyDetection): boolean;
51
52
  export declare function signalListLabel(signals: readonly HarmonySignal[]): string;
52
53
  /**
53
54
  * Detect harmony-protocol leakage in `text`. Returns undefined if clean.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@sayknow-cli/agent-core",
4
- "version": "0.3.2",
4
+ "version": "0.3.4",
5
5
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
6
  "homepage": "https://github.com/jaybeyond/Sayknow_CLI",
7
7
  "author": "jaybeyond",
@@ -35,9 +35,9 @@
35
35
  "fmt": "biome format --write ."
36
36
  },
37
37
  "dependencies": {
38
- "@sayknow-cli/ai": "0.3.2",
39
- "@sayknow-cli/natives": "0.3.2",
40
- "@sayknow-cli/utils": "0.3.2",
38
+ "@sayknow-cli/ai": "0.3.4",
39
+ "@sayknow-cli/natives": "0.3.4",
40
+ "@sayknow-cli/utils": "0.3.4",
41
41
  "@opentelemetry/api": "^1.9.0"
42
42
  },
43
43
  "devDependencies": {
package/src/agent-loop.ts CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  type HarmonyRecoveredToolCall,
25
25
  isHarmonyLeakMitigationTarget,
26
26
  recoverHarmonyToolCall,
27
+ shouldMitigateHarmonyLeak,
27
28
  signalListLabel,
28
29
  } from "./harmony-leak";
29
30
  import { type AgentRunCoverage, type AgentRunSummary, ToolCallBlockedError } from "./run-collector";
@@ -515,19 +516,11 @@ async function runLoopBody(
515
516
  streamFn,
516
517
  harmonyRetryAttempt,
517
518
  );
518
- // Post-stream harmony-leak detection. The mitigation scaffolding
519
- // below (HarmonyLeakInterruption catch + retry/recover/audit,
520
- // plus harmonyAbortController) existed but nothing invoked the
521
- // detector, so leaks on openai-codex models were never caught.
522
- // Detect on the completed message and route recoverable tool-arg
523
- // leaks through recovery, everything else through abort-retry.
524
- if (isHarmonyLeakMitigationTarget(config.model)) {
525
- const detection = detectHarmonyLeakInAssistantMessage(message);
526
- if (detection) {
527
- const rec = recoverHarmonyToolCall(message, detection);
528
- const removed = rec ? rec.removed : extractHarmonyRemoved(message, detection);
529
- throw new HarmonyLeakInterruption(detection, removed, rec);
530
- }
519
+ const detection = detectHarmonyLeakInAssistantMessage(message);
520
+ if (detection && shouldMitigateHarmonyLeak(config.model, detection)) {
521
+ const rec = recoverHarmonyToolCall(message, detection);
522
+ const removed = rec ? rec.removed : extractHarmonyRemoved(message, detection);
523
+ throw new HarmonyLeakInterruption(detection, removed, rec);
531
524
  }
532
525
  harmonyRetryAttempt = 0;
533
526
  harmonyTruncateResumeCount = 0;
@@ -127,6 +127,11 @@ export function isHarmonyLeakMitigationTarget(model: Model): boolean {
127
127
  return model.provider === "openai-codex";
128
128
  }
129
129
 
130
+ export function shouldMitigateHarmonyLeak(model: Model, detection: HarmonyDetection): boolean {
131
+ if (isHarmonyLeakMitigationTarget(model)) return true;
132
+ return detection.signals.some(signal => signal.classes.includes("I"));
133
+ }
134
+
130
135
  export function signalListLabel(signals: readonly HarmonySignal[]): string {
131
136
  const seen: string[] = [];
132
137
  for (const signal of signals) {