jenkins b66c762f5d hermes(webui): add HUX card UI models with contract-locked suites
Standalone per-card browser model/security/view modules for HUX-01..10
plus node+pytest suites that read the hux.v1 contract schemas directly.
Reconciled drift found on integration: the activity model now accepts
all 32 hux.event.v1 kinds (delegation.*, memory.suppressed,
memory.retrieval_removed, budget.exhausted, side_effect.*), the
autonomy model carries the external_side_effect capability, and the
foundation boundary test now asserts the shipped static HUX surface
exists on disk and that images never bake activated HUX_FLAGS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BvMSXH8VH2tMWXanb8SJdf
2026-08-24 04:12:03 -03:00

49 lines
2.6 KiB
TypeScript

/** Same-origin endpoint descriptors for the future HUX-05 backend. */
import { ACTIVITY_TIMELINE_FLAG, AUTONOMY_FLAG, FOUNDATION_FLAG } from "./model.ts";
import { isOpaqueId, normalizeIdentity, sameIdentity } from "./security.ts";
import type { AutonomyEndpoint, AutonomyEndpointContract, HuxIdentity } from "./types.ts";
interface FoundationClient {
apiVersion: string;
identity: HuxIdentity;
enabled(flag: string): boolean;
endpoint(path: string): string;
}
function endpoint(method: AutonomyEndpoint["method"], path: string,
responseSchema: string, requiredBody?: readonly string[]): AutonomyEndpoint {
return Object.freeze({method, path, responseSchema, ...(requiredBody ? {requiredBody} : {})});
}
/** Describe, but never call, the tenant-scoped HUX-05 interface. */
export function createAutonomyEndpointContract(client: FoundationClient): AutonomyEndpointContract | null {
if (client.apiVersion !== "hux.v1" ||
![FOUNDATION_FLAG, ACTIVITY_TIMELINE_FLAG, AUTONOMY_FLAG].every((flag) => client.enabled(flag))) return null;
const identity = normalizeIdentity(client.identity);
if (!identity || !sameIdentity(identity, client.identity)) throw new TypeError("Scoped HUX identity required");
const scoped = (root: string, id: string, prefix: string): string => {
if (!isOpaqueId(id, prefix)) throw new TypeError(`Opaque ${prefix} id required`);
return client.endpoint(`${root}/${encodeURIComponent(id)}`);
};
return Object.freeze({
identity: Object.freeze({...identity}),
policy: endpoint("GET", client.endpoint("/autonomy/policy"), "hux.policy.v1"),
updatePolicy: endpoint("PUT", client.endpoint("/autonomy/policy"), "hux.policy.v1",
["expected_policy_id", "expected_updated_at", "autonomy", "grants", "budgets", "guardrails"]),
approvals: endpoint("GET", client.endpoint("/approvals?status=pending&limit=100"), "hux.approval_page.v1"),
decideApproval(approvalId: string) {
return endpoint("POST", `${scoped("/approvals", approvalId, "apr")}/decision`, "hux.approval.v1",
["expected_run_id", "expected_conversation_id", "expected_capability", "expected_requested_at", "choice"]);
},
stop(runId: string) {
if (!/^run_[A-Za-z0-9._-]{1,116}$/.test(runId)) throw new TypeError("Exact run id required");
return endpoint("POST", client.endpoint(`/runs/${encodeURIComponent(runId)}/stop`),
"hux.cancel_receipt.v1", ["conversation_id"]);
},
release: Object.freeze({schema: "hux.release.v1" as const,
requiredState: "live_verified" as const,
featureFlags: Object.freeze([FOUNDATION_FLAG, ACTIVITY_TIMELINE_FLAG, AUTONOMY_FLAG])}),
});
}