Release: CLI 4.0.2 and engine 0.1.1

Ship signed skill-bundle verification, fix annotated-session checkpoint ordering, and pin all published engine platform packages. Validated with Rust, Node, browser, and provider-backed end-to-end tests. AI assistance: prepared and validated with Codex under Paul Bakaus direction.
This commit is contained in:
Paul Bakaus
2026-09-05 10:47:32 -07:00
committed by GitHub
parent 8dac6ae7e0
commit eebfb7c2ce
13 changed files with 143 additions and 53 deletions
@@ -57,6 +57,12 @@
"readyPattern": "Local:\\s+https?://[^:]+:(\\d+)",
"readyTimeoutMs": 120000,
"steer": false,
"liveChrome": {
"annotations": {
"selector": "h1.hero-title",
"uploadDelayMs": 300
}
},
"pickSelector": "ul.expense-list",
"pickPosition": {
"x": 10,
+42 -1
View File
@@ -2,12 +2,53 @@ import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { runInNewContext } from 'node:vm';
const SOURCE = readFileSync(join(process.cwd(), 'skill/scripts/live-browser.js'), 'utf-8');
const PENDING_DOCK_POSITION_SOURCE = SOURCE.match(/function positionPendingDock\(\) \{[\s\S]*?\n \}/)?.[0] || '';
const CAPTURE_AND_EMIT_SOURCE = SOURCE.match(/async function captureAndEmit\([\s\S]*?\n \}/)?.[0] || '';
describe('live-browser source contracts', () => {
it('does not checkpoint a generation before captureAndEmit creates its session', () => {
for (const name of ['handleGo', 'handleInsertCreate']) {
const body = SOURCE.match(new RegExp(`function ${name}\\(\\) \\{[\\s\\S]*?\\n \\}`))?.[0];
assert.ok(body);
assert.doesNotMatch(body, /sendCheckpoint\('generate_started'\)/);
}
});
for (const annotated of [false, true]) {
for (const outcome of ['created', 'failed', 'superseded']) {
it(`${annotated ? 'annotated' : 'plain'} generation checkpoints only its acknowledged current session (${outcome})`, async () => {
const capture = Promise.withResolvers();
const creation = Promise.withResolvers();
const events = [];
const context = {
currentSessionId: 'session-a', state: 'GENERATING', PORT: 1234, TOKEN: 'test',
console, Date,
captureElementToBlob: () => capture.promise,
showShaderOverlay() {},
fetch: async () => ({ ok: true, json: async () => ({ path: '/annotation.png' }) }),
sendEvent: async (payload) => { events.push(payload.type); return creation.promise; },
sendCheckpoint: (reason) => events.push(reason),
};
const emit = runInNewContext(`(${CAPTURE_AND_EMIT_SOURCE})`, context);
const pending = emit({}, { type: 'generate', id: 'session-a' }, {
comments: annotated ? [{ text: 'change title' }] : [], strokes: [],
}, {});
await new Promise(resolve => setImmediate(resolve));
assert.deepEqual(events, annotated ? [] : ['generate']);
capture.resolve({ blob: {}, paper: 'white' });
await new Promise(resolve => setImmediate(resolve));
assert.deepEqual(events, ['generate'], 'capture/upload must not checkpoint before creation is acknowledged');
if (outcome === 'superseded') context.currentSessionId = 'session-b';
creation.resolve(outcome === 'failed' ? null : { ok: true });
await pending;
assert.deepEqual(events, outcome === 'created' ? ['generate', 'generate_started'] : ['generate']);
});
}
}
it('reports foreground poll connectivity without a background worker dependency', () => {
assert.match(
SOURCE,
@@ -29,7 +70,7 @@ describe('live-browser source contracts', () => {
);
assert.match(
CAPTURE_AND_EMIT_SOURCE,
/if \(hasAnnotations\) \{[\s\S]*?basePayload\.clientSentAt = Date\.now\(\);\s*sendEvent\(screenshotPath \? \{ \.\.\.basePayload, screenshotPath \} : basePayload\);\s*\}/,
/if \(hasAnnotations\) \{[\s\S]*?basePayload\.clientSentAt = Date\.now\(\);\s*const created = await sendEvent\(screenshotPath \? \{ \.\.\.basePayload, screenshotPath \} : basePayload\);/,
'annotated generation should dispatch exactly after capture and upload resolve',
);
});
+14
View File
@@ -7,6 +7,7 @@ import {
MANUAL_EDIT_SYSTEM_INSTRUCTIONS,
VARIANT_SYSTEM_INSTRUCTIONS,
createLlmAgent,
llmRequestSettings,
parseManualEditResponse,
parseVariantResponse,
progressiveVariantGuidance,
@@ -19,6 +20,19 @@ import {
validateVariantVisibleCopy,
} from './live-e2e/agents/llm-agent.mjs';
describe('live-e2e LLM request settings', () => {
it('explicitly selects low-effort DeepSeek thinking for bounded JSON edit requests', () => {
assert.deepEqual(llmRequestSettings('deepseek'), {
thinking: { type: 'enabled' }, output_config: { effort: 'low' },
});
});
it('leaves other providers unchanged', () => {
assert.deepEqual(llmRequestSettings('anthropic'), {});
assert.deepEqual(llmRequestSettings('openai'), {});
});
});
describe('live-e2e LLM agent provider config', () => {
it('defaults to OpenAI gpt-5.6-terra at medium reasoning effort', () => {
const config = resolveLlmAgentConfig({}, {});
+10 -6
View File
@@ -1283,17 +1283,21 @@ for (const { name, fixture } of fixtures) {
const pickSelector = annotation.selector || fixture.runtime.pickSelector || 'h1.hero-title';
try {
await waitForHandshake(page);
if (annotation.uploadDelayMs) {
await page.route('**/annotation?*', async (route) => {
await new Promise(resolve => setTimeout(resolve, annotation.uploadDelayMs));
await route.continue();
});
}
if (fixture.runtime.preActions) await runPreActions(page, fixture.runtime.preActions);
await pickElement(page, pickSelector, { resetPickMode: true });
await drawAnnotationPinAndStroke(page, {
comment: annotation.comment || 'Make this selected element easier to scan',
});
await clickGo(page);
await waitForCyclingRobust(page, 3, {
agentMode,
preActions: fixture.runtime.preActions,
log: (m) => t.diagnostic(m),
});
// A reload would mask a checkpoint-before-creation race by adopting
// the session again. Annotated generation must complete in this tab.
await waitForCycling(page, 3, { timeout: agentMode === 'llm' ? 180_000 : 30_000 });
const generateEvent = recordedGenerateEvents.at(-1);
await assertAnnotationUploadEvent(generateEvent);
@@ -1302,7 +1306,7 @@ for (const { name, fixture } of fixtures) {
const sourceFile = await locateSessionFile(session.appRoot);
const svelteComponentTarget = svelteComponentTargetFor(sourceFile);
await clickNext(page);
await cycleToVariant(page, 2, 3);
assert.equal(await getVisibleVariant(page), 2, 'variant 2 visible after annotated generate');
await clickAccept(page, { expectedVariant: 2 });
await waitForBarHidden(page);
+18 -1
View File
@@ -248,6 +248,17 @@ function resolveProvider(opts, env) {
return 'openai';
}
export function llmRequestSettings(provider) {
// DeepSeek defaults to high-effort thinking, which can consume the entire
// bounded response before emitting the JSON these edit tests exercise.
// Low effort retains planning for the full live spec without inheriting
// the provider's high-effort default.
// https://api-docs.deepseek.com/guides/thinking_mode/
return provider === 'deepseek'
? { thinking: { type: 'enabled' }, output_config: { effort: 'low' } }
: {};
}
/**
* Anthropic-SDK-shaped shim over the `ai` SDK for OpenAI models, so the
* three text-only call sites in this file stay provider-agnostic. system
@@ -331,6 +342,7 @@ export async function createLlmAgent(opts = {}) {
try {
response = await client.messages.create(
{
...llmRequestSettings(provider),
model,
temperature: 0,
max_tokens: 16000,
@@ -476,6 +488,7 @@ export async function createLlmAgent(opts = {}) {
try {
response = await client.messages.create(
{
...llmRequestSettings(provider),
model,
temperature: 0,
max_tokens: 16000,
@@ -605,11 +618,15 @@ export async function createLlmAgent(opts = {}) {
].join('\n');
const response = await client.messages.create({
...llmRequestSettings(provider),
model,
max_tokens: 4096,
system: systemBlocks(STEER_SYSTEM_INSTRUCTIONS),
messages: [{ role: 'user', content: userMessage }],
});
}, provider === 'deepseek' ? {
maxRetries: LLM_REQUEST_MAX_RETRIES,
timeout: MANUAL_EDIT_REQUEST_TIMEOUT_MS,
} : {});
const cacheRead = response.usage?.cache_read_input_tokens ?? 0;
const inputTokens = response.usage?.input_tokens ?? 0;
+7 -11
View File
@@ -26,8 +26,7 @@ import {
clickEditCopy,
clickExitLiveMode,
clickGo,
clickNext,
clickPrev,
cycleToVariant,
clickSaveEdit,
drawAnnotationPinAndStroke,
editTextLeaf,
@@ -40,6 +39,7 @@ import {
waitForBarHidden,
waitForCycling,
waitForHandshake,
waitForVariantSettled,
} from './live-e2e/ui.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -281,7 +281,7 @@ async function runAnnotationGenerateFlow({ page, tmp, evidence }) {
const generateEvent = latestJournalEvent(tmp, (event) => event.type === 'generate' && event.screenshotPath);
await assertAnnotationUploadEvent(generateEvent);
assert.ok(existsSync(generateEvent.screenshotPath), 'annotation screenshot file exists');
await clickNext(page);
await cycleTo(page, 2);
await assertVariantCounter(page, 2, 3);
await evidence.capture('annotation-cycle');
await clickDiscard(page);
@@ -514,14 +514,10 @@ async function clickPendingTrash(page) {
}
async function cycleTo(page, target) {
for (let i = 0; i < 6; i++) {
const visible = await getVisibleVariant(page);
if (visible === target) return;
if (visible == null) await page.waitForTimeout(250);
else if (visible < target) await clickNext(page);
else await clickPrev(page);
}
assert.equal(await getVisibleVariant(page), target, `variant ${target} visible`);
// Component imports finish after the counter changes. Do not send the next
// click (or reload) while the previous variant is still mounting.
await cycleToVariant(page, target, 3);
await waitForVariantSettled(page, target, 3);
}
async function waitForVisibleCycling(page, count, { timeout }) {