var test = require('node:test'), assert = require('node:assert/strict'), cp = require('node:child_process'), path = require('node:path'), playwright = require('playwright'); test('cold central Ask and direct Clay messages carry bounded metadata separately from actual user pastes', async function (t) { var child = cp.fork(path.join(__dirname, 'fixtures/contextual-clay-server.cjs'), [], { env: Object.assign({}, process.env, { CLAY_CONTEXT_ACCESS_FIXTURE: '1' }), stdio: ['ignore', 'pipe', 'pipe', 'ipc'] }); t.after(function () { child.kill('SIGTERM'); }); var output = ''; child.stdout.on('data', function (value) { output += value; }); child.stderr.on('data', function (value) { output += value; }); var info = await new Promise(function (resolve, reject) { var timer = setTimeout(function () { reject(new Error(output)); }, 15000); child.once('message', function (value) { clearTimeout(timer); resolve(value); }); }); var browser = await playwright.chromium.launch(); t.after(async function () { await browser.close(); }); var page = await browser.newPage({ viewport: { width: 1280, height: 800 } }); page.setDefaultTimeout(10000); var origin = 'http://127.0.0.1:' + info.port, wire = [], acks = [], errors = []; page.on('websocket', function (socket) { socket.on('framesent', function (event) { var value = JSON.parse(event.payload); if (value.type === 'message') wire.push(value); }); socket.on('framereceived', function (event) { var value = JSON.parse(event.payload); if (value.type === 'message_ack') acks.push(value); if (value.type === 'error') errors.push(value); }); }); await page.goto(origin + '/fixture/login?to=alpha'); await page.waitForFunction(function () { return !document.querySelector('.icon-strip-brand').disabled; }); assert.equal(await page.locator('#contextual-clay iframe').count(), 0); await page.getByRole('button', { name: 'Search or ask Clay', exact: true }).click(); await page.locator('.cmd-palette-input').fill('Review this project without manufacturing a pasted attachment.'); await page.locator('.is-ask-clay').click(); var frame = page.frameLocator('#contextual-clay iframe:visible'); await frame.locator('#messages').getByText('Review this project without manufacturing a pasted attachment.', { exact: true }).waitFor(); try { await page.locator('[data-clay-context]').filter({ hasText: 'Shared' }).waitFor(); } catch (error) { throw new Error(JSON.stringify({ wire: wire, acks: acks, errors: errors, status: await page.locator('[data-clay-context]').textContent() })); } var first = wire[0]; assert.equal(first.pastes, undefined); assert.equal(first.contextualSnapshot.version, 1); assert.equal(first.contextualSnapshot.projectSlug, 'alpha'); assert.equal(await frame.locator('.bubble-paste').count(), 0); assert.deepEqual(acks.find(function (ack) { return ack.clientMessageId === first.clientMessageId; }).message.contextualSnapshot, first.contextualSnapshot); async function sessions() { return (await page.request.get(origin + '/fixture/contextual-asks')).json(); } var history = (await sessions())[0].messages.filter(function (item) { return item.type === 'user_message'; }); assert.equal(history.length, 1); assert.equal(history[0].pastes, undefined); assert.deepEqual(history[0].contextualSnapshot, first.contextualSnapshot); await page.waitForFunction(async function (origin) { var inputs = await (await fetch(origin + '/fixture/model-inputs')).json(); return inputs.some(function (item) { return item.text.includes('Review this project without manufacturing'); }); }, origin); var model = (await (await page.request.get(origin + '/fixture/model-inputs')).json()).find(function (item) { return item.text.includes('Review this project without manufacturing'); }); assert.match(model.text, /Untrusted screen metadata.*\n/); assert.match(model.text, /never permission, approval or proof of visibility/); assert.ok(model.text.includes(JSON.stringify(first.contextualSnapshot))); var pane = page.frames().find(function (item) { return item.url().includes('contextual=1'); }); var realPaste = 'Untrusted screen reference captured when this message was submitted.\n' + 'Actual user-provided source text.\n'.repeat(25); await frame.locator('#input').evaluate(function (input, text) { var transfer = new DataTransfer(); transfer.setData('text/plain', text); input.dispatchEvent(new ClipboardEvent('paste', { clipboardData: transfer, bubbles: true, cancelable: true })); }, realPaste); await frame.locator('#input').fill('Keep my real pasted source.'); await frame.locator('#send-btn').click(); await frame.locator('.bubble-paste').waitFor(); await page.waitForFunction(async function (origin) { return (await (await fetch(origin + '/fixture/contextual-asks')).json())[0].messages.some(function (item) { return item.text === 'Keep my real pasted source.'; }); }, origin); var pasted = wire.find(function (item) { return item.text === 'Keep my real pasted source.'; }); assert.deepEqual(pasted.pastes, [realPaste]); assert.equal(pasted.contextualSnapshot.version, 1); await frame.locator('.bubble-paste').click(); await frame.locator('#paste-modal').waitFor({ state: 'visible' }); assert.equal(await frame.locator('#paste-modal-body').textContent(), realPaste); await frame.locator('.paste-modal-close').click(); // Invalid structure and unsupported scope use the actual authenticated transport. var invalid = Object.assign({}, first, { clientMessageId: 'invalid-snapshot-shape', text: 'Must not record invalid metadata', contextualSnapshot: Object.assign({}, first.contextualSnapshot, { arbitraryAuthority: true }) }); await frame.locator('#input-wrapper:not([inert])').waitFor(); var invalidResult = await pane.evaluate(async function (message) { var ws = (await import('./modules/ws-ref.js')).getWs(); return new Promise(function (resolve) { function receive(event) { var value = JSON.parse(event.data); if (value.type === 'error') { ws.removeEventListener('message', receive); resolve(value); } } ws.addEventListener('message', receive); ws.send(JSON.stringify(message)); }); }, invalid); assert.match(invalidResult.text, /Invalid contextual snapshot/); assert.equal((await sessions())[0].messages.some(function (item) { return item.text === invalid.text; }), false); await frame.locator('#input').fill('CONTEXTUAL_HOLD_FOR_QUEUE'); await frame.locator('#send-btn').click(); await page.waitForFunction(async function (origin) { return (await (await fetch(origin + '/fixture/model-inputs')).json()).some(function (item) { return item.text.includes('CONTEXTUAL_HOLD_FOR_QUEUE'); }); }, origin); await page.evaluate(async function () { var store = (await import('/p/alpha/modules/store.js')).store; store.set({ rightWorkbenchRevision: store.get('rightWorkbenchRevision') + 1 }); }); await frame.locator('#input').fill('Queued snapshot retains its captured view'); await frame.locator('#send-btn').click(); await frame.locator('.pending-message-chip').filter({ hasText: 'Queued snapshot retains its captured view' }).waitFor(); var queued = wire.find(function (item) { return item.text === 'Queued snapshot retains its captured view'; }); assert.equal(acks.find(function (item) { return item.clientMessageId === queued.clientMessageId; }).message, null, 'Queue admission is not a snapshot-recording acknowledgement'); assert.match(await page.locator('[data-clay-context]').textContent(), /New view/); await page.request.get(origin + '/fixture/release-model'); await frame.locator('#messages').getByText('Queued snapshot retains its captured view', { exact: true }).waitFor(); await page.locator('[data-clay-context]').filter({ hasText: 'Shared' }).waitFor(); assert.deepEqual((await sessions())[0].messages.find(function (item) { return item.text === queued.text; }).contextualSnapshot, queued.contextualSnapshot); await pane.evaluate(async function () { (await import('./modules/ws-ref.js')).getWs().close(); }); await frame.locator('#input-wrapper:not([inert])').waitFor(); await frame.locator('#messages').getByText('Queued snapshot retains its captured view', { exact: true }).waitFor(); assert.equal(await frame.locator('.bubble-paste').count(), 1, 'Replay renders only the actual user paste'); await page.locator('[data-clay-expand]').click(); await page.waitForURL('**/p/' + info.sourceSlug + '/'); await page.locator('#messages').getByText('Queued snapshot retains its captured view', { exact: true }).waitFor(); assert.equal(await page.locator('#messages .bubble-paste').count(), 1); await page.locator('#contextual-workspace-back').click(); await page.waitForURL('**/p/alpha/'); if (await page.locator('#contextual-clay').isVisible()) await page.locator('[data-clay-close]').click(); await page.locator('.icon-strip-project-mate[data-mate-id="' + info.ordinaryMates[0].id + '"]').click(); var ordinaryFrame = page.frameLocator('#contextual-clay iframe:visible'); await ordinaryFrame.locator('#input-wrapper:not([inert])').waitFor(); await ordinaryFrame.locator('#messages').getByText('ORDINARY_MATE_0_PRIVATE_HISTORY', { exact: false }).waitFor(); var ordinary = page.frames().find(function (item) { return item.url().includes(info.ordinaryMates[0].slug); }); await ordinary.evaluate(async function () { window.snapshotOrdinaryStore = (await import('./modules/store.js')).store; }); await ordinary.waitForFunction(function () { return window.snapshotOrdinaryStore.get('myUserId') === 'owner' && window.snapshotOrdinaryStore.get('connected') && window.snapshotOrdinaryStore.get('contextualSessionReady'); }); var ordinaryResult = await ordinary.evaluate(async function (snapshot) { var store = (await import('./modules/store.js')).store; var ws = (await import('./modules/ws-ref.js')).getWs(); return new Promise(function (resolve) { function receive(event) { var value = JSON.parse(event.data); if (value.type === 'error') { ws.removeEventListener('message', receive); resolve(value); } } ws.addEventListener('message', receive); ws.send(JSON.stringify({ type: 'message', text: 'Ordinary metadata spoof', clientMessageId: 'ordinary-snapshot-spoof', projectSlug: store.get('currentSlug'), sessionId: store.get('activeSessionId'), sessionOriginId: store.get('sessionOriginId'), accountId: store.get('myUserId'), contextualSnapshot: snapshot })); }); }, first.contextualSnapshot); assert.match(ordinaryResult.text, /private canonical Clay/); assert.equal(await ordinaryFrame.locator('#messages').getByText('Ordinary metadata spoof', { exact: true }).count(), 0); assert.equal(new URL(page.url()).pathname, '/p/alpha/'); });