Bound beginSession/compileCtx payloads to a compact digest

The session-init endpoints concatenated unbounded engram activate/scan
results as FULL node objects (content up to ~90KB per node), producing a
~900KB response. After the MCP wrapper re-escapes that into a stringified
text block the client dropped the socket ('connection closed unexpectedly')
on every beginSession call. Cap each list (8-10 activated, 10-20 recent)
and project every node to a light identity plus a bounded, UTF-8-safe
content snippet. Response drops from ~900KB to ~12KB; full content stays
available on demand via recall/fetch/inspectGraph.
This commit is contained in:
2026-07-31 14:59:09 -05:00
parent f3660e92a1
commit 872120c757
3 changed files with 329 additions and 132 deletions
+123 -10
View File
@@ -87,6 +87,107 @@ fn api_or_empty(s: String) -> String {
return "[]"
}
// Compact projection for session/context digests
//
// beginSession/compileCtx are session-INIT digests, not full graph dumps. The
// engram scan/activate builtins return FULL node objects content runs to tens
// of KB per node (the self-identity hub is ~90KB alone), and node JSON carries
// content + metadata + timestamps. Concatenated unbounded, the assembled response
// reached ~900KB and after the MCP wrapper re-escapes it into a stringified
// text block the client dropped the socket ("connection closed unexpectedly")
// on every call. These helpers CAP the array length and project each node down
// to a light identity + a bounded, UTF-8-safe content snippet, holding the
// digest well under ~150KB regardless of graph size. Full content stays
// available on demand via recall / fetch / inspectGraph.
// api_num_or_zero raw JSON numeric literal for `key`, or "0" when absent.
// Used for numeric node/activation fields so they stay unquoted (valid JSON).
fn api_num_or_zero(obj: String, key: String) -> String {
let v: String = json_get_raw(obj, key)
if str_eq(v, "") { return "0" }
return v
}
// api_utf8_trunc byte-truncate `s` to at most `n` bytes WITHOUT splitting a
// multibyte UTF-8 sequence (str_slice is byte-based). Backs the cut off while the
// first EXCLUDED byte is a UTF-8 continuation byte (0x80..0xBF), so the snippet is
// always a valid prefix. Guards against re-introducing a parse failure via
// invalid UTF-8 in a JSON string value.
fn api_utf8_trunc(s: String, n: Int) -> String {
if str_len(s) <= n { return s }
let cut: Int = n
let scanning: Bool = true
while scanning && cut > 0 {
let b: Int = str_char_code(s, cut)
let is_cont: Bool = b >= 128 && b < 192
let cut = if is_cont { cut - 1 } else { cut }
let scanning = is_cont
}
return str_slice(s, 0, cut)
}
// api_compact_node light projection of a full engram node: identity fields +
// a bounded, UTF-8-safe content snippet. Drops embeddings, metadata, tags, and
// timestamps; truncates content. `content_truncated` flags a clipped snippet.
fn api_compact_node(node: String, snip: Int) -> String {
let id: String = json_get(node, "id")
let ntype: String = json_get(node, "node_type")
let label: String = json_get(node, "label")
let tier: String = json_get(node, "tier")
let content: String = json_get(node, "content")
let snippet: String = api_utf8_trunc(content, snip)
let trunc_str: String = if str_len(content) > snip { "true" } else { "false" }
return "{\"id\":\"" + api_json_escape(id) + "\""
+ ",\"node_type\":\"" + api_json_escape(ntype) + "\""
+ ",\"label\":\"" + api_json_escape(label) + "\""
+ ",\"tier\":\"" + api_json_escape(tier) + "\""
+ ",\"importance\":" + api_num_or_zero(node, "importance")
+ ",\"salience\":" + api_num_or_zero(node, "salience")
+ ",\"content\":\"" + api_json_escape(snippet) + "\""
+ ",\"content_truncated\":" + trunc_str + "}"
}
// api_compact_node_array map api_compact_node over a bare-node array, capping
// the element count. For scan results (recent, typed lists).
fn api_compact_node_array(raw: String, max_items: Int, snip: Int) -> String {
if !api_nonempty(raw) { return "[]" }
let n: Int = json_array_len(raw)
let cap: Int = if n < max_items { n } else { max_items }
let out: String = "["
let i: Int = 0
while i < cap {
let node: String = json_array_get(raw, i)
let sep: String = if i == 0 { "" } else { "," }
let out = out + sep + api_compact_node(node, snip)
let i = i + 1
}
return out + "]"
}
// api_compact_activated like api_compact_node_array but for activation results,
// whose elements wrap the node as {"node":{...},"activation_strength":...,...}.
// Preserves the activation scalars, compacts the inner node.
fn api_compact_activated(raw: String, max_items: Int, snip: Int) -> String {
if !api_nonempty(raw) { return "[]" }
let n: Int = json_array_len(raw)
let cap: Int = if n < max_items { n } else { max_items }
let out: String = "["
let i: Int = 0
while i < cap {
let el: String = json_array_get(raw, i)
let node: String = json_get_raw(el, "node")
let sep: String = if i == 0 { "" } else { "," }
let out = out + sep + "{\"node\":" + api_compact_node(node, snip)
+ ",\"activation_strength\":" + api_num_or_zero(el, "activation_strength")
+ ",\"working_memory_weight\":" + api_num_or_zero(el, "working_memory_weight")
+ ",\"epistemic_confidence\":" + api_num_or_zero(el, "epistemic_confidence")
+ ",\"hops\":" + api_num_or_zero(el, "hops")
+ ",\"promoted\":" + api_num_or_zero(el, "promoted") + "}"
let i = i + 1
}
return out + "]"
}
// api_persisted read-back-after-write guard against hallucinated saves.
// After a write builtin returns an id, confirm the node is actually queryable
// via engram_get_node_json(id) (returns "" or "null" when missing). Returns
@@ -182,25 +283,37 @@ fn handle_api_begin_session(body: String) -> String {
// own dedicated tool, inspectGraph; duplicating it here served nothing).
// self_neighbors key retained as [] for response-shape compatibility.
let stats: String = engram_stats_json()
let activated: String = engram_activate_json("session start recent memory important", 1)
let state_events: String = engram_scan_nodes_by_type_json("InternalStateEvent", 5, 0)
let recent: String = engram_scan_nodes_json(10, 0)
// PAYLOAD BOUND (2026-07-31): compact every list to a digest. The raw
// activate/scan builtins emit FULL node objects (content up to ~90KB each);
// unbounded concatenation reached ~900KB and closed the MCP client socket.
// Cap counts + project to identity + UTF-8-safe content snippets <~150KB.
let activated_raw: String = engram_activate_json("session start recent memory important", 1)
let activated: String = api_compact_activated(activated_raw, 8, 240)
let state_events_raw: String = engram_scan_nodes_by_type_json("InternalStateEvent", 5, 0)
let state_events: String = api_compact_node_array(state_events_raw, 5, 500)
let recent_raw: String = engram_scan_nodes_json(10, 0)
let recent: String = api_compact_node_array(recent_raw, 10, 240)
return "{\"stats\":" + stats
+ ",\"recent\":" + api_or_empty(recent)
+ ",\"activated\":" + api_or_empty(activated)
+ ",\"recent\":" + recent
+ ",\"activated\":" + activated
+ ",\"self_neighbors\":[]"
+ ",\"recent_state_events\":" + api_or_empty(state_events) + "}"
+ ",\"recent_state_events\":" + state_events + "}"
}
// handle_api_compile_ctx compile active-work context.
// Spread-activates from "active work" intent + recent nodes.
fn handle_api_compile_ctx(body: String) -> String {
let stats: String = engram_stats_json()
let activated: String = engram_activate_json("active work context current task in progress", 2)
let recent: String = engram_scan_nodes_json(20, 0)
// PAYLOAD BOUND (2026-07-31): same digest treatment as begin_session. This
// handler's depth-2 spread returns even more full nodes, so bounding here is
// essential cap to 10 activated + 20 recent, project to snippets.
let activated_raw: String = engram_activate_json("active work context current task in progress", 2)
let activated: String = api_compact_activated(activated_raw, 10, 240)
let recent_raw: String = engram_scan_nodes_json(20, 0)
let recent: String = api_compact_node_array(recent_raw, 20, 240)
return "{\"stats\":" + stats
+ ",\"recent_nodes\":" + api_or_empty(recent)
+ ",\"activated\":" + api_or_empty(activated) + "}"
+ ",\"recent_nodes\":" + recent
+ ",\"activated\":" + activated + "}"
}
// Memory