fix: lighten session list payloads (#1538)
* lighten session list * fetch full session on open * sanitize session list * sanitize global sessions * preserve revert markers in session lists * fix: preserve session metadata in list sanitizers --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
1c274d0a56
commit
9685630436
@@ -237,6 +237,199 @@ describe('OpenCode proxy SSE forwarding', () => {
|
||||
expect(Number(data.contentLength)).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('sanitizes experimental session list responses and forwards query params', async () => {
|
||||
let seenQuery = null;
|
||||
let seenAuth = null;
|
||||
|
||||
const upstream = express();
|
||||
upstream.get('/experimental/session', (req, res) => {
|
||||
seenQuery = req.query;
|
||||
seenAuth = req.headers.authorization ?? null;
|
||||
res.setHeader('X-Next-Cursor', '123');
|
||||
res.json([
|
||||
{
|
||||
id: 'ses_1',
|
||||
slug: 'alpha',
|
||||
projectID: 'proj_1',
|
||||
workspaceID: 'ws_1',
|
||||
directory: '/repo/app',
|
||||
path: '/repo/app',
|
||||
parentID: 'ses_parent',
|
||||
title: 'Alpha',
|
||||
agent: 'build',
|
||||
model: { id: 'gpt-5', providerID: 'openai', variant: 'default' },
|
||||
version: '1.0.0',
|
||||
time: { created: 1, updated: 2 },
|
||||
cost: 7,
|
||||
tokens: { input: 10, output: 20 },
|
||||
share: { url: 'https://share.example/ses_1' },
|
||||
project: { id: 'proj_1', worktree: '/repo/app' },
|
||||
summary: {
|
||||
additions: 5,
|
||||
deletions: 3,
|
||||
files: 2,
|
||||
diffs: [{ patch: '@@ -1 +1 @@', additions: 5, deletions: 3 }],
|
||||
},
|
||||
metadata: { openchamber: { kind: 'review', originalSessionID: 'ses_original' } },
|
||||
permission: [{ permission: 'todowrite', action: 'deny', pattern: '*' }],
|
||||
revert: { messageID: 'msg_1', partID: 'part_1', snapshot: 'abc123', diff: 'diff --git a/x b/x' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
upstreamServer = await listen(upstream);
|
||||
const upstreamPort = upstreamServer.address().port;
|
||||
const externalBaseUrl = `http://127.0.0.1:${upstreamPort}`;
|
||||
|
||||
const app = express();
|
||||
registerOpenCodeProxy(app, {
|
||||
fs: {
|
||||
promises: {
|
||||
realpath: async (value) => value === '/link/repo' ? '/real/repo' : value,
|
||||
},
|
||||
},
|
||||
os: {},
|
||||
path,
|
||||
OPEN_CODE_READY_GRACE_MS: 0,
|
||||
getRuntime: () => ({
|
||||
openCodePort: upstreamPort,
|
||||
openCodeBaseUrl: externalBaseUrl,
|
||||
isOpenCodeReady: true,
|
||||
openCodeNotReadySince: 0,
|
||||
isRestartingOpenCode: false,
|
||||
}),
|
||||
getOpenCodeAuthHeaders: () => ({ Authorization: 'Bearer session-token' }),
|
||||
buildOpenCodeUrl: (requestPath) => `${externalBaseUrl}${requestPath}`,
|
||||
ensureOpenCodeApiPrefix: () => {},
|
||||
});
|
||||
proxyServer = await listen(app);
|
||||
const proxyPort = proxyServer.address().port;
|
||||
|
||||
const response = await fetch(`http://127.0.0.1:${proxyPort}/api/experimental/session?archived=false&limit=500&cursor=99&roots=true&directory=%2Flink%2Frepo`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('x-next-cursor')).toBe('123');
|
||||
expect(seenAuth).toBe('Bearer session-token');
|
||||
expect(seenQuery).toMatchObject({
|
||||
archived: 'false',
|
||||
limit: '500',
|
||||
cursor: '99',
|
||||
roots: 'true',
|
||||
directory: '/real/repo',
|
||||
});
|
||||
|
||||
await expect(response.json()).resolves.toEqual([
|
||||
{
|
||||
id: 'ses_1',
|
||||
slug: 'alpha',
|
||||
projectID: 'proj_1',
|
||||
workspaceID: 'ws_1',
|
||||
directory: '/repo/app',
|
||||
path: '/repo/app',
|
||||
parentID: 'ses_parent',
|
||||
title: 'Alpha',
|
||||
agent: 'build',
|
||||
model: { id: 'gpt-5', providerID: 'openai', variant: 'default' },
|
||||
version: '1.0.0',
|
||||
time: { created: 1, updated: 2 },
|
||||
cost: 7,
|
||||
tokens: { input: 10, output: 20 },
|
||||
share: { url: 'https://share.example/ses_1' },
|
||||
metadata: { openchamber: { kind: 'review', originalSessionID: 'ses_original' } },
|
||||
project: { id: 'proj_1', worktree: '/repo/app' },
|
||||
summary: { additions: 5, deletions: 3, files: 2 },
|
||||
revert: { messageID: 'msg_1', partID: 'part_1' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('sanitizes session list responses without sanitizing session detail responses', async () => {
|
||||
let seenListQuery = null;
|
||||
|
||||
const upstream = express();
|
||||
upstream.get('/session', (req, res) => {
|
||||
seenListQuery = req.query;
|
||||
res.json([
|
||||
{
|
||||
id: 'ses_1',
|
||||
directory: '/repo/app',
|
||||
title: 'Alpha',
|
||||
time: { created: 1, updated: 2 },
|
||||
summary: {
|
||||
additions: 5,
|
||||
deletions: 3,
|
||||
files: 2,
|
||||
diffs: [{ patch: '@@ -1 +1 @@', additions: 5, deletions: 3 }],
|
||||
},
|
||||
metadata: { custom: { value: 'kept' } },
|
||||
revert: { messageID: 'msg_1', partID: 'part_1', snapshot: 'abc123', diff: 'diff --git a/x b/x' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
upstream.get('/session/abc', (_req, res) => {
|
||||
res.json({
|
||||
id: 'abc',
|
||||
directory: '/repo/app',
|
||||
title: 'Detail',
|
||||
summary: { diffs: [{ patch: '@@ -1 +1 @@' }] },
|
||||
revert: { messageID: 'msg_1', snapshot: 'abc123', diff: 'diff --git a/x b/x' },
|
||||
});
|
||||
});
|
||||
upstreamServer = await listen(upstream);
|
||||
const upstreamPort = upstreamServer.address().port;
|
||||
const externalBaseUrl = `http://127.0.0.1:${upstreamPort}`;
|
||||
|
||||
const app = express();
|
||||
registerOpenCodeProxy(app, {
|
||||
fs: {
|
||||
promises: {
|
||||
realpath: async (value) => value === '/link/repo' ? '/real/repo' : value,
|
||||
},
|
||||
},
|
||||
os: {},
|
||||
path,
|
||||
OPEN_CODE_READY_GRACE_MS: 0,
|
||||
getRuntime: () => ({
|
||||
openCodePort: upstreamPort,
|
||||
openCodeBaseUrl: externalBaseUrl,
|
||||
isOpenCodeReady: true,
|
||||
openCodeNotReadySince: 0,
|
||||
isRestartingOpenCode: false,
|
||||
}),
|
||||
getOpenCodeAuthHeaders: () => ({}),
|
||||
buildOpenCodeUrl: (requestPath) => `${externalBaseUrl}${requestPath}`,
|
||||
ensureOpenCodeApiPrefix: () => {},
|
||||
});
|
||||
proxyServer = await listen(app);
|
||||
const proxyPort = proxyServer.address().port;
|
||||
|
||||
const listResponse = await fetch(`http://127.0.0.1:${proxyPort}/api/session?directory=%2Flink%2Frepo`);
|
||||
|
||||
expect(listResponse.status).toBe(200);
|
||||
expect(seenListQuery).toMatchObject({ directory: '/real/repo' });
|
||||
await expect(listResponse.json()).resolves.toEqual([
|
||||
{
|
||||
id: 'ses_1',
|
||||
directory: '/repo/app',
|
||||
title: 'Alpha',
|
||||
time: { created: 1, updated: 2 },
|
||||
summary: { additions: 5, deletions: 3, files: 2 },
|
||||
metadata: { custom: { value: 'kept' } },
|
||||
revert: { messageID: 'msg_1', partID: 'part_1' },
|
||||
},
|
||||
]);
|
||||
|
||||
const detailResponse = await fetch(`http://127.0.0.1:${proxyPort}/api/session/abc`);
|
||||
|
||||
expect(detailResponse.status).toBe(200);
|
||||
await expect(detailResponse.json()).resolves.toEqual({
|
||||
id: 'abc',
|
||||
directory: '/repo/app',
|
||||
title: 'Detail',
|
||||
summary: { diffs: [{ patch: '@@ -1 +1 @@' }] },
|
||||
revert: { messageID: 'msg_1', snapshot: 'abc123', diff: 'diff --git a/x b/x' },
|
||||
});
|
||||
});
|
||||
|
||||
it('forwards unparsed SDK JSON bodies to generic API proxy requests', async () => {
|
||||
const upstream = express();
|
||||
upstream.post('/session/abc/revert', express.json(), (req, res) => {
|
||||
|
||||
Reference in New Issue
Block a user