zames_pro 2.82.18 → 2.82.21

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
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.82.21] - 2026-10-22
11
+
12
+ ### Fixed
13
+
14
+ - **glm:** unwrap nested pagination wrappers in the chat list
15
+
16
+ ## [2.82.20] - 2026-10-22
17
+
18
+ ### Fixed
19
+
20
+ - **qwen:** unwrap a paginated chat-list wrapper
21
+
22
+ ## [2.82.19] - 2026-10-22
23
+
24
+ ### Fixed
25
+
26
+ - **glm:** unwrap a nested chats wrapper in the chat list
27
+
28
+ ### Changed
29
+
30
+ - **glm:** drop guessed role fields from the history parser
31
+
10
32
  ## [2.82.18] - 2026-10-22
11
33
 
12
34
  ### Fixed
package/dist/glm-chats.js CHANGED
@@ -85,12 +85,20 @@ export function scrapeGlmChatMessages(opts) {
85
85
  * anything unrecognizable (never throws).
86
86
  */
87
87
  export function parseGlmChatList(json) {
88
+ // Unwrap up to THREE levels of list wrappers: a paginated reply nests them
89
+ // (`{data:{list:{page,data:[…]}}}`, `{chats:{page,data:[…]}}`). Leaving an
90
+ // inner wrapper in place made its KEY (`'data'`/`'list'`) look like a chat id
91
+ // with an empty title, so /chats showed one fake chat and hid the real ones.
92
+ // A bare id-keyed MAP of chats has none of these keys and is kept as-is.
88
93
  let arr = json;
89
- if (arr && typeof arr === 'object' && !Array.isArray(arr)) {
94
+ for (let depth = 0; depth < 3; depth++) {
95
+ if (!arr || typeof arr !== 'object' || Array.isArray(arr))
96
+ break;
90
97
  const o = arr;
91
- // A wrapper key wins; otherwise the object itself is an id-keyed MAP
92
- // of chats (a real alternative shape), not an empty list.
93
- arr = o.data ?? o.chats ?? o.list ?? o.items ?? arr;
98
+ const next = o.data ?? o.chats ?? o.list ?? o.items;
99
+ if (next === undefined)
100
+ break;
101
+ arr = next;
94
102
  }
95
103
  const keyed = Array.isArray(arr)
96
104
  ? null
@@ -154,30 +162,25 @@ export function parseGlmHistory(json) {
154
162
  .sort((a, b) => (Number(a.timestamp) || 0) - (Number(b.timestamp) || 0));
155
163
  const out = [];
156
164
  for (const m of sorted) {
157
- // The role has been `role` (string, case-insensitive) or `from`
158
- // (`human`/`assistant`, z.ai's older spelling). Reading only `role`
159
- // attributed a `from: 'human'` turn to the model.
165
+ // The role comes from `role` (confirmed by the live fixture). Only the
166
+ // values the API actually uses are matched: a numeric flag or a `from`
167
+ // field was never observed on z.ai and guessing at it would silently
168
+ // re-attribute turns.
160
169
  const roleStr = String(m.role ?? '').toLowerCase();
161
- const fromStr = String(m.from ?? '').toLowerCase();
162
170
  // A SYSTEM/TOOL turn is neither the operator nor the model's answer: z.ai's
163
171
  // own system prompt and the sandbox output of its agent runtime used to be
164
172
  // restored as model REPLIES (the Qwen parser drops those roles too).
165
- if (/^(system|tool)$/.test(roleStr) || /^(system|tool)$/.test(fromStr)) {
173
+ if (/^(system|tool)$/.test(roleStr))
166
174
  continue;
167
- }
168
- // A numeric flag uses 1 = user, 2 = assistant (the same convention as
169
- // Qwen's history); anything else is matched by name.
170
- const isUser = roleStr === 'user' ||
171
- roleStr === 'human' ||
172
- roleStr === '1' ||
173
- fromStr === 'user' ||
174
- fromStr === 'human';
175
+ const isUser = roleStr === 'user' || roleStr === 'human';
175
176
  const role = isUser ? 'user' : 'assistant';
176
177
  let text = '';
177
178
  if (typeof m.content === 'string')
178
179
  text = m.content;
179
180
  else if (typeof m.text === 'string')
180
181
  text = m.text;
182
+ // `fragments` is the DeepSeek-style payload (kept as a safety net for a
183
+ // build that reuses it): REQUEST is the user turn, RESPONSE the answer.
181
184
  else if (Array.isArray(m.fragments)) {
182
185
  const want = role === 'assistant' ? 'RESPONSE' : 'REQUEST';
183
186
  for (const fr of m.fragments) {
@@ -91,6 +91,26 @@ export function scrapeQwenChatMessages(opts) {
91
91
  * { data: [ {id,title} ] }, { data: { chats: [...] } }, { data: { list: [...] } }.
92
92
  * Returns [] for anything unrecognizable (never throws).
93
93
  */
94
+ /**
95
+ * Unwrap the list out of its wrappers: `{chats:[…]}`, `{list:{page,data:[…]}}`.
96
+ * A paginated reply nests the wrapper INSIDE the wrapper, and treating that
97
+ * inner object as an id-keyed map made its own key (`'data'`, `'list'`) look
98
+ * like a chat id. A plain id-keyed map (no known wrapper key) is returned
99
+ * as-is, because that is a legitimate shape too.
100
+ */
101
+ function unwrapChatList(data) {
102
+ let cur = data;
103
+ for (let depth = 0; depth < 3; depth++) {
104
+ if (!cur || typeof cur !== 'object' || Array.isArray(cur))
105
+ return cur;
106
+ const o = cur;
107
+ const inner = o.chats ?? o.list ?? o.items ?? o.data;
108
+ if (inner === undefined)
109
+ return cur;
110
+ cur = inner;
111
+ }
112
+ return cur;
113
+ }
94
114
  export function parseQwenChatList(json) {
95
115
  const root = json;
96
116
  // A BARE array (no `data` wrapper) is the simplest shape the endpoint can
@@ -105,10 +125,7 @@ export function parseQwenChatList(json) {
105
125
  ? // A bare map keyed by chat id (the same convention as the history
106
126
  // shape) has no `chats`/`list`/`items` wrapper: without this fallback
107
127
  // a legitimate list was rejected and /chats printed "(none)".
108
- (data.chats ??
109
- data.list ??
110
- data.items ??
111
- data)
128
+ unwrapChatList(data)
112
129
  : null;
113
130
  const keyed = Array.isArray(arr)
114
131
  ? null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zames_pro",
3
- "version": "2.82.18",
3
+ "version": "2.82.21",
4
4
  "description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen and other web LLMs — no API key required. Reads and edits files, runs commands, commits to git, like Claude Code / Codex CLI.",
5
5
  "type": "module",
6
6
  "bin": {