openvibers/openvibe.codes Commits

Describe Codes prompt layers (#360)

OP OpenVibers committed 3 days ago · 1c97df67f9 · 4 files, +16 −5

* Describe Codes prompt layers

* Store Codes prompt layers with run result

* Merge remote-tracking branch 'origin/main' into feat/s8-layered-prompts

Changes

ddiff --git a/engine/jobrunner.js b/engine/jobrunner.js
iindex c9e511f..082758d 100644
---- a/engine/jobrunner.js
++++ b/engine/jobrunner.js
@@@ -146,6 +146,8 @@ async function runAttempt(opts = {}) {
sessionId: state.sessionId,
model: st.model || opts.model || null,
summary: ((st.events || []).find((e) => e.t === 'final') || {}).text || '',
++ // Preserve the exact, inspectable prompt composition with the run result for later review.
++ promptLayers: prompt.parts,
usage: state.usage,
cost: state.cost,
steps: state.steps,
ddiff --git a/engine/prompt.js b/engine/prompt.js
iindex 7b0bf7e..1f362f1 100644
---- a/engine/prompt.js
++++ b/engine/prompt.js
@@@ -63,12 +63,14 @@ function assemble({ brief, dir, env = process.env, know } = {}) {
const houseRules = fragment('house-rules.md');
const repo = repomap(dir);
const learned = lessons({ file: know, repo: repo.repo, brief, env });
++ // Every prompt section is inspectable as a versioned layer. Stable layers stay first;
++ // the request is always last so changing the task leaves the cached prefix byte-identical.
const parts = [
-- { name: 'git-rule', text: gitRule },
-- { name: 'house-rules', text: houseRules },
-- { name: 'lessons', text: learned },
-- { name: 'repo-digest', text: repo.text },
-- { name: 'brief', text: String(brief || '') },
++ { name: 'git-rule', layer: 'platform', source: 'engine/fragments/git-rule.md', version: 1, text: gitRule },
++ { name: 'house-rules', layer: 'product', source: 'engine/fragments/house-rules.md', version: 1, text: houseRules },
++ { name: 'lessons', layer: 'role', source: 'Know recall', version: 1, text: learned },
++ { name: 'repo-digest', layer: 'role', source: 'engine/repomap.js', version: 1, text: repo.text },
++ { name: 'brief', layer: 'task', source: 'launch request', version: 1, text: String(brief || '') },
].filter((p) => p.text && p.text.trim());
return { text: parts.map((p) => p.text).join('\n\n'), parts };
}
ddiff --git a/test/engine-jobrunner.test.js b/test/engine-jobrunner.test.js
iindex 1c89a34..0225f74 100644
---- a/test/engine-jobrunner.test.js
++++ b/test/engine-jobrunner.test.js
@@@ -38,6 +38,9 @@ function fake(dir, name, lines) {
assert.equal(res.sessionId, 's-1');
assert.equal(res.summary, 'landed');
assert.equal(res.ok, true);
++ assert.equal(res.promptLayers.at(-1).layer, 'task');
++ assert.equal(res.promptLayers.at(-1).text, 'do it');
++ assert.ok(res.promptLayers.slice(0, -1).every((part) => part.source && part.version === 1));
assert.ok(fs.existsSync(res.log), 'the feed is kept');
fs.rmSync(dir, { recursive: true, force: true });
});
ddiff --git a/test/engine-prompt.test.js b/test/engine-prompt.test.js
iindex 9845741..8d68c84 100644
---- a/test/engine-prompt.test.js
++++ b/test/engine-prompt.test.js
@@@ -56,6 +56,10 @@ const { assemble, lessons, fragment } = require('../engine/prompt');
fs.writeFileSync(path.join(dir, 'README.md'), '# Demo\n\nA demo repository.\n');
const built = assemble({ brief: 'the brief', dir });
assert.deepStrictEqual(built.parts.map((p) => p.name), ['git-rule', 'house-rules', 'repo-digest', 'brief']);
++ assert.deepStrictEqual(built.parts.map((p) => p.layer), ['platform', 'product', 'role', 'task']);
++ assert.ok(built.parts.every((p) => p.source && p.version === 1));
++ const changed = assemble({ brief: 'different task', dir });
++ assert.deepStrictEqual(changed.parts.slice(0, -1), built.parts.slice(0, -1), 'changing task preserves the stable prompt prefix');
assert.ok(built.text.trimEnd().endsWith('the brief'), 'the brief is last');
fs.rmSync(dir, { recursive: true, force: true });
});