Changelog, dev startup, README, AgentHub 0.4.0, model catalog, and landing site (#7)

Branch-length batch covering tooling, the model layer, the Web App and the public
surfaces. Highlights:

- Changelog: a per-release `changelog/<version>/` tree, grouped by the surface each
  change touches, with a root CHANGELOG.md holding one line per release.
- Dev startup: `scripts/dev-prebuild.mjs` serializes the skills+core prebuild behind a
  lock and keeps `pnpm install` current; `pnpm dev` runs server+web together.
- AgentHub 0.3.3 -> 0.4.0: OmniMessage complete payloads carry one opaque `fidelity`
  object in place of item-level `signature`/`phase`, threaded verbatim through Trace,
  replay and resume; malformed classification adapted to the new error types.
- Model layer: a model is always referenced by an explicit `(provider, model_id)` pair.
  The provider is never inferred, guessed or defaulted -- both the catalog inference and
  the unique-match config resolution are gone, and CLI, SDK, server routes and
  run_subagent all require the complete pair. Catalog gains the Qwen Token Plan, Qwen
  Pay-As-You-Go and Fireworks AI gateways, plus an expanded OpenRouter group.
- Web App: catalog preset sync and per-group speed test on the Models page, positional
  slash commands, a markdown renderer, skill-library update reminders, and a vertically
  centred draft page whose upward menus size themselves to the room available.
- Public surfaces: restructured READMEs, the penguin.ooo landing site and blog, refreshed
  benchmark results for both suites, and the demo videos playing on the landing page.

Includes the fixes from a full review of the branch: 23 confirmed findings, among them a
provider-inference bug that could send one vendor's API key to another vendor's endpoint,
and an Escape handler that destroyed the composer's contents unrecoverably.

Verified on the branch head: pnpm test (1127 passing, 7 packages), pnpm typecheck and
pnpm format:check clean, Playwright e2e 14/14.
This commit is contained in:
Yaowei Zheng authored and GitHub committed 2026-07-21 17:43:31 +08:00
1 parent abf0a2f248
commit d4faee3a1e
214 files changed
+9406 -1688

No files matched your search

+21 -6
View File
@@ -108,10 +108,13 @@ export function agentSessionsRoutes(deps: AppDeps): Hono<AppEnv> {
const body = await readJson(c);
const modelId = optionalString(body, "modelId", { minLen: 1, label: "modelId" });
const provider = optionalString(body, "provider", { minLen: 1, label: "provider" });
// Model reference is submitted as a pair: provider can't appear without modelId (core does the same validation; this catches it early).
if (provider !== undefined && modelId === undefined) {
// Model reference is submitted as a pair — both or neither. Neither half is ever
// inferred from the other, so half a reference is rejected here instead of being
// resolved (core does the same validation; this catches it early). Omitting both
// falls back to the Project's default model.
if ((modelId === undefined) !== (provider === undefined)) {
throw badRequest(
"provider is specified but modelId is not: a model reference must be given as a pair.",
"modelId and provider must be given together as a model reference pair: specify both, or neither to use the Project's default model.",
);
}
const approvalMode = optionalEnum(body, "approvalMode", APPROVAL_MODES);
@@ -361,6 +364,11 @@ export function sessionsRoutes(deps: AppDeps): Hono<AppEnv> {
const row = resolveSession(c);
const rel = c.req.query("path") ?? "";
const download = c.req.query("download") === "1";
// Sandboxed top-level preview ("open in a new tab" for html): the document keeps its REAL
// content type but carries a CSP sandbox WITHOUT allow-same-origin — it renders and runs
// fully in an opaque origin, so agent-generated markup cannot reach this origin's cookies
// or API. The request itself still authenticates (top-level GET sends the Lax cookie).
const preview = !download && c.req.query("preview") === "1";
const { data, fileName, contentType, scriptable } = await deps.workspaceFiles.read(
row.workspace,
rel,
@@ -368,15 +376,22 @@ export function sessionsRoutes(deps: AppDeps): Hono<AppEnv> {
const disposition = download ? "attachment" : "inline";
// Same-origin XSS defense: html/svg inline previews are always returned as plain
// text (Workspace files may be Agent-generated and untrusted); downloads
// (attachment) keep the real content type. Paired with nosniff to prevent MIME
// sniffing from undoing this.
const effectiveType = !download && scriptable ? "text/plain; charset=utf-8" : contentType;
// (attachment) keep the real content type, and sandboxed previews keep it under the
// CSP above. Paired with nosniff to prevent MIME sniffing from undoing this.
const effectiveType =
!download && scriptable && !preview ? "text/plain; charset=utf-8" : contentType;
return new Response(new Uint8Array(data), {
status: 200,
headers: {
"Content-Type": effectiveType,
"Content-Disposition": `${disposition}; filename*=UTF-8''${encodeURIComponent(fileName)}`,
"X-Content-Type-Options": "nosniff",
...(preview && scriptable
? {
"Content-Security-Policy":
"sandbox allow-scripts allow-popups allow-modals allow-forms",
}
: {}),
},
});
});