Oregami
Repositories/oxedyne/daimond

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.
6import { open, chat, mockLog, clearMockLog, contentText, connectMock } from './harness.mjs';
7
8const s = await open({ name: 'skillsau4', defaults: false });
9const { page } = s;
10const msgs = [];
11page.on('console', m => { if (/AUD/.test(m.text())) msgs.push(m.text()); });
12page.on('pageerror', e => msgs.push('PAGEERROR ' + String(e).slice(0,160)));
13await page.waitForTimeout(2000);
14await connectMock(s);
15await chat(s, 'hello'); // a chat exists and is in focus
16await page.waitForTimeout(600);
17
18const out = {};
19const 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/.
24out.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.
35clearMockLog();
36const before = await chat(s, '/brief');
37out.before = { refused: /no skill called/i.test(before), reachedModel: mockLog().length };
38
39// The `/` menu, and the draft row in it.
40await page.fill('#chat-input', '/');
41await page.waitForTimeout(1500);
42out.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.
49out.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});
59out.aud = msgs.slice(-8);
60if (!out.menu.drafts.length) { console.log(JSON.stringify(out, null, 1)); await s.browser.close(); process.exit(1); }
61await page.evaluate(() => document.querySelector('.skill-menu .skill-item[data-draft]').click());
62await page.waitForTimeout(900);
63out.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});
67await 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});
72await page.waitForTimeout(1500);
73
74out.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.
84await page.fill('#chat-input', '');
85clearMockLog();
86await chat(s, '/brief the sync bug');
87const log = mockLog();
88const usr = (r) => (r.messages || []).filter(m => m.role === 'user').map(m => contentText(m.content)).join('\n');
89out.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
96console.log(JSON.stringify(out, null, 1));
97await s.browser.close();