@kenkaiiii/gg-agent 5.67.0 → 5.68.0
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/index.cjs +216 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +111 -4
- package/dist/index.d.ts +111 -4
- package/dist/index.js +210 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -22,7 +22,12 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Agent: () => Agent,
|
|
24
24
|
AgentStream: () => AgentStream,
|
|
25
|
+
DEFAULT_STREAM_RULE_MAX_RETRIES: () => DEFAULT_STREAM_RULE_MAX_RETRIES,
|
|
26
|
+
DEFAULT_STREAM_RULE_WINDOW_CHARS: () => DEFAULT_STREAM_RULE_WINDOW_CHARS,
|
|
27
|
+
JsonEscapeDecoder: () => JsonEscapeDecoder,
|
|
28
|
+
StreamRuleMonitor: () => StreamRuleMonitor,
|
|
25
29
|
agentLoop: () => agentLoop,
|
|
30
|
+
buildStreamRuleReminder: () => buildStreamRuleReminder,
|
|
26
31
|
cancelledBeforeStartText: () => cancelledBeforeStartText,
|
|
27
32
|
indeterminateOutcomeText: () => indeterminateOutcomeText,
|
|
28
33
|
isAbortError: () => isAbortError,
|
|
@@ -30,6 +35,7 @@ __export(index_exports, {
|
|
|
30
35
|
isContextOverflow: () => isContextOverflow,
|
|
31
36
|
isLocalBackendUrl: () => isLocalBackendUrl,
|
|
32
37
|
isUsageLimitError: () => isUsageLimitError,
|
|
38
|
+
repairToolPairingAdjacent: () => repairToolPairingAdjacent,
|
|
33
39
|
setStreamDiagnostic: () => setStreamDiagnostic
|
|
34
40
|
});
|
|
35
41
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -117,6 +123,133 @@ function clampOutputTokens(key, requested) {
|
|
|
117
123
|
return requested === void 0 ? ceiling : Math.min(requested, ceiling);
|
|
118
124
|
}
|
|
119
125
|
|
|
126
|
+
// src/stream-rules.ts
|
|
127
|
+
var DEFAULT_STREAM_RULE_MAX_RETRIES = 3;
|
|
128
|
+
var DEFAULT_STREAM_RULE_WINDOW_CHARS = 1024;
|
|
129
|
+
var JSON_ESCAPES = {
|
|
130
|
+
n: "\n",
|
|
131
|
+
r: "\r",
|
|
132
|
+
t: " ",
|
|
133
|
+
b: "\b",
|
|
134
|
+
f: "\f",
|
|
135
|
+
'"': '"',
|
|
136
|
+
"\\": "\\",
|
|
137
|
+
"/": "/"
|
|
138
|
+
};
|
|
139
|
+
var JsonEscapeDecoder = class {
|
|
140
|
+
pending = "";
|
|
141
|
+
push(chunk) {
|
|
142
|
+
const input = this.pending + chunk;
|
|
143
|
+
this.pending = "";
|
|
144
|
+
if (!input.includes("\\")) return input;
|
|
145
|
+
let out = "";
|
|
146
|
+
let i = 0;
|
|
147
|
+
while (i < input.length) {
|
|
148
|
+
const ch = input.charAt(i);
|
|
149
|
+
if (ch !== "\\") {
|
|
150
|
+
out += ch;
|
|
151
|
+
i++;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (i + 1 >= input.length) {
|
|
155
|
+
this.pending = input.slice(i);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
const next = input.charAt(i + 1);
|
|
159
|
+
if (next === "u") {
|
|
160
|
+
if (i + 6 > input.length) {
|
|
161
|
+
this.pending = input.slice(i);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
const hex = input.slice(i + 2, i + 6);
|
|
165
|
+
out += /^[0-9a-fA-F]{4}$/.test(hex) ? String.fromCharCode(Number.parseInt(hex, 16)) : input.slice(i, i + 6);
|
|
166
|
+
i += 6;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
out += JSON_ESCAPES[next] ?? next;
|
|
170
|
+
i += 2;
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
var StreamRuleMonitor = class {
|
|
176
|
+
maxRetries;
|
|
177
|
+
rules;
|
|
178
|
+
windowChars;
|
|
179
|
+
fired = /* @__PURE__ */ new Set();
|
|
180
|
+
retries = 0;
|
|
181
|
+
textWindow = "";
|
|
182
|
+
toolStreams = /* @__PURE__ */ new Map();
|
|
183
|
+
constructor(config) {
|
|
184
|
+
this.rules = config.rules;
|
|
185
|
+
this.maxRetries = Math.max(0, config.maxRetries ?? DEFAULT_STREAM_RULE_MAX_RETRIES);
|
|
186
|
+
this.windowChars = Math.max(64, config.windowChars ?? DEFAULT_STREAM_RULE_WINDOW_CHARS);
|
|
187
|
+
}
|
|
188
|
+
/** False once the retry cap is spent or every rule has fired — skip all matching. */
|
|
189
|
+
get active() {
|
|
190
|
+
return this.retries < this.maxRetries && this.fired.size < this.rules.length;
|
|
191
|
+
}
|
|
192
|
+
get retriesUsed() {
|
|
193
|
+
return this.retries;
|
|
194
|
+
}
|
|
195
|
+
beginAttempt() {
|
|
196
|
+
this.textWindow = "";
|
|
197
|
+
this.toolStreams.clear();
|
|
198
|
+
}
|
|
199
|
+
checkText(delta) {
|
|
200
|
+
if (!this.active || !delta) return null;
|
|
201
|
+
const scan = this.textWindow + delta;
|
|
202
|
+
this.textWindow = scan.slice(-this.windowChars);
|
|
203
|
+
const rules = this.match(scan, "text");
|
|
204
|
+
return rules.length > 0 ? { rules, source: "text" } : null;
|
|
205
|
+
}
|
|
206
|
+
checkToolArgs(id, toolName, argsDelta) {
|
|
207
|
+
if (!this.active || !argsDelta) return null;
|
|
208
|
+
let state = this.toolStreams.get(id);
|
|
209
|
+
if (!state) {
|
|
210
|
+
state = { window: "", decoder: new JsonEscapeDecoder() };
|
|
211
|
+
this.toolStreams.set(id, state);
|
|
212
|
+
}
|
|
213
|
+
const scan = state.window + state.decoder.push(argsDelta);
|
|
214
|
+
state.window = scan.slice(-this.windowChars);
|
|
215
|
+
const rules = this.match(scan, "tool", toolName);
|
|
216
|
+
return rules.length > 0 ? { rules, source: "tool", toolName } : null;
|
|
217
|
+
}
|
|
218
|
+
/** Consume one retry and mark the rules fired. Returns the retry ordinal (1-based). */
|
|
219
|
+
recordTrigger(rules) {
|
|
220
|
+
for (const rule of rules) this.fired.add(rule.name);
|
|
221
|
+
this.retries++;
|
|
222
|
+
return this.retries;
|
|
223
|
+
}
|
|
224
|
+
match(scan, source, toolName) {
|
|
225
|
+
const hits = [];
|
|
226
|
+
for (const rule of this.rules) {
|
|
227
|
+
if (this.fired.has(rule.name)) continue;
|
|
228
|
+
if (rule.scope !== "both" && rule.scope !== source) continue;
|
|
229
|
+
if (source === "tool" && rule.tools && toolName !== void 0) {
|
|
230
|
+
if (!rule.tools.includes(toolName)) continue;
|
|
231
|
+
}
|
|
232
|
+
rule.pattern.lastIndex = 0;
|
|
233
|
+
if (rule.pattern.test(scan)) hits.push(rule);
|
|
234
|
+
}
|
|
235
|
+
return hits;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
function buildStreamRuleReminder(rules) {
|
|
239
|
+
const blocks = rules.map(
|
|
240
|
+
(rule) => `<system-reminder reason="stream_rule" rule="${rule.name.replace(/"/g, "'")}">
|
|
241
|
+
Your previous response was interrupted mid-stream because it violated this rule. The interrupted output was discarded: the user never saw it and no tool call from it ran. Regenerate the response from the start and follow the rule:
|
|
242
|
+
|
|
243
|
+
${rule.reminder.trim()}
|
|
244
|
+
</system-reminder>`
|
|
245
|
+
);
|
|
246
|
+
return {
|
|
247
|
+
role: "user",
|
|
248
|
+
content: blocks.join("\n\n"),
|
|
249
|
+
provenance: { source: "runtime", kind: "notification", visibility: "hidden" }
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
120
253
|
// src/agent-loop.ts
|
|
121
254
|
var DEFAULT_MAX_TURNS = 300;
|
|
122
255
|
var DEFAULT_TOOL_TIMEOUT_MS = 3e5;
|
|
@@ -382,6 +515,7 @@ async function* agentLoop(messages, options) {
|
|
|
382
515
|
let runawayToolcallRetries = 0;
|
|
383
516
|
let overflowCompactionAttempts = 0;
|
|
384
517
|
let toolResultTruncationAttempted = false;
|
|
518
|
+
const streamRuleMonitor = options.streamRules && options.streamRules.rules.length > 0 ? new StreamRuleMonitor(options.streamRules) : null;
|
|
385
519
|
const invalidToolArgumentCounts = /* @__PURE__ */ new Map();
|
|
386
520
|
let toolArgumentAutoContinueUsed = false;
|
|
387
521
|
let useNonStreamingFallback = false;
|
|
@@ -435,16 +569,7 @@ async function* agentLoop(messages, options) {
|
|
|
435
569
|
turn++;
|
|
436
570
|
if (logicalTurnStartedAt === 0) logicalTurnStartedAt = Date.now();
|
|
437
571
|
toolMap = new Map((options.tools ?? []).map((t) => [t.name, t]));
|
|
438
|
-
|
|
439
|
-
for (const m of messages) {
|
|
440
|
-
if (typeof m.content === "string") msgChars += m.content.length;
|
|
441
|
-
else if (Array.isArray(m.content)) {
|
|
442
|
-
for (const p of m.content) {
|
|
443
|
-
if ("text" in p && typeof p.text === "string") msgChars += p.text.length;
|
|
444
|
-
if ("content" in p && typeof p.content === "string") msgChars += p.content.length;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
572
|
+
const msgChars = countMessageChars(messages);
|
|
448
573
|
let firstEventTimeoutMs;
|
|
449
574
|
let initialHardTimeoutMs;
|
|
450
575
|
if (baseFirstEventTimeoutMs === STREAM_FIRST_EVENT_TIMEOUT_MS) {
|
|
@@ -514,6 +639,9 @@ async function* agentLoop(messages, options) {
|
|
|
514
639
|
let toolcallDeltaCount = 0;
|
|
515
640
|
let runawayDetected = null;
|
|
516
641
|
let attemptText = "";
|
|
642
|
+
let streamRuleHit = null;
|
|
643
|
+
let attemptThinkingChars = 0;
|
|
644
|
+
streamRuleMonitor?.beginAttempt();
|
|
517
645
|
let lastYieldEndTime = Date.now();
|
|
518
646
|
let maxConsumerLagMs = 0;
|
|
519
647
|
const forwardAbort = () => streamController.abort();
|
|
@@ -672,8 +800,14 @@ async function* agentLoop(messages, options) {
|
|
|
672
800
|
}
|
|
673
801
|
if (event.type === "text_delta") {
|
|
674
802
|
attemptText += event.text;
|
|
803
|
+
streamRuleHit = streamRuleMonitor?.checkText(event.text) ?? null;
|
|
804
|
+
if (streamRuleHit) {
|
|
805
|
+
streamController.abort();
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
675
808
|
yield { type: "text_delta", text: event.text };
|
|
676
809
|
} else if (event.type === "thinking_delta") {
|
|
810
|
+
attemptThinkingChars += event.text.length;
|
|
677
811
|
yield { type: "thinking_delta", text: event.text };
|
|
678
812
|
} else if (event.type === "server_toolcall") {
|
|
679
813
|
yield {
|
|
@@ -693,6 +827,11 @@ async function* agentLoop(messages, options) {
|
|
|
693
827
|
const chunkChars = event.argsJson?.length ?? 0;
|
|
694
828
|
toolcallDeltaChars += chunkChars;
|
|
695
829
|
toolcallDeltaCount++;
|
|
830
|
+
streamRuleHit = streamRuleMonitor?.checkToolArgs(event.id, event.name, event.argsJson ?? "") ?? null;
|
|
831
|
+
if (streamRuleHit) {
|
|
832
|
+
streamController.abort();
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
696
835
|
if (!runawayDetected && (toolcallDeltaChars > MAX_TOOLCALL_DELTA_CHARS || toolcallDeltaCount > MAX_TOOLCALL_DELTA_EVENTS)) {
|
|
697
836
|
runawayDetected = {
|
|
698
837
|
kind: toolcallDeltaChars > MAX_TOOLCALL_DELTA_CHARS ? "chars" : "events",
|
|
@@ -714,6 +853,54 @@ async function* agentLoop(messages, options) {
|
|
|
714
853
|
lastYieldEndTime = Date.now();
|
|
715
854
|
resetIdleTimer();
|
|
716
855
|
}
|
|
856
|
+
if (streamRuleHit && streamRuleMonitor) {
|
|
857
|
+
const attempt = streamRuleMonitor.recordTrigger(streamRuleHit.rules);
|
|
858
|
+
const ruleNames = streamRuleHit.rules.map((rule) => rule.name);
|
|
859
|
+
const priorPrompt = latestProviderUsage ? latestProviderUsage.inputTokens + (latestProviderUsage.cacheRead ?? 0) + (latestProviderUsage.cacheWrite ?? 0) : 0;
|
|
860
|
+
const newChars = priorPrompt > 0 && usageAnchorIndex !== void 0 ? countMessageChars(messages.slice(usageAnchorIndex + 1)) : msgChars;
|
|
861
|
+
const abortedUsage = {
|
|
862
|
+
inputTokens: Math.ceil(newChars / 4),
|
|
863
|
+
outputTokens: Math.ceil(
|
|
864
|
+
(attemptText.length + attemptThinkingChars + toolcallDeltaChars) / 4
|
|
865
|
+
),
|
|
866
|
+
...priorPrompt > 0 ? { cacheRead: priorPrompt } : {}
|
|
867
|
+
};
|
|
868
|
+
totalUsage.inputTokens += abortedUsage.inputTokens;
|
|
869
|
+
totalUsage.outputTokens += abortedUsage.outputTokens;
|
|
870
|
+
if (abortedUsage.cacheRead) {
|
|
871
|
+
totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + abortedUsage.cacheRead;
|
|
872
|
+
}
|
|
873
|
+
diag("stream_rule_triggered", {
|
|
874
|
+
rules: ruleNames.join(","),
|
|
875
|
+
source: streamRuleHit.source,
|
|
876
|
+
toolName: streamRuleHit.toolName,
|
|
877
|
+
attempt,
|
|
878
|
+
maxAttempts: streamRuleMonitor.maxRetries,
|
|
879
|
+
discardedChars: attemptText.length + toolcallDeltaChars,
|
|
880
|
+
provider: options.provider,
|
|
881
|
+
model: options.model
|
|
882
|
+
});
|
|
883
|
+
messages.push(buildStreamRuleReminder(streamRuleHit.rules));
|
|
884
|
+
yield {
|
|
885
|
+
type: "stream_rule_triggered",
|
|
886
|
+
rules: ruleNames,
|
|
887
|
+
source: streamRuleHit.source,
|
|
888
|
+
...streamRuleHit.toolName !== void 0 ? { toolName: streamRuleHit.toolName } : {},
|
|
889
|
+
attempt,
|
|
890
|
+
maxAttempts: streamRuleMonitor.maxRetries,
|
|
891
|
+
usage: abortedUsage
|
|
892
|
+
};
|
|
893
|
+
yield {
|
|
894
|
+
type: "retry",
|
|
895
|
+
reason: "stream_rule",
|
|
896
|
+
attempt,
|
|
897
|
+
maxAttempts: streamRuleMonitor.maxRetries,
|
|
898
|
+
delayMs: 0,
|
|
899
|
+
silent: true
|
|
900
|
+
};
|
|
901
|
+
turn--;
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
717
904
|
diag("stream_done", {
|
|
718
905
|
events: streamEventCount,
|
|
719
906
|
totalMs: Date.now() - streamCallStart,
|
|
@@ -1760,6 +1947,19 @@ function sanitizeOrphanedServerTools(messages) {
|
|
|
1760
1947
|
break;
|
|
1761
1948
|
}
|
|
1762
1949
|
}
|
|
1950
|
+
function countMessageChars(messages) {
|
|
1951
|
+
let chars = 0;
|
|
1952
|
+
for (const m of messages) {
|
|
1953
|
+
if (typeof m.content === "string") chars += m.content.length;
|
|
1954
|
+
else if (Array.isArray(m.content)) {
|
|
1955
|
+
for (const p of m.content) {
|
|
1956
|
+
if ("text" in p && typeof p.text === "string") chars += p.text.length;
|
|
1957
|
+
if ("content" in p && typeof p.content === "string") chars += p.content.length;
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
return chars;
|
|
1962
|
+
}
|
|
1763
1963
|
function repairToolPairingAdjacent(messages) {
|
|
1764
1964
|
for (let i = 0; i < messages.length; i++) {
|
|
1765
1965
|
const msg = messages[i];
|
|
@@ -1939,7 +2139,12 @@ var Agent = class {
|
|
|
1939
2139
|
0 && (module.exports = {
|
|
1940
2140
|
Agent,
|
|
1941
2141
|
AgentStream,
|
|
2142
|
+
DEFAULT_STREAM_RULE_MAX_RETRIES,
|
|
2143
|
+
DEFAULT_STREAM_RULE_WINDOW_CHARS,
|
|
2144
|
+
JsonEscapeDecoder,
|
|
2145
|
+
StreamRuleMonitor,
|
|
1942
2146
|
agentLoop,
|
|
2147
|
+
buildStreamRuleReminder,
|
|
1943
2148
|
cancelledBeforeStartText,
|
|
1944
2149
|
indeterminateOutcomeText,
|
|
1945
2150
|
isAbortError,
|
|
@@ -1947,6 +2152,7 @@ var Agent = class {
|
|
|
1947
2152
|
isContextOverflow,
|
|
1948
2153
|
isLocalBackendUrl,
|
|
1949
2154
|
isUsageLimitError,
|
|
2155
|
+
repairToolPairingAdjacent,
|
|
1950
2156
|
setStreamDiagnostic
|
|
1951
2157
|
});
|
|
1952
2158
|
//# sourceMappingURL=index.cjs.map
|