Skip to content

Commit eaa13b8

Browse files
committed
Release v0.2.32
1 parent f93ecd4 commit eaa13b8

278 files changed

Lines changed: 25570 additions & 6585 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎RELEASE.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
2-
"version": "0.2.31",
2+
"version": "0.2.32",
33
"license": "FSL-1.1-ALv2",
44
"baselineSha256": "7847f65a9b20b366a60d52d19ca3cd6bdd1bb09b8cb506659a270f27a21d8877",
55
"e2b": {
66
"recipeSha256": "761809ee2efa2b6c750451ace29283cbb8e55b6ce3b5b14c2bb4509df71df801",
77
"profileSha256": "414dbecee2d47de36016ea05c736e2edff3a0deb298babf923887ea184a01c10"
88
},
99
"dxd": {
10-
"url": "https://github.com/dxcode-dev/dxcode/releases/download/v0.2.31/dxd-linux-x64",
11-
"sha256": "b7fa901b77be3b60892fbe5be563a5c3cdf8d5ec4aea6ddce03119e833ae5e49"
10+
"url": "https://github.com/dxcode-dev/dxcode/releases/download/v0.2.32/dxd-linux-x64",
11+
"sha256": "0d84c34e348d585c0367f87dbbf1c0a1d611be13f10aa96507b3fe5673f63b3a"
1212
},
1313
"toolchain": {
1414
"node": ">=22.19.0",

‎THIRD_PARTY_NOTICES.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,29 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
5555
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
5656
SOFTWARE.
5757

58+
## dx view kit icons
59+
60+
The `icons.svg` symbols in `packages/api/src/views/kit/` are Lucide icons:
61+
62+
- Source: <https://github.com/lucide-icons/lucide>
63+
- License: ISC
64+
65+
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part
66+
of Feather (MIT). All other copyright (c) for Lucide are held by Lucide
67+
Contributors 2022.
68+
69+
Permission to use, copy, modify, and/or distribute this software for any
70+
purpose with or without fee is hereby granted, provided that the above
71+
copyright notice and this permission notice appear in all copies.
72+
73+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
74+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
75+
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
76+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
77+
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
78+
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
79+
THIS SOFTWARE.
80+
5881
## Pi coding agent
5982

6083
The baseline dx agent prompt in

‎apps/core/migration-manifest.json‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@
7272
"filename": "0012_release_v0.2.23.sql",
7373
"sha256": "cefa0b2bf09c97668d19c7f81fc53f7915d8b86a2ea321aa27dde33a2d72db7e",
7474
"classification": "non-destructive"
75+
},
76+
{
77+
"filename": "0013_release_v0.2.32.sql",
78+
"sha256": "c5a0354f0bea618d480b6e3e7eeb4f6e45672f5ddce7215fe8682cb2024e8460",
79+
"classification": "non-destructive"
7580
}
7681
],
7782
"durableObjects": [
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
-- Browser sessions that ended within the last hour. Better Auth's signed
2+
-- session cookie cache (auth/better-auth.ts, one hour) lets a browser skip
3+
-- D1, so a deleted session row alone does not stop a browser holding a warm
4+
-- cache. Core checks this record on every cache hit
5+
-- (auth/session-revocation.ts). The trigger records every deletion however it
6+
-- happens: ending one or all other sessions, signing out, expiry cleanup, or
7+
-- a deleted user. Rows older than the cache max age protect nothing and are
8+
-- pruned on the next deletion.
9+
CREATE TABLE session_revocation (
10+
session_id TEXT PRIMARY KEY,
11+
ended_at INTEGER NOT NULL
12+
) WITHOUT ROWID;
13+
14+
CREATE INDEX session_revocation_ended_at ON session_revocation (ended_at);
15+
16+
CREATE TRIGGER session_revocation_record
17+
AFTER DELETE ON "session"
18+
BEGIN
19+
DELETE FROM session_revocation
20+
WHERE ended_at < CAST(unixepoch('subsec') * 1000 AS INTEGER) - 3600000;
21+
INSERT INTO session_revocation (session_id, ended_at)
22+
VALUES (OLD.id, CAST(unixepoch('subsec') * 1000 AS INTEGER))
23+
ON CONFLICT (session_id) DO UPDATE SET ended_at = excluded.ended_at;
24+
END;
25+
26+
-- Dictation vocabulary: names, technical terms, and other words dictation
27+
-- should recognize. One list per person and one per workspace; a
28+
-- transcription is biased toward both, personal terms first
29+
-- (dictation/vocabulary.ts). `terms` is a JSON array of at most 50 terms of
30+
-- at most 64 characters, validated by the route before it is written.
31+
CREATE TABLE dictation_vocabulary (
32+
scope TEXT NOT NULL CHECK (scope IN ('personal', 'workspace')),
33+
target_id TEXT NOT NULL CHECK (length(target_id) BETWEEN 1 AND 128),
34+
terms TEXT NOT NULL CHECK (json_valid(terms) AND json_type(terms) = 'array' AND json_array_length(terms) <= 50),
35+
updated_by TEXT NOT NULL,
36+
updated_at TEXT NOT NULL,
37+
PRIMARY KEY (scope, target_id)
38+
) WITHOUT ROWID;
39+
40+
-- A Thread its owner snoozed: hidden in the sidebar's Snoozed section until
41+
-- `snoozed_until`, then back in its usual section marked Woke until the owner
42+
-- opens it. Nothing runs at the wake time; readers compare it with the clock.
43+
-- `snoozed_at` is when the owner snoozed it. Waking clears both and records
44+
-- `snooze_woke_at`, so the time asleep does not count toward Inactive. Only
45+
-- active root Threads are snoozed; archiving clears the snooze.
46+
ALTER TABLE threads ADD COLUMN snoozed_until TEXT;
47+
ALTER TABLE threads ADD COLUMN snoozed_at TEXT;
48+
ALTER TABLE threads ADD COLUMN snooze_woke_at TEXT;

‎apps/core/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@dx/core",
33
"private": true,
4-
"version": "0.2.31",
4+
"version": "0.2.32",
55
"license": "FSL-1.1-ALv2",
66
"type": "module",
77
"scripts": {

‎apps/core/src/agents/built-in-skill-names.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const DX_WORKSPACE_SKILL = "dx-workspace";
55
export const BUILT_IN_SKILL_NAMES = [
66
"browser-use",
77
"building-skills",
8+
"creating-views",
89
"explaining-code",
910
"orb-setup",
1011
"proving-changes",

‎apps/core/src/agents/built-in-skills.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import viewIcons from "@dx/api/views/kit/icons.svg?raw";
2+
import viewKitCss from "@dx/api/views/kit/kit.css?raw";
3+
import viewKitJs from "@dx/api/views/kit/kit.js?raw";
4+
import viewFrame from "@dx/api/views/view-frame.mjs?raw";
15
import { defineSkill, type Sandbox, type SkillDefinition } from "@flue/runtime";
26
import { parse } from "yaml";
37
import { encodeBase64 } from "../encoding/base64.js";
@@ -8,6 +12,8 @@ import {
812
} from "./built-in-skill-names.js";
913
import browserUse from "./built-in-skills/browser-use/skill.md?raw";
1014
import buildingSkills from "./built-in-skills/building-skills/skill.md?raw";
15+
import creatingViewsPreview from "./built-in-skills/creating-views/scripts/preview.mjs?raw";
16+
import creatingViews from "./built-in-skills/creating-views/skill.md?raw";
1117
import explainingCode from "./built-in-skills/explaining-code/skill.md?raw";
1218
import orbSetup from "./built-in-skills/orb-setup/skill.md?raw";
1319
import provingChangesAgentInstructions from "./built-in-skills/proving-changes/reference/agent-instructions.md?raw";
@@ -39,6 +45,19 @@ const sources: ReadonlyArray<{
3945
}> = [
4046
{ name: "browser-use", files: { "SKILL.md": browserUse } },
4147
{ name: "building-skills", files: { "SKILL.md": buildingSkills } },
48+
{
49+
name: "creating-views",
50+
// The frame and kit are the web app's own (@dx/api/views), so a preview
51+
// frames a view exactly as the transcript does.
52+
files: {
53+
"SKILL.md": creatingViews,
54+
"scripts/preview.mjs": creatingViewsPreview,
55+
"scripts/view-frame.mjs": viewFrame,
56+
"scripts/kit/kit.css": viewKitCss,
57+
"scripts/kit/kit.js": viewKitJs,
58+
"scripts/kit/icons.svg": viewIcons,
59+
},
60+
},
4261
{ name: "explaining-code", files: { "SKILL.md": explainingCode } },
4362
{ name: "orb-setup", files: { "SKILL.md": orbSetup } },
4463
{

‎apps/core/src/agents/built-in-skills/browser-use/skill.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ Report as a visual transcript, in order: one line per action describing what was
264264
clicked, with the non-sensitive values named (say that a password, card number, or code was
265265
entered without reproducing it; card numbers by last four digits only), the confirmation with its
266266
reference or order number quoted as text, the fields you could not fill and why, and links to the
267-
screenshots, recording, and contact sheet. The transcript does not render workspace images
268-
inline: link each file as `[Order confirmation](file:///home/user/artifacts/browser-use/acme-checkout/order-confirmation.png)`,
269-
which opens it in the Thread's file viewer.
267+
screenshots, recording, and contact sheet. Show each capture inline
268+
with a Markdown image of its absolute path, such as
269+
`![Order confirmation](/home/user/artifacts/browser-use/acme-checkout/order-confirmation.png)`;
270+
recordings (`.webm`, `.mp4`) play in place the same way.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env node
2+
// Previews a dx-view document the way the dx web app frames it: view-frame.mjs and kit/ are the
3+
// same files the web app uses, so the CSP, theme tokens, kit, and bridge match. Screenshots the
4+
// view at the reply width (720px) and at phone width (360px) in light and dark themes, and prints
5+
// page errors. Needs agent-browser (see the using-agent-browser skill).
6+
//
7+
// Usage: node preview.mjs <view.html> [output-dir] [--palette daydream|deadpan]
8+
import { execFileSync } from "node:child_process";
9+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
10+
import { basename, dirname, join, resolve } from "node:path";
11+
import { fileURLToPath } from "node:url";
12+
import { viewDocument } from "./view-frame.mjs";
13+
14+
const args = process.argv.slice(2);
15+
const paletteFlag = args.indexOf("--palette");
16+
const palette = paletteFlag === -1 ? "daydream" : args[paletteFlag + 1];
17+
const [input, output] = args.filter(
18+
(_, index) =>
19+
paletteFlag === -1 || (index !== paletteFlag && index !== paletteFlag + 1),
20+
);
21+
if (!input) {
22+
console.error(
23+
"Usage: node preview.mjs <view.html> [output-dir] [--palette daydream|deadpan]",
24+
);
25+
process.exit(2);
26+
}
27+
const html = readFileSync(input, "utf8");
28+
const kitDir = join(dirname(fileURLToPath(import.meta.url)), "kit");
29+
const kit = {
30+
css: readFileSync(join(kitDir, "kit.css"), "utf8"),
31+
js: readFileSync(join(kitDir, "kit.js"), "utf8"),
32+
icons: readFileSync(join(kitDir, "icons.svg"), "utf8"),
33+
};
34+
const outDir = resolve(output ?? `/tmp/dx-view-preview-${Date.now()}`);
35+
mkdirSync(outDir, { recursive: true });
36+
37+
const session = `dx-view-${process.pid}`;
38+
const browser = (...command) =>
39+
execFileSync("agent-browser", ["--session", session, ...command], {
40+
encoding: "utf8",
41+
stdio: ["ignore", "pipe", "ignore"],
42+
});
43+
44+
const name = basename(input).replace(/\.html?$/i, "");
45+
let failed = false;
46+
try {
47+
for (const scheme of ["light", "dark"]) {
48+
const file = join(outDir, `${name}-${scheme}.html`);
49+
writeFileSync(file, viewDocument(html, { kit, palette, scheme }));
50+
for (const width of [720, 360]) {
51+
browser("set", "viewport", String(width), "900", "2");
52+
browser("open", `file://${file}`);
53+
browser("wait", "1400");
54+
const height = browser(
55+
"eval",
56+
"Math.ceil(document.documentElement.getBoundingClientRect().height)",
57+
);
58+
const shot = join(outDir, `${name}-${scheme}-${width}.png`);
59+
// Crop to the content, like the inline frame does (up to its 1200px limit).
60+
browser(
61+
"set",
62+
"viewport",
63+
String(width),
64+
String(Math.min(1200, Number(height) + 8)),
65+
"2",
66+
);
67+
browser("screenshot", shot);
68+
const errors = JSON.parse(browser("errors", "--json")).data?.errors ?? [];
69+
console.log(
70+
`${scheme} ${width}px: ${shot} (content height ${height.trim()}px)`,
71+
);
72+
for (const error of errors) {
73+
failed = true;
74+
console.log(` error: ${error.text} (line ${error.line})`);
75+
}
76+
browser("errors", "--clear");
77+
}
78+
}
79+
} finally {
80+
try {
81+
browser("close");
82+
} catch {}
83+
}
84+
if (failed) process.exitCode = 1;

0 commit comments

Comments
 (0)