Skip to content

Commit 5f267b1

Browse files
grypezclaude
andcommitted
fix(caprock): decode smallcaps ! escape in vat provision responses
@endo/marshal smallcaps encoding prefixes strings starting with sigil characters (like `-` for negative floats) with `!`. Shell flags such as `--oneline` or `-l` were rendered as `!--oneline` and `!-l` in the TUI because vatFindMatch decoded only the outer CapData wrapper, leaving nested strings un-unescaped. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent bbfb371 commit 5f267b1

2 files changed

Lines changed: 31 additions & 1 deletion

File tree

packages/caprock/bin/hook.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
pingDaemon,
3737
sendCommand,
3838
decodeCapData,
39+
decodeSmallcapsStrings,
3940
createKernelSession,
4041
authorizeRequest,
4142
recordProvisioned,
@@ -285,7 +286,8 @@ async function vatFindMatch(
285286
if (isJsonRpcFailure(response)) {
286287
return null;
287288
}
288-
return decodeCapData(response.result as CapData) as Provision | null;
289+
const raw = decodeCapData(response.result as CapData);
290+
return decodeSmallcapsStrings(raw) as Provision | null;
289291
}
290292

291293
/**

packages/caprock/src/rpc.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,31 @@ export function decodeCapData(capData: CapData): unknown {
311311
}
312312
throw new Error(`Unexpected CapData body format: ${body.slice(0, 40)}`);
313313
}
314+
315+
/**
316+
* Recursively strip the smallcaps `!` escape prefix from string values in a
317+
* decoded CapData object. In smallcaps encoding, strings that begin with a
318+
* sigil character (including `-` for negative special floats) are prefixed
319+
* with `!` to distinguish them from encoding markers. This reversal is needed
320+
* when decoding complex objects like Provision (whose argv may contain flags
321+
* like `--oneline` that become `!--oneline` after encoding).
322+
*
323+
* @param value - A JSON-parsed smallcaps value.
324+
* @returns The value with all `!`-escaped strings decoded.
325+
*/
326+
export function decodeSmallcapsStrings(value: unknown): unknown {
327+
if (typeof value === 'string') {
328+
return value.startsWith('!') ? value.slice(1) : value;
329+
}
330+
if (Array.isArray(value)) {
331+
return value.map(decodeSmallcapsStrings);
332+
}
333+
if (typeof value === 'object' && value !== null) {
334+
const result: Record<string, unknown> = {};
335+
for (const [key, val] of Object.entries(value as Record<string, unknown>)) {
336+
result[key] = decodeSmallcapsStrings(val);
337+
}
338+
return result;
339+
}
340+
return value;
341+
}

0 commit comments

Comments
 (0)