oxedyne/daimond/dev/probe_skillsau4.mjs
4.8 KiB, 1 run
created by r2519314175:83, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | // probe_skillsau4.mjs — a daimon drafts, the owner installs, in the real page. |
| 2 | // |
| 3 | // The draft is written with the FILE TOOLS a fenced turn already has, into the chat's own |
| 4 | // folder, exactly as the standing note tells a daimon to. Then the `/` menu is opened, the |
| 5 | // draft row is tapped, and the dialog answered — and what a `/name` then reaches is checked. |
| 6 | import { open, chat, mockLog, clearMockLog, contentText, connectMock } from './harness.mjs'; |
| 7 | |
| 8 | const s = await open({ name: 'skillsau4', defaults: false }); |
| 9 | const { page } = s; |
| 10 | const msgs = []; |
| 11 | page.on('console', m => { if (/AUD/.test(m.text())) msgs.push(m.text()); }); |
| 12 | page.on('pageerror', e => msgs.push('PAGEERROR ' + String(e).slice(0,160))); |
| 13 | await page.waitForTimeout(2000); |
| 14 | await connectMock(s); |
| 15 | await chat(s, 'hello'); // a chat exists and is in focus |
| 16 | await page.waitForTimeout(600); |
| 17 | |
| 18 | const out = {}; |
| 19 | const DRAFT = '---\nname: brief\ndescription: Brief a worker properly.\n---\n' |
| 20 | + '# Brief\n\nName the files, the command that proves it, and what to do when it fails.\n'; |
| 21 | |
| 22 | // A daimon writes the draft the way the standing note tells it to, through the ordinary |
| 23 | // file tools and inside the fence — never into .daimond/. |
| 24 | out.wrote = await page.evaluate(async (text) => { |
| 25 | const m = await import('/pkg/oxedyne_daimond.js'); |
| 26 | const f = window.DaimondAttach.focus(); |
| 27 | const own = (f.kind === 'diamond') ? ('diamonds/' + f.id) : window.DaimondAttach.chatScratch(f.id); |
| 28 | const dir = own + '/' + m.skill_drafts_dir(); |
| 29 | await m.store_write(dir + '/brief.md', text); |
| 30 | return { focus: f.kind, path: dir + '/brief.md', install: m.skill_install_path('brief'), |
| 31 | refusal: m.skill_draft_refusal('brief', text) }; |
| 32 | }, DRAFT); |
| 33 | |
| 34 | // Before: /brief resolves to nothing at all. |
| 35 | clearMockLog(); |
| 36 | const before = await chat(s, '/brief'); |
| 37 | out.before = { refused: /no skill called/i.test(before), reachedModel: mockLog().length }; |
| 38 | |
| 39 | // The `/` menu, and the draft row in it. |
| 40 | await page.fill('#chat-input', '/'); |
| 41 | await page.waitForTimeout(1500); |
| 42 | out.menu = await page.evaluate(() => { |
| 43 | const rows = [...document.querySelectorAll('.skill-menu .skill-item')]; |
| 44 | return { rows: rows.map(r => r.textContent), |
| 45 | drafts: rows.filter(r => r.hasAttribute('data-draft')).map(r => r.getAttribute('data-draft')) }; |
| 46 | }); |
| 47 | |
| 48 | // One tap on the draft row, and one on Install. |
| 49 | out.listing = await page.evaluate(async () => { |
| 50 | const m = await import('/pkg/oxedyne_daimond.js'); |
| 51 | const f = window.DaimondAttach.focus(); |
| 52 | const dir = window.DaimondAttach.chatScratch(f.id) + '/' + m.skill_drafts_dir(); |
| 53 | let raw = 'ERR'; |
| 54 | try { raw = String(await m.store_list(dir) || ''); } catch (e) { raw = 'THREW ' + e; } |
| 55 | let up = 'ERR'; |
| 56 | try { up = String(await m.store_list(window.DaimondAttach.chatScratch(f.id)) || ''); } catch (e) { up = 'THREW ' + e; } |
| 57 | return { dir, raw, up }; |
| 58 | }); |
| 59 | out.aud = msgs.slice(-8); |
| 60 | if (!out.menu.drafts.length) { console.log(JSON.stringify(out, null, 1)); await s.browser.close(); process.exit(1); } |
| 61 | await page.evaluate(() => document.querySelector('.skill-menu .skill-item[data-draft]').click()); |
| 62 | await page.waitForTimeout(900); |
| 63 | out.dialog = await page.evaluate(() => { |
| 64 | const d = document.querySelector('.modal.dlg'); |
| 65 | return d ? { text: d.innerText.slice(0, 400), buttons: [...d.querySelectorAll('button')].map(b => b.textContent) } : null; |
| 66 | }); |
| 67 | await page.evaluate(() => { |
| 68 | const d = document.querySelector('.modal.dlg'); |
| 69 | const b = [...d.querySelectorAll('button')].find(x => /install/i.test(x.textContent)); |
| 70 | if (b) b.click(); |
| 71 | }); |
| 72 | await page.waitForTimeout(1500); |
| 73 | |
| 74 | out.installed = await page.evaluate(async () => { |
| 75 | const m = await import('/pkg/oxedyne_daimond.js'); |
| 76 | let at = ''; try { at = String(await m.store_read('.daimond/skills/brief.md') || ''); } catch (e) { at = 'ERR ' + e; } |
| 77 | let draft = 'still there'; |
| 78 | try { draft = String(await m.store_read((window.DaimondAttach.chatScratch(window.DaimondAttach.focus().id)) + '/' + m.skill_drafts_dir() + '/brief.md') || ''); } catch (e) { draft = ''; } |
| 79 | return { bytes: at.length, matches: at === '---\nname: brief\ndescription: Brief a worker properly.\n---\n# Brief\n\nName the files, the command that proves it, and what to do when it fails.\n', |
| 80 | draftLeft: draft.length === 0 ? 'gone or emptied' : draft.slice(0, 20) }; |
| 81 | }); |
| 82 | |
| 83 | // After: /brief now reaches the model carrying the installed file's own text. |
| 84 | await page.fill('#chat-input', ''); |
| 85 | clearMockLog(); |
| 86 | await chat(s, '/brief the sync bug'); |
| 87 | const log = mockLog(); |
| 88 | const usr = (r) => (r.messages || []).filter(m => m.role === 'user').map(m => contentText(m.content)).join('\n'); |
| 89 | out.after = log.length ? { |
| 90 | requests: log.length, |
| 91 | named: /\.daimond\/skills\/brief\.md/.test(usr(log[0])), |
| 92 | body: /Name the files, the command that proves it/.test(usr(log[0])), |
| 93 | request: /User request: the sync bug/.test(usr(log[0])), |
| 94 | } : { requests: 0 }; |
| 95 | |
| 96 | console.log(JSON.stringify(out, null, 1)); |
| 97 | await s.browser.close(); |