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:
@@ -13,6 +13,7 @@ import type {
|
||||
CompactionMode,
|
||||
CompactionReason,
|
||||
EventMessage,
|
||||
Fidelity,
|
||||
ImageUrlPayload,
|
||||
InlineDataPayload,
|
||||
InlineThinkingPayload,
|
||||
@@ -61,30 +62,28 @@ export function sessionMeta(payload: SessionMetaPayload): SessionMetaMessage {
|
||||
// Complete model_msg -----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Provider fidelity fields: kept as-is and restored verbatim on replay.
|
||||
* Builder convention: positional-argument-style builders carry these in a trailing `fidelity`
|
||||
* object (narrowed via Pick per payload type — e.g. thinking only has signature); object-argument-
|
||||
* style builders (toolCall) flatten `fidelity` fields into the parameter object alongside
|
||||
* `stopReason`, mirroring the payload structure directly.
|
||||
* Provider-fidelity payload (opaque, see `Fidelity` in types.ts): kept as-is and restored
|
||||
* verbatim on replay. Builder convention: positional-argument-style builders take it as a
|
||||
* trailing `fidelity` object; object-argument-style builders (toolCall) carry it in the
|
||||
* parameter object alongside `stopReason`. An empty object is treated as absent — the
|
||||
* payload field is only set when the fidelity carries at least one key.
|
||||
*/
|
||||
export interface FidelityFields {
|
||||
phase?: string | null;
|
||||
signature?: string;
|
||||
function fidelityProp(fidelity?: Fidelity): { fidelity?: Fidelity } {
|
||||
return fidelity !== undefined && Object.keys(fidelity).length > 0 ? { fidelity } : {};
|
||||
}
|
||||
|
||||
export function textMessage(
|
||||
role: Role,
|
||||
text: string,
|
||||
stopReason: StopReason = "completed",
|
||||
fidelity?: FidelityFields,
|
||||
fidelity?: Fidelity,
|
||||
): OmniMessage<TextPayload> {
|
||||
return model({
|
||||
type: "text",
|
||||
role,
|
||||
text,
|
||||
stop_reason: stopReason,
|
||||
...(fidelity?.phase != null ? { phase: fidelity.phase } : {}),
|
||||
...(fidelity?.signature !== undefined ? { signature: fidelity.signature } : {}),
|
||||
...fidelityProp(fidelity),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -93,7 +92,7 @@ export const userText = (text: string): OmniMessage<TextPayload> => textMessage(
|
||||
export const assistantText = (
|
||||
text: string,
|
||||
stopReason: StopReason = "completed",
|
||||
fidelity?: FidelityFields,
|
||||
fidelity?: Fidelity,
|
||||
): OmniMessage<TextPayload> => textMessage("assistant", text, stopReason, fidelity);
|
||||
|
||||
export function imageUrlMessage(imageUrl: string): OmniMessage<ImageUrlPayload> {
|
||||
@@ -109,7 +108,7 @@ export function inlineData(
|
||||
role: Role,
|
||||
data: string,
|
||||
mimeType: string,
|
||||
fidelity?: Pick<FidelityFields, "signature">,
|
||||
fidelity?: Fidelity,
|
||||
): OmniMessage<InlineDataPayload> {
|
||||
return model({
|
||||
type: "inline_data",
|
||||
@@ -117,28 +116,28 @@ export function inlineData(
|
||||
data,
|
||||
mime_type: mimeType,
|
||||
stop_reason: "completed",
|
||||
...(fidelity?.signature !== undefined ? { signature: fidelity.signature } : {}),
|
||||
...fidelityProp(fidelity),
|
||||
});
|
||||
}
|
||||
|
||||
export function thinkingMessage(
|
||||
thinking: string,
|
||||
stopReason: StopReason = "completed",
|
||||
fidelity?: Pick<FidelityFields, "signature">,
|
||||
fidelity?: Fidelity,
|
||||
): OmniMessage<ThinkingPayload> {
|
||||
return model({
|
||||
type: "thinking",
|
||||
role: "assistant",
|
||||
thinking,
|
||||
stop_reason: stopReason,
|
||||
...(fidelity?.signature !== undefined ? { signature: fidelity.signature } : {}),
|
||||
...fidelityProp(fidelity),
|
||||
});
|
||||
}
|
||||
|
||||
export function inlineThinking(
|
||||
data: string,
|
||||
mimeType: string,
|
||||
fidelity?: Pick<FidelityFields, "signature">,
|
||||
fidelity?: Fidelity,
|
||||
): OmniMessage<InlineThinkingPayload> {
|
||||
return model({
|
||||
type: "inline_thinking",
|
||||
@@ -146,7 +145,7 @@ export function inlineThinking(
|
||||
data,
|
||||
mime_type: mimeType,
|
||||
stop_reason: "completed",
|
||||
...(fidelity?.signature !== undefined ? { signature: fidelity.signature } : {}),
|
||||
...fidelityProp(fidelity),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -155,7 +154,7 @@ export function toolCall(args: {
|
||||
arguments: string;
|
||||
toolCallId: string;
|
||||
stopReason?: StopReason;
|
||||
signature?: string;
|
||||
fidelity?: Fidelity;
|
||||
}): OmniMessage<ToolCallPayload> {
|
||||
return model({
|
||||
type: "tool_call",
|
||||
@@ -164,7 +163,7 @@ export function toolCall(args: {
|
||||
arguments: args.arguments,
|
||||
tool_call_id: args.toolCallId,
|
||||
stop_reason: args.stopReason ?? "completed",
|
||||
...(args.signature !== undefined ? { signature: args.signature } : {}),
|
||||
...fidelityProp(args.fidelity),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -99,15 +99,23 @@ export interface SessionMetaPayload {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Docs: /docs/omni-message § "model_msg: complete payloads"
|
||||
|
||||
/**
|
||||
* Provider-fidelity payload (mirrors AgentHub's `Fidelity`): an arbitrary JSON-style object of
|
||||
* wire-level data the LLM client records to reproduce the original message on replay — thinking
|
||||
* signatures, phase labels, encrypted reasoning, the upstream reasoning field name, etc. Opaque
|
||||
* to PenguinHarness: written to the Trace as-is and passed back verbatim; some models **require**
|
||||
* it when history is replayed (e.g. Claude thinking signatures, GPT-5 encrypted reasoning) —
|
||||
* losing it breaks Session resumption.
|
||||
*/
|
||||
export type Fidelity = Record<string, unknown>;
|
||||
|
||||
export interface TextPayload {
|
||||
type: "text";
|
||||
role: Role;
|
||||
text: string;
|
||||
stop_reason?: StopReason;
|
||||
/** Provider fidelity field: text phase marker (e.g. GPT-5 segments by phase), kept as-is and restored verbatim. */
|
||||
phase?: string | null;
|
||||
/** Provider fidelity field: signature, kept as-is and restored verbatim. */
|
||||
signature?: string;
|
||||
/** Provider-fidelity payload (e.g. `phase` for GPT-5 segment markers, `signature`), kept as-is and restored verbatim. */
|
||||
fidelity?: Fidelity;
|
||||
}
|
||||
|
||||
export interface ImageUrlPayload {
|
||||
@@ -125,8 +133,8 @@ export interface InlineDataPayload {
|
||||
data: string;
|
||||
mime_type: string;
|
||||
stop_reason?: StopReason;
|
||||
/** Provider fidelity field: signature, kept as-is and restored verbatim. */
|
||||
signature?: string;
|
||||
/** Provider-fidelity payload, kept as-is and restored verbatim. */
|
||||
fidelity?: Fidelity;
|
||||
}
|
||||
|
||||
export interface ThinkingPayload {
|
||||
@@ -135,11 +143,12 @@ export interface ThinkingPayload {
|
||||
thinking: string;
|
||||
stop_reason?: StopReason;
|
||||
/**
|
||||
* Provider fidelity field: thinking-block signature (Claude thinking blocks / redacted
|
||||
* thinking, GPT-5 encrypted reasoning, etc. — **required** when some models replay history),
|
||||
* kept as-is and restored verbatim — losing it breaks Session resumption.
|
||||
* Provider-fidelity payload closing the thinking block (Claude thinking signatures / redacted
|
||||
* thinking, GPT-5 encrypted reasoning, the OpenAI-compatible reasoning field name, etc. —
|
||||
* **required** when some models replay history), kept as-is and restored verbatim — losing it
|
||||
* breaks Session resumption.
|
||||
*/
|
||||
signature?: string;
|
||||
fidelity?: Fidelity;
|
||||
}
|
||||
|
||||
export interface InlineThinkingPayload {
|
||||
@@ -149,8 +158,8 @@ export interface InlineThinkingPayload {
|
||||
data: string;
|
||||
mime_type: string;
|
||||
stop_reason?: StopReason;
|
||||
/** Provider fidelity field: signature, kept as-is and restored verbatim. */
|
||||
signature?: string;
|
||||
/** Provider-fidelity payload, kept as-is and restored verbatim. */
|
||||
fidelity?: Fidelity;
|
||||
}
|
||||
|
||||
export interface ToolCallPayload {
|
||||
@@ -161,8 +170,8 @@ export interface ToolCallPayload {
|
||||
arguments: string;
|
||||
tool_call_id: string;
|
||||
stop_reason?: StopReason;
|
||||
/** Provider fidelity field: signature, kept as-is and restored verbatim. */
|
||||
signature?: string;
|
||||
/** Provider-fidelity payload, kept as-is and restored verbatim. */
|
||||
fidelity?: Fidelity;
|
||||
}
|
||||
|
||||
export interface ToolCallOutputPayload {
|
||||
|
||||
Reference in New Issue
Block a user