@kenkaiiii/gg-agent 5.67.1 → 5.68.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/index.cjs +217 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -4
- package/dist/index.d.ts +112 -4
- package/dist/index.js +211 -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();
|
|
@@ -599,6 +727,7 @@ async function* agentLoop(messages, options) {
|
|
|
599
727
|
transportSessionId: options.transportSessionId,
|
|
600
728
|
projectId: liveProjectId,
|
|
601
729
|
cacheRetention: options.cacheRetention,
|
|
730
|
+
onContextPrepared: options.onContextPrepared,
|
|
602
731
|
promptCacheKey: options.promptCacheKey,
|
|
603
732
|
serviceTier: options.serviceTier,
|
|
604
733
|
supportsImages: options.supportsImages,
|
|
@@ -672,8 +801,14 @@ async function* agentLoop(messages, options) {
|
|
|
672
801
|
}
|
|
673
802
|
if (event.type === "text_delta") {
|
|
674
803
|
attemptText += event.text;
|
|
804
|
+
streamRuleHit = streamRuleMonitor?.checkText(event.text) ?? null;
|
|
805
|
+
if (streamRuleHit) {
|
|
806
|
+
streamController.abort();
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
675
809
|
yield { type: "text_delta", text: event.text };
|
|
676
810
|
} else if (event.type === "thinking_delta") {
|
|
811
|
+
attemptThinkingChars += event.text.length;
|
|
677
812
|
yield { type: "thinking_delta", text: event.text };
|
|
678
813
|
} else if (event.type === "server_toolcall") {
|
|
679
814
|
yield {
|
|
@@ -693,6 +828,11 @@ async function* agentLoop(messages, options) {
|
|
|
693
828
|
const chunkChars = event.argsJson?.length ?? 0;
|
|
694
829
|
toolcallDeltaChars += chunkChars;
|
|
695
830
|
toolcallDeltaCount++;
|
|
831
|
+
streamRuleHit = streamRuleMonitor?.checkToolArgs(event.id, event.name, event.argsJson ?? "") ?? null;
|
|
832
|
+
if (streamRuleHit) {
|
|
833
|
+
streamController.abort();
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
696
836
|
if (!runawayDetected && (toolcallDeltaChars > MAX_TOOLCALL_DELTA_CHARS || toolcallDeltaCount > MAX_TOOLCALL_DELTA_EVENTS)) {
|
|
697
837
|
runawayDetected = {
|
|
698
838
|
kind: toolcallDeltaChars > MAX_TOOLCALL_DELTA_CHARS ? "chars" : "events",
|
|
@@ -714,6 +854,54 @@ async function* agentLoop(messages, options) {
|
|
|
714
854
|
lastYieldEndTime = Date.now();
|
|
715
855
|
resetIdleTimer();
|
|
716
856
|
}
|
|
857
|
+
if (streamRuleHit && streamRuleMonitor) {
|
|
858
|
+
const attempt = streamRuleMonitor.recordTrigger(streamRuleHit.rules);
|
|
859
|
+
const ruleNames = streamRuleHit.rules.map((rule) => rule.name);
|
|
860
|
+
const priorPrompt = latestProviderUsage ? latestProviderUsage.inputTokens + (latestProviderUsage.cacheRead ?? 0) + (latestProviderUsage.cacheWrite ?? 0) : 0;
|
|
861
|
+
const newChars = priorPrompt > 0 && usageAnchorIndex !== void 0 ? countMessageChars(messages.slice(usageAnchorIndex + 1)) : msgChars;
|
|
862
|
+
const abortedUsage = {
|
|
863
|
+
inputTokens: Math.ceil(newChars / 4),
|
|
864
|
+
outputTokens: Math.ceil(
|
|
865
|
+
(attemptText.length + attemptThinkingChars + toolcallDeltaChars) / 4
|
|
866
|
+
),
|
|
867
|
+
...priorPrompt > 0 ? { cacheRead: priorPrompt } : {}
|
|
868
|
+
};
|
|
869
|
+
totalUsage.inputTokens += abortedUsage.inputTokens;
|
|
870
|
+
totalUsage.outputTokens += abortedUsage.outputTokens;
|
|
871
|
+
if (abortedUsage.cacheRead) {
|
|
872
|
+
totalUsage.cacheRead = (totalUsage.cacheRead ?? 0) + abortedUsage.cacheRead;
|
|
873
|
+
}
|
|
874
|
+
diag("stream_rule_triggered", {
|
|
875
|
+
rules: ruleNames.join(","),
|
|
876
|
+
source: streamRuleHit.source,
|
|
877
|
+
toolName: streamRuleHit.toolName,
|
|
878
|
+
attempt,
|
|
879
|
+
maxAttempts: streamRuleMonitor.maxRetries,
|
|
880
|
+
discardedChars: attemptText.length + toolcallDeltaChars,
|
|
881
|
+
provider: options.provider,
|
|
882
|
+
model: options.model
|
|
883
|
+
});
|
|
884
|
+
messages.push(buildStreamRuleReminder(streamRuleHit.rules));
|
|
885
|
+
yield {
|
|
886
|
+
type: "stream_rule_triggered",
|
|
887
|
+
rules: ruleNames,
|
|
888
|
+
source: streamRuleHit.source,
|
|
889
|
+
...streamRuleHit.toolName !== void 0 ? { toolName: streamRuleHit.toolName } : {},
|
|
890
|
+
attempt,
|
|
891
|
+
maxAttempts: streamRuleMonitor.maxRetries,
|
|
892
|
+
usage: abortedUsage
|
|
893
|
+
};
|
|
894
|
+
yield {
|
|
895
|
+
type: "retry",
|
|
896
|
+
reason: "stream_rule",
|
|
897
|
+
attempt,
|
|
898
|
+
maxAttempts: streamRuleMonitor.maxRetries,
|
|
899
|
+
delayMs: 0,
|
|
900
|
+
silent: true
|
|
901
|
+
};
|
|
902
|
+
turn--;
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
717
905
|
diag("stream_done", {
|
|
718
906
|
events: streamEventCount,
|
|
719
907
|
totalMs: Date.now() - streamCallStart,
|
|
@@ -1760,6 +1948,19 @@ function sanitizeOrphanedServerTools(messages) {
|
|
|
1760
1948
|
break;
|
|
1761
1949
|
}
|
|
1762
1950
|
}
|
|
1951
|
+
function countMessageChars(messages) {
|
|
1952
|
+
let chars = 0;
|
|
1953
|
+
for (const m of messages) {
|
|
1954
|
+
if (typeof m.content === "string") chars += m.content.length;
|
|
1955
|
+
else if (Array.isArray(m.content)) {
|
|
1956
|
+
for (const p of m.content) {
|
|
1957
|
+
if ("text" in p && typeof p.text === "string") chars += p.text.length;
|
|
1958
|
+
if ("content" in p && typeof p.content === "string") chars += p.content.length;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
return chars;
|
|
1963
|
+
}
|
|
1763
1964
|
function repairToolPairingAdjacent(messages) {
|
|
1764
1965
|
for (let i = 0; i < messages.length; i++) {
|
|
1765
1966
|
const msg = messages[i];
|
|
@@ -1939,7 +2140,12 @@ var Agent = class {
|
|
|
1939
2140
|
0 && (module.exports = {
|
|
1940
2141
|
Agent,
|
|
1941
2142
|
AgentStream,
|
|
2143
|
+
DEFAULT_STREAM_RULE_MAX_RETRIES,
|
|
2144
|
+
DEFAULT_STREAM_RULE_WINDOW_CHARS,
|
|
2145
|
+
JsonEscapeDecoder,
|
|
2146
|
+
StreamRuleMonitor,
|
|
1942
2147
|
agentLoop,
|
|
2148
|
+
buildStreamRuleReminder,
|
|
1943
2149
|
cancelledBeforeStartText,
|
|
1944
2150
|
indeterminateOutcomeText,
|
|
1945
2151
|
isAbortError,
|
|
@@ -1947,6 +2153,7 @@ var Agent = class {
|
|
|
1947
2153
|
isContextOverflow,
|
|
1948
2154
|
isLocalBackendUrl,
|
|
1949
2155
|
isUsageLimitError,
|
|
2156
|
+
repairToolPairingAdjacent,
|
|
1950
2157
|
setStreamDiagnostic
|
|
1951
2158
|
});
|
|
1952
2159
|
//# sourceMappingURL=index.cjs.map
|