@caupulican/pi-agent-core 0.91.2 → 0.91.3

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.
@@ -3,10 +3,19 @@ import type { AssistantMessage } from "@caupulican/pi-ai/types";
3
3
  export declare const DEGENERATE_REPEATED_LINE_MIN = 4;
4
4
  /** Mid-stream abort once a generation loop is already this long; cheaper than waiting for `done`. */
5
5
  export declare const DEGENERATE_STREAM_ABORT_RUN = 8;
6
+ /** Shortest unit that may be an exact tiled status sentence (`Foo.Foo.` with no space). */
7
+ export declare const DEGENERATE_EXACT_REPEAT_MIN = 24;
8
+ /** Smallest power-of-two prose tile that repeats to `text` (`Foo.Foo.` / `Foo.`×4). */
9
+ export declare function exactTiledRepeat(text: string): {
10
+ unit: string;
11
+ times: number;
12
+ } | undefined;
6
13
  /**
7
14
  * Collapse consecutive identical assistant lines, period-2 ABAB line runs, and the same
8
15
  * sentence repeated inside one paragraph. Session 01a0016f stored 551 copies of one
9
16
  * sentence as a single line; newline-only collapse cannot see that.
17
+ * Session 01a00337 stored the same status sentence concatenated with no space (`Foo.Foo.`)
18
+ * because Responses deltas replayed the full sentence; whitespace split cannot see that.
10
19
  */
11
20
  export declare function collapseRepeatedLines(text: string): string;
12
21
  export declare function isDegenerateRepeatedText(text: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"degenerate-assistant-text.d.ts","sourceRoot":"","sources":["../src/degenerate-assistant-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,8GAA8G;AAC9G,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,qGAAqG;AACrG,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAyD7C;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKjE;AAID,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAa9F;AAED,2EAA2E;AAC3E,wBAAgB,qCAAqC,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAExF;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAKtE"}
1
+ {"version":3,"file":"degenerate-assistant-text.d.ts","sourceRoot":"","sources":["../src/degenerate-assistant-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,8GAA8G;AAC9G,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,qGAAqG;AACrG,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,2FAA2F;AAC3F,eAAO,MAAM,2BAA2B,KAAK,CAAC;AA6D9C,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAY1F;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW1D;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOjE;AAID,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CAsB9F;AAED,2EAA2E;AAC3E,wBAAgB,qCAAqC,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAExF;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAKtE"}
@@ -2,6 +2,8 @@
2
2
  export const DEGENERATE_REPEATED_LINE_MIN = 4;
3
3
  /** Mid-stream abort once a generation loop is already this long; cheaper than waiting for `done`. */
4
4
  export const DEGENERATE_STREAM_ABORT_RUN = 8;
5
+ /** Shortest unit that may be an exact tiled status sentence (`Foo.Foo.` with no space). */
6
+ export const DEGENERATE_EXACT_REPEAT_MIN = 24;
5
7
  function collapseRepeatedUnits(units, joinWith) {
6
8
  if (units.length < DEGENERATE_REPEATED_LINE_MIN)
7
9
  return units.join(joinWith);
@@ -56,16 +58,42 @@ function maxRepeatedUnitRun(units) {
56
58
  function splitSentences(text) {
57
59
  return text.split(/(?<=[.!?])\s+/);
58
60
  }
61
+ function isProseTile(unit) {
62
+ return /[.!?]/.test(unit) || /\s/.test(unit);
63
+ }
64
+ /** Smallest power-of-two prose tile that repeats to `text` (`Foo.Foo.` / `Foo.`×4). */
65
+ export function exactTiledRepeat(text) {
66
+ if (text.length < DEGENERATE_EXACT_REPEAT_MIN * 2)
67
+ return undefined;
68
+ let unit = text;
69
+ let times = 1;
70
+ while (unit.length >= DEGENERATE_EXACT_REPEAT_MIN * 2 && unit.length % 2 === 0) {
71
+ const half = unit.length / 2;
72
+ const next = unit.slice(0, half);
73
+ if (!isProseTile(next) || next !== unit.slice(half))
74
+ break;
75
+ unit = next;
76
+ times *= 2;
77
+ }
78
+ return times > 1 ? { unit, times } : undefined;
79
+ }
59
80
  /**
60
81
  * Collapse consecutive identical assistant lines, period-2 ABAB line runs, and the same
61
82
  * sentence repeated inside one paragraph. Session 01a0016f stored 551 copies of one
62
83
  * sentence as a single line; newline-only collapse cannot see that.
84
+ * Session 01a00337 stored the same status sentence concatenated with no space (`Foo.Foo.`)
85
+ * because Responses deltas replayed the full sentence; whitespace split cannot see that.
63
86
  */
64
87
  export function collapseRepeatedLines(text) {
65
- const lineCollapsed = collapseRepeatedUnits(text.split("\n"), "\n");
88
+ const tiled = exactTiledRepeat(text);
89
+ const source = tiled ? tiled.unit : text;
90
+ const lineCollapsed = collapseRepeatedUnits(source.split("\n"), "\n");
66
91
  return lineCollapsed
67
92
  .split("\n")
68
- .map((line) => collapseRepeatedUnits(splitSentences(line), " "))
93
+ .map((line) => {
94
+ const lineTile = exactTiledRepeat(line);
95
+ return collapseRepeatedUnits(splitSentences(lineTile ? lineTile.unit : line), " ");
96
+ })
69
97
  .join("\n");
70
98
  }
71
99
  export function isDegenerateRepeatedText(text) {
@@ -74,6 +102,9 @@ export function isDegenerateRepeatedText(text) {
74
102
  export function shouldAbortDegenerateStream(text) {
75
103
  if (text.length < 400)
76
104
  return false;
105
+ const tiled = exactTiledRepeat(text);
106
+ if (tiled && tiled.times >= DEGENERATE_STREAM_ABORT_RUN)
107
+ return true;
77
108
  const sentences = splitSentences(text);
78
109
  if (maxRepeatedUnitRun(sentences) >= DEGENERATE_STREAM_ABORT_RUN)
79
110
  return true;
@@ -82,15 +113,24 @@ export function shouldAbortDegenerateStream(text) {
82
113
  const collapsedDegenerateMessages = new WeakSet();
83
114
  export function collapseDegenerateAssistantMessage(message) {
84
115
  let changed = false;
85
- const content = message.content.map((block) => {
86
- if (block.type !== "text")
87
- return block;
116
+ const seenText = new Set();
117
+ const content = [];
118
+ for (const block of message.content) {
119
+ if (block.type !== "text") {
120
+ content.push(block);
121
+ continue;
122
+ }
88
123
  const collapsed = collapseRepeatedLines(block.text);
89
- if (collapsed === block.text)
90
- return block;
91
- changed = true;
92
- return { ...block, text: collapsed };
93
- });
124
+ if (collapsed !== block.text)
125
+ changed = true;
126
+ if (collapsed.trim() !== "" && seenText.has(collapsed)) {
127
+ changed = true;
128
+ continue;
129
+ }
130
+ if (collapsed.trim() !== "")
131
+ seenText.add(collapsed);
132
+ content.push(collapsed === block.text ? block : { ...block, text: collapsed });
133
+ }
94
134
  if (!changed)
95
135
  return message;
96
136
  const next = { ...message, content };
@@ -1 +1 @@
1
- {"version":3,"file":"degenerate-assistant-text.js","sourceRoot":"","sources":["../src/degenerate-assistant-text.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@caupulican/pi-ai/types\";\n\n/** Consecutive identical non-empty units (lines or sentences) at or above this run count are degeneration. */\nexport const DEGENERATE_REPEATED_LINE_MIN = 4;\n\n/** Mid-stream abort once a generation loop is already this long; cheaper than waiting for `done`. */\nexport const DEGENERATE_STREAM_ABORT_RUN = 8;\n\nfunction collapseRepeatedUnits(units: readonly string[], joinWith: string): string {\n\tif (units.length < DEGENERATE_REPEATED_LINE_MIN) return units.join(joinWith);\n\tconst out: string[] = [];\n\tlet index = 0;\n\twhile (index < units.length) {\n\t\tconst unit = units[index];\n\t\tlet run = 1;\n\t\twhile (index + run < units.length && units[index + run] === unit) run++;\n\t\tif (unit.trim() !== \"\" && run >= DEGENERATE_REPEATED_LINE_MIN) {\n\t\t\tout.push(unit);\n\t\t\tindex += run;\n\t\t\tcontinue;\n\t\t}\n\t\tif (index + 1 < units.length) {\n\t\t\tconst next = units[index + 1];\n\t\t\tif (unit.trim() !== \"\" && next.trim() !== \"\" && unit !== next) {\n\t\t\t\tlet pairs = 1;\n\t\t\t\twhile (\n\t\t\t\t\tindex + (pairs + 1) * 2 <= units.length &&\n\t\t\t\t\tunits[index + pairs * 2] === unit &&\n\t\t\t\t\tunits[index + pairs * 2 + 1] === next\n\t\t\t\t) {\n\t\t\t\t\tpairs++;\n\t\t\t\t}\n\t\t\t\tif (pairs >= DEGENERATE_REPEATED_LINE_MIN) {\n\t\t\t\t\tout.push(unit, next);\n\t\t\t\t\tindex += pairs * 2;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tout.push(unit);\n\t\tindex++;\n\t}\n\treturn out.join(joinWith);\n}\n\nfunction maxRepeatedUnitRun(units: readonly string[]): number {\n\tlet maxRun = 1;\n\tlet run = 1;\n\tfor (let index = 1; index < units.length; index++) {\n\t\tif (units[index] === units[index - 1] && units[index].trim() !== \"\") {\n\t\t\trun++;\n\t\t\tif (run > maxRun) maxRun = run;\n\t\t} else {\n\t\t\trun = 1;\n\t\t}\n\t}\n\treturn units.length === 0 ? 0 : maxRun;\n}\n\nfunction splitSentences(text: string): string[] {\n\treturn text.split(/(?<=[.!?])\\s+/);\n}\n\n/**\n * Collapse consecutive identical assistant lines, period-2 ABAB line runs, and the same\n * sentence repeated inside one paragraph. Session 01a0016f stored 551 copies of one\n * sentence as a single line; newline-only collapse cannot see that.\n */\nexport function collapseRepeatedLines(text: string): string {\n\tconst lineCollapsed = collapseRepeatedUnits(text.split(\"\\n\"), \"\\n\");\n\treturn lineCollapsed\n\t\t.split(\"\\n\")\n\t\t.map((line) => collapseRepeatedUnits(splitSentences(line), \" \"))\n\t\t.join(\"\\n\");\n}\n\nexport function isDegenerateRepeatedText(text: string): boolean {\n\treturn collapseRepeatedLines(text) !== text;\n}\n\nexport function shouldAbortDegenerateStream(text: string): boolean {\n\tif (text.length < 400) return false;\n\tconst sentences = splitSentences(text);\n\tif (maxRepeatedUnitRun(sentences) >= DEGENERATE_STREAM_ABORT_RUN) return true;\n\treturn maxRepeatedUnitRun(text.split(\"\\n\")) >= DEGENERATE_STREAM_ABORT_RUN;\n}\n\nconst collapsedDegenerateMessages = new WeakSet<AssistantMessage>();\n\nexport function collapseDegenerateAssistantMessage(message: AssistantMessage): AssistantMessage {\n\tlet changed = false;\n\tconst content = message.content.map((block) => {\n\t\tif (block.type !== \"text\") return block;\n\t\tconst collapsed = collapseRepeatedLines(block.text);\n\t\tif (collapsed === block.text) return block;\n\t\tchanged = true;\n\t\treturn { ...block, text: collapsed };\n\t});\n\tif (!changed) return message;\n\tconst next = { ...message, content };\n\tcollapsedDegenerateMessages.add(next);\n\treturn next;\n}\n\n/** True when this message is (or was collapsed from) a generation loop. */\nexport function isCollapsedDegenerateAssistantMessage(message: AssistantMessage): boolean {\n\treturn collapsedDegenerateMessages.has(message);\n}\n\nexport function assistantMessageText(message: AssistantMessage): string {\n\treturn message.content\n\t\t.filter((block) => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"degenerate-assistant-text.js","sourceRoot":"","sources":["../src/degenerate-assistant-text.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@caupulican/pi-ai/types\";\n\n/** Consecutive identical non-empty units (lines or sentences) at or above this run count are degeneration. */\nexport const DEGENERATE_REPEATED_LINE_MIN = 4;\n\n/** Mid-stream abort once a generation loop is already this long; cheaper than waiting for `done`. */\nexport const DEGENERATE_STREAM_ABORT_RUN = 8;\n\n/** Shortest unit that may be an exact tiled status sentence (`Foo.Foo.` with no space). */\nexport const DEGENERATE_EXACT_REPEAT_MIN = 24;\n\nfunction collapseRepeatedUnits(units: readonly string[], joinWith: string): string {\n\tif (units.length < DEGENERATE_REPEATED_LINE_MIN) return units.join(joinWith);\n\tconst out: string[] = [];\n\tlet index = 0;\n\twhile (index < units.length) {\n\t\tconst unit = units[index];\n\t\tlet run = 1;\n\t\twhile (index + run < units.length && units[index + run] === unit) run++;\n\t\tif (unit.trim() !== \"\" && run >= DEGENERATE_REPEATED_LINE_MIN) {\n\t\t\tout.push(unit);\n\t\t\tindex += run;\n\t\t\tcontinue;\n\t\t}\n\t\tif (index + 1 < units.length) {\n\t\t\tconst next = units[index + 1];\n\t\t\tif (unit.trim() !== \"\" && next.trim() !== \"\" && unit !== next) {\n\t\t\t\tlet pairs = 1;\n\t\t\t\twhile (\n\t\t\t\t\tindex + (pairs + 1) * 2 <= units.length &&\n\t\t\t\t\tunits[index + pairs * 2] === unit &&\n\t\t\t\t\tunits[index + pairs * 2 + 1] === next\n\t\t\t\t) {\n\t\t\t\t\tpairs++;\n\t\t\t\t}\n\t\t\t\tif (pairs >= DEGENERATE_REPEATED_LINE_MIN) {\n\t\t\t\t\tout.push(unit, next);\n\t\t\t\t\tindex += pairs * 2;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tout.push(unit);\n\t\tindex++;\n\t}\n\treturn out.join(joinWith);\n}\n\nfunction maxRepeatedUnitRun(units: readonly string[]): number {\n\tlet maxRun = 1;\n\tlet run = 1;\n\tfor (let index = 1; index < units.length; index++) {\n\t\tif (units[index] === units[index - 1] && units[index].trim() !== \"\") {\n\t\t\trun++;\n\t\t\tif (run > maxRun) maxRun = run;\n\t\t} else {\n\t\t\trun = 1;\n\t\t}\n\t}\n\treturn units.length === 0 ? 0 : maxRun;\n}\n\nfunction splitSentences(text: string): string[] {\n\treturn text.split(/(?<=[.!?])\\s+/);\n}\n\nfunction isProseTile(unit: string): boolean {\n\treturn /[.!?]/.test(unit) || /\\s/.test(unit);\n}\n\n/** Smallest power-of-two prose tile that repeats to `text` (`Foo.Foo.` / `Foo.`×4). */\nexport function exactTiledRepeat(text: string): { unit: string; times: number } | undefined {\n\tif (text.length < DEGENERATE_EXACT_REPEAT_MIN * 2) return undefined;\n\tlet unit = text;\n\tlet times = 1;\n\twhile (unit.length >= DEGENERATE_EXACT_REPEAT_MIN * 2 && unit.length % 2 === 0) {\n\t\tconst half = unit.length / 2;\n\t\tconst next = unit.slice(0, half);\n\t\tif (!isProseTile(next) || next !== unit.slice(half)) break;\n\t\tunit = next;\n\t\ttimes *= 2;\n\t}\n\treturn times > 1 ? { unit, times } : undefined;\n}\n\n/**\n * Collapse consecutive identical assistant lines, period-2 ABAB line runs, and the same\n * sentence repeated inside one paragraph. Session 01a0016f stored 551 copies of one\n * sentence as a single line; newline-only collapse cannot see that.\n * Session 01a00337 stored the same status sentence concatenated with no space (`Foo.Foo.`)\n * because Responses deltas replayed the full sentence; whitespace split cannot see that.\n */\nexport function collapseRepeatedLines(text: string): string {\n\tconst tiled = exactTiledRepeat(text);\n\tconst source = tiled ? tiled.unit : text;\n\tconst lineCollapsed = collapseRepeatedUnits(source.split(\"\\n\"), \"\\n\");\n\treturn lineCollapsed\n\t\t.split(\"\\n\")\n\t\t.map((line) => {\n\t\t\tconst lineTile = exactTiledRepeat(line);\n\t\t\treturn collapseRepeatedUnits(splitSentences(lineTile ? lineTile.unit : line), \" \");\n\t\t})\n\t\t.join(\"\\n\");\n}\n\nexport function isDegenerateRepeatedText(text: string): boolean {\n\treturn collapseRepeatedLines(text) !== text;\n}\n\nexport function shouldAbortDegenerateStream(text: string): boolean {\n\tif (text.length < 400) return false;\n\tconst tiled = exactTiledRepeat(text);\n\tif (tiled && tiled.times >= DEGENERATE_STREAM_ABORT_RUN) return true;\n\tconst sentences = splitSentences(text);\n\tif (maxRepeatedUnitRun(sentences) >= DEGENERATE_STREAM_ABORT_RUN) return true;\n\treturn maxRepeatedUnitRun(text.split(\"\\n\")) >= DEGENERATE_STREAM_ABORT_RUN;\n}\n\nconst collapsedDegenerateMessages = new WeakSet<AssistantMessage>();\n\nexport function collapseDegenerateAssistantMessage(message: AssistantMessage): AssistantMessage {\n\tlet changed = false;\n\tconst seenText = new Set<string>();\n\tconst content: AssistantMessage[\"content\"] = [];\n\tfor (const block of message.content) {\n\t\tif (block.type !== \"text\") {\n\t\t\tcontent.push(block);\n\t\t\tcontinue;\n\t\t}\n\t\tconst collapsed = collapseRepeatedLines(block.text);\n\t\tif (collapsed !== block.text) changed = true;\n\t\tif (collapsed.trim() !== \"\" && seenText.has(collapsed)) {\n\t\t\tchanged = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (collapsed.trim() !== \"\") seenText.add(collapsed);\n\t\tcontent.push(collapsed === block.text ? block : { ...block, text: collapsed });\n\t}\n\tif (!changed) return message;\n\tconst next = { ...message, content };\n\tcollapsedDegenerateMessages.add(next);\n\treturn next;\n}\n\n/** True when this message is (or was collapsed from) a generation loop. */\nexport function isCollapsedDegenerateAssistantMessage(message: AssistantMessage): boolean {\n\treturn collapsedDegenerateMessages.has(message);\n}\n\nexport function assistantMessageText(message: AssistantMessage): string {\n\treturn message.content\n\t\t.filter((block) => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\"\\n\");\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caupulican/pi-agent-core",
3
- "version": "0.91.2",
3
+ "version": "0.91.3",
4
4
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -115,13 +115,13 @@
115
115
  "scripts": {
116
116
  "clean": "shx rm -rf dist",
117
117
  "build": "node ../../scripts/run-tsc.mjs -p tsconfig.build.json",
118
- "test": "vitest --run",
118
+ "test": "vitest --run --bail=1",
119
119
  "test:harness": "vitest --run --config vitest.harness.config.ts",
120
120
  "coverage:harness": "vitest --run --config vitest.harness.config.ts --coverage",
121
121
  "prepublishOnly": "npm run clean && npm run build"
122
122
  },
123
123
  "dependencies": {
124
- "@caupulican/pi-ai": "^0.91.2",
124
+ "@caupulican/pi-ai": "^0.91.3",
125
125
  "ignore": "7.0.5",
126
126
  "typebox": "1.1.38",
127
127
  "yaml": "2.9.0"