Skip to content

Commit 98fdb4f

Browse files
author
SqlRush
committed
Load microcompact cache collections
1 parent 0aaa9df commit 98fdb4f

5 files changed

Lines changed: 58 additions & 4 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,8 @@ M7 补充:prompt history `LogEntry` 读取现在接受 `sessionID`/`session`/`
520520

521521
本轮补充:microcompact disk cache loader 现在也递归解包 GraphQL-style `viewer`/`edge`/`node`/`attrs` wrapper,node `id` 可作为 digest fallback,attrs/properties 内的 summary、version、timestamp 和 TTL aliases 都会恢复。
522522

523+
本轮补充:microcompact disk cache loader 现在也会穿透 `edges`/`nodes`/`included` 等 collection wrapper 和数组元素,跳过无 summary 的非 cache resource,并恢复 GraphQL connection 或 JSON:API included 风格 cache entry。
524+
523525
本轮补充:microcompact disk cache loader 的 summary-like payload 现在接受 text content-block object、text content-block array 和 string array,会把可见 text block 合并为 summary,兼容官方/SDK 响应内容块形态的 cached microcompact 文件。
524526

525527
本轮补充:microcompact disk cache loader 的 summary-like payload 现在也接受完整 contract message object,并会递归解包 `message`/`assistantMessage`/`resultMessage`/`outputMessage`/`completionMessage` wrapper,从 message content 中恢复 visible text summary。

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ test/parity/ # golden tests against TS/official behavior
260260
- 本轮补充:microcompact disk cache loader 现在会从 `metadata`/`meta`/`cacheInfo`/`cacheDetails`/`cacheEntry`/`entry`/`record`/`cache` 等 sidecar object 中补缺失的 digest、version、cache-hit、timestamp、TTL 和 count aliases;主 summary payload 字段仍保持优先。
261261
- 本轮补充:microcompact disk cache loader 现在也接受 JSON:API/resource-style `resource`/`attributes`/`properties` wrapper,summary payload 可放在 attributes/properties 内,外层 resource `id` 可作为 digest fallback。
262262
- 本轮补充:microcompact disk cache loader 现在也递归解包 GraphQL-style `viewer`/`edge`/`node`/`attrs` wrapper,node `id` 可作为 digest fallback,attrs/properties 内的 summary、version、timestamp 和 TTL aliases 都会恢复。
263+
- 本轮补充:microcompact disk cache loader 现在也会穿透 `edges`/`nodes`/`included` 等 collection wrapper 和数组元素,跳过无 summary 的非 cache resource,并恢复 GraphQL connection 或 JSON:API included 风格 cache entry。
263264
- 本轮补充:microcompact disk cache loader 的 summary-like payload 现在接受 text content-block object、text content-block array 和 string array,会把可见 text block 合并为 summary,兼容官方/SDK 响应内容块形态的 cached microcompact 文件。
264265
- 本轮补充:microcompact disk cache loader 的 summary-like payload 现在也接受完整 contract message object,并会递归解包 `message`/`assistantMessage`/`resultMessage`/`outputMessage`/`completionMessage` wrapper,从 message content 中恢复 visible text summary。
265266
- 本轮补充:microcompact disk cache loader 的 summary array 元素现在也接受完整 contract message object,可把 message list 与 content-block 混合数组恢复成可见摘要文本。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ M6 progress now includes:
131131
- `internal/compact`: microcompact disk cache loading now accepts non-strict cache-hit booleans for `cached`/`fromCache`/`cacheHit`/`isCached`, including `yes`/`no`, `on`/`off`, and `1`/`0` JSON number or string values.
132132
- `internal/compact`: microcompact disk cache loading now accepts JSON:API/resource-style `resource`/`attributes`/`properties` wrappers, with summary payloads inside attributes/properties and outer resource IDs used as digest fallback when needed.
133133
- `internal/compact`: microcompact disk cache loading now also recurses through GraphQL-style `viewer`/`edge`/`node`/`attrs` wrappers, preserving node IDs as digest fallback while recovering summary, version, timestamp, and TTL aliases.
134+
- `internal/compact`: microcompact disk cache loading now also traverses collection wrappers such as `edges`, `nodes`, and `included`, skipping non-cache resources without summaries while recovering GraphQL connection or JSON:API included cache entries.
134135
- `internal/compact`: microcompact disk cache summary payloads now accept text content-block objects, text content-block arrays, and string arrays, joining visible text blocks into the recovered summary for official/SDK-style cached microcompact files.
135136
- `internal/compact`: microcompact disk cache summary payloads now also accept full contract message objects and unwrap `message`/`assistantMessage`/`resultMessage`/`outputMessage`/`completionMessage` response wrappers before extracting visible text summary.
136137
- `internal/compact`: microcompact disk cache summary arrays now also accept full contract message objects mixed with content blocks, extracting visible message text while skipping non-visible blocks.

internal/compact/compact_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,24 @@ func TestLoadMicroResultAcceptsWrappedCacheObjects(t *testing.T) {
436436
want: "viewer node summary",
437437
wantExpires: true,
438438
},
439+
{
440+
digest: "graphql-edges-cache",
441+
body: `"data":{"edges":[
442+
{"node":{"id":"tool_1","type":"tool","attrs":{"name":"Bash"}}},
443+
{"node":{"id":"graphql-edges-cache","type":"microcompact-cache","attrs":{"summaryMarkdown":"edges summary","formatVersion":"microcompact.v1","cachedAt":100,"ttlSeconds":3600}}}
444+
]}`,
445+
want: "edges summary",
446+
wantExpires: true,
447+
},
448+
{
449+
digest: "included-resource-cache",
450+
body: `"included":[
451+
{"id":"tool_2","type":"tool","attributes":{"name":"Read"}},
452+
{"resource":{"id":"included-resource-cache","type":"microcompact-cache","attributes":{"compressedText":"included summary","cacheVersion":"microcompact.v1","generatedAt":100,"maxAgeSeconds":3600}}}
453+
]`,
454+
want: "included summary",
455+
wantExpires: true,
456+
},
439457
{
440458
digest: "wrapped-envelope",
441459
body: `"data":{"summary":"envelope summary"},"digest":"wrapped-envelope","version":"microcompact.v1","createdAt":100,"ttlSeconds":3600`,

internal/compact/micro.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ type MicroResult struct {
4343
}
4444

4545
func (r *MicroResult) UnmarshalJSON(data []byte) error {
46+
trimmed := bytes.TrimSpace(data)
47+
if len(trimmed) > 0 && trimmed[0] == '[' {
48+
if nested, ok := microResultArrayWrappedJSON(trimmed); ok {
49+
return json.Unmarshal(nested, r)
50+
}
51+
}
4652
var fields map[string]json.RawMessage
4753
if err := json.Unmarshal(data, &fields); err != nil {
4854
return err
@@ -188,11 +194,11 @@ func microResultWrappedJSON(fields map[string]json.RawMessage) (json.RawMessage,
188194
return nil, false
189195
}
190196
for _, name := range []string{
191-
"result", "data", "cache", "cacheEntry", "cache_entry", "entry", "record", "item", "resource", "payload", "response", "body",
192-
"viewer", "edge", "node",
197+
"result", "data", "cache", "cacheEntry", "cache_entry", "entry", "entries", "record", "records", "item", "items", "resource", "resources", "payload", "response", "body",
198+
"viewer", "edge", "edges", "node", "nodes",
193199
"microcompact", "microCompact", "micro_compact", "micro_result", "microResult", "microcompactResult", "microCompactResult", "micro_compact_result",
194-
"message", "assistantMessage", "assistant_message", "resultMessage", "result_message", "outputMessage", "output_message", "completion", "completionMessage", "completion_message",
195-
"attributes", "properties", "attrs", "value",
200+
"message", "messages", "assistantMessage", "assistant_message", "resultMessage", "result_message", "outputMessage", "output_message", "completion", "completionMessage", "completion_message",
201+
"attributes", "properties", "attrs", "value", "values", "included", "collection", "list", "children",
196202
} {
197203
raw, ok := fields[name]
198204
if !ok {
@@ -202,6 +208,32 @@ func microResultWrappedJSON(fields map[string]json.RawMessage) (json.RawMessage,
202208
if len(trimmed) > 0 && trimmed[0] == '{' {
203209
return raw, true
204210
}
211+
if len(trimmed) > 0 && trimmed[0] == '[' {
212+
if nested, ok := microResultArrayWrappedJSON(trimmed); ok {
213+
return nested, true
214+
}
215+
}
216+
}
217+
return nil, false
218+
}
219+
220+
func microResultArrayWrappedJSON(data json.RawMessage) (json.RawMessage, bool) {
221+
var items []json.RawMessage
222+
if err := json.Unmarshal(data, &items); err != nil {
223+
return nil, false
224+
}
225+
for _, item := range items {
226+
trimmed := bytes.TrimSpace(item)
227+
if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) || trimmed[0] != '{' {
228+
continue
229+
}
230+
var result MicroResult
231+
if err := json.Unmarshal(trimmed, &result); err != nil {
232+
continue
233+
}
234+
if result.Summary != "" {
235+
return item, true
236+
}
205237
}
206238
return nil, false
207239
}

0 commit comments

Comments
 (0)