import assert from "node:assert/strict"; import test from "node:test"; import { createAutonomyEndpointContract } from "../../dockerfiles/hermes-webui-hux/autonomy/endpoints.ts"; import { ACTIVITY_TIMELINE_FLAG, AUTONOMY_FLAG, BUDGET_MAX, CAPABILITIES, FOUNDATION_FLAG, actionAuthorization, autonomyControlsEnabled, buildApprovalDecision, buildPolicyUpdate, buildStopIntent, normalizeAutonomyPage, normalizeStopReceipt, } from "../../dockerfiles/hermes-webui-hux/autonomy/model.ts"; import { boundedInteger, exactKeys, identityKey, isOpaqueId, isUtc, normalizeIdentity, safeText, sameIdentity, } from "../../dockerfiles/hermes-webui-hux/autonomy/security.ts"; const IDENTITY = { tenantRef: "tnt_0123456789abcdef", userRef: "usr_0123456789abcdef", surface: "chat", }; const CONVERSATION = "conv_test1234"; const NOW = Date.parse("2026-08-24T10:05:00Z"); const grants = (decision = "ask") => CAPABILITIES.map((capability) => ({capability, decision})); function policy(extra = {}) { return { schema: "hux.policy.v1", id: "pol_test1234", owner: IDENTITY.userRef, scope: {level: "conversation", scope_id: CONVERSATION}, autonomy: "safe", grants: grants(), budgets: { tokens_per_run: 200000, tool_calls_per_run: 40, wall_clock_seconds: 900, delegations_per_run: 4, }, provenance: {surface: "chat"}, updated_at: "2026-08-24T10:00:00Z", ...extra, }; } function approval(id = "apr_test1234", extra = {}) { return { schema: "hux.approval.v1", id, run_id: "run_test1234", conversation_id: CONVERSATION, capability: "send_message", request: {summary: "Send the completed report", risk: "high"}, status: "pending", requested_at: "2026-08-24T10:00:00Z", expires_at: "2026-08-24T10:10:00Z", ...extra, }; } function envelope(extra = {}) { return { schema: "hux.autonomy_page.v1", api_version: "hux.v1", identity: {tenant_ref: IDENTITY.tenantRef, user_ref: IDENTITY.userRef, surface: "chat"}, conversation_id: CONVERSATION, policy: policy(), approvals: [approval()], ...extra, }; } test("HUX-05 is inert unless all three dependency flags are present", () => { assert.equal(autonomyControlsEnabled(), false); assert.equal(autonomyControlsEnabled([]), false); assert.equal(autonomyControlsEnabled([AUTONOMY_FLAG]), false); assert.equal(autonomyControlsEnabled([FOUNDATION_FLAG, AUTONOMY_FLAG]), false); assert.equal(autonomyControlsEnabled([FOUNDATION_FLAG, ACTIVITY_TIMELINE_FLAG]), false); assert.equal(autonomyControlsEnabled([FOUNDATION_FLAG, ACTIVITY_TIMELINE_FLAG, AUTONOMY_FLAG]), true); }); test("security helpers reject unscoped identity and redact display text", () => { assert.deepEqual(normalizeIdentity({tenant_ref: IDENTITY.tenantRef, user_ref: IDENTITY.userRef, surface: "chat"}), IDENTITY); assert.deepEqual(normalizeIdentity(IDENTITY), IDENTITY); assert.equal(normalizeIdentity(null), null); assert.equal(normalizeIdentity([]), null); assert.equal(normalizeIdentity({...IDENTITY, tenantRef: "tenant"}), null); assert.equal(normalizeIdentity({...IDENTITY, userRef: "brad@example.test"}), null); assert.equal(normalizeIdentity({...IDENTITY, surface: "browser"}), null); assert.equal(sameIdentity(IDENTITY, {...IDENTITY}), true); assert.equal(sameIdentity(IDENTITY, {...IDENTITY, surface: "worker"}), false); assert.match(identityKey(IDENTITY), /^tnt_.*:usr_.*:chat$/); assert.equal(isOpaqueId("apr_test1234", "apr"), true); assert.equal(isOpaqueId("pol_test1234", "apr"), false); assert.equal(isOpaqueId(4), false); assert.equal(isUtc("2026-08-24T10:00:00.123Z"), true); assert.equal(isUtc("2026-99-24T10:00:00Z"), false); assert.equal(isUtc(4), false); assert.equal(boundedInteger(5, 5), 5); assert.equal(boundedInteger(-1, 5), null); assert.equal(boundedInteger(6, 5), null); assert.equal(boundedInteger(1.5, 5), null); assert.equal(exactKeys({b: 1, a: 2}, ["a", "b"]), true); assert.equal(exactKeys({a: 1, extra: 2}, ["a"]), false); assert.equal(safeText({}, "fallback"), "fallback"); assert.equal(safeText(" \n ", "fallback"), "fallback"); assert.equal(safeText("token=secret Bearer abc.def\u0000", "fallback"), "token=[redacted] Bearer [redacted]"); assert.equal(safeText("abcdef", "fallback", 4), "abc…"); }); test("policy and pending approvals normalize against exact identity and scope", () => { const page = normalizeAutonomyPage(envelope(), IDENTITY, CONVERSATION, NOW); assert.ok(page); assert.equal(page.policy.autonomy, "safe"); assert.deepEqual(page.policy.scope, {level: "conversation", scopeId: CONVERSATION}); assert.deepEqual(page.policy.budgets, { tokensPerRun: 200000, toolCallsPerRun: 40, wallClockSeconds: 900, delegationsPerRun: 4, }); assert.equal(page.approvals.length, 1); assert.equal(page.approvals[0].risk, "high"); assert.equal(page.rejectedApprovals, 0); const partial = normalizeAutonomyPage(envelope({approvals: [ approval(), approval("apr_expired1", {expires_at: "2026-08-24T10:04:59Z"}), approval("apr_cross123", {conversation_id: "conv_other1234"}), approval("apr_secret12", {request: {summary: "token=do-not-show", risk: "low"}}), ]}), IDENTITY, CONVERSATION, NOW); assert.equal(partial.approvals.length, 2); assert.equal(partial.rejectedApprovals, 2); assert.doesNotMatch(partial.approvals[1].summary, /do-not-show/); }); test("page normalization rejects malformed envelopes, policies, and budgets", () => { const invalidEnvelopes = [ null, {...envelope(), schema: "hux.autonomy_page.v0"}, {...envelope(), api_version: "hux.v0"}, {...envelope(), conversation_id: "bad"}, {...envelope(), identity: {...envelope().identity, user_ref: "usr_ffffffffffffffff"}}, {...envelope(), identity: null}, {...envelope(), approvals: {}}, {...envelope(), approvals: Array(101).fill(approval())}, {...envelope(), policy: {...policy(), surprise: true}}, {...envelope(), policy: {...policy(), schema: "old"}}, {...envelope(), policy: {...policy(), id: "bad"}}, {...envelope(), policy: {...policy(), owner: "usr_ffffffffffffffff"}}, {...envelope(), policy: {...policy(), autonomy: "reckless"}}, {...envelope(), policy: {...policy(), updated_at: "today"}}, {...envelope(), policy: {...policy(), scope: null}}, {...envelope(), policy: {...policy(), scope: {level: "global", scope_id: CONVERSATION}}}, {...envelope(), policy: {...policy(), scope: {level: "project", scope_id: "bad"}}}, {...envelope(), policy: {...policy(), scope: {level: "planet"}}}, {...envelope(), policy: {...policy(), grants: {}}}, {...envelope(), policy: {...policy(), grants: Array(65).fill({capability: "shell", decision: "ask"})}}, {...envelope(), policy: {...policy(), grants: [{capability: "unknown", decision: "ask"}]}}, {...envelope(), policy: {...policy(), grants: [{capability: "shell", decision: "maybe"}]}}, {...envelope(), policy: {...policy(), grants: [{capability: "shell", decision: "ask"}, {capability: "shell", decision: "deny"}]}}, {...envelope(), policy: {...policy(), budgets: null}}, {...envelope(), policy: {...policy(), budgets: {unknown: 1}}}, {...envelope(), policy: {...policy(), budgets: {tokens_per_run: BUDGET_MAX.tokensPerRun + 1}}}, ]; for (const value of invalidEnvelopes) { assert.equal(normalizeAutonomyPage(value, IDENTITY, CONVERSATION, NOW), null); } }); test("approval rows fail closed when malformed, resolved, stale, or reordered", () => { const invalid = [ null, {...approval(), extra: true}, {...approval(), schema: "old"}, {...approval(), id: "bad"}, {...approval(), run_id: ""}, {...approval(), run_id: "x".repeat(121)}, {...approval(), capability: "unknown"}, {...approval(), status: "approved"}, {...approval(), requested_at: "today"}, {...approval(), expires_at: "today"}, {...approval(), requested_at: "2026-08-24T10:11:00Z"}, {...approval(), request: null}, {...approval(), request: {summary: "x", risk: "extreme"}}, {...approval(), request: {summary: "x", risk: "low", injected: true}}, ]; const page = normalizeAutonomyPage(envelope({approvals: invalid}), IDENTITY, CONVERSATION, NOW); assert.deepEqual(page.approvals, []); assert.equal(page.rejectedApprovals, invalid.length); }); test("policy updates preserve canonical budgets and require bounded spend and scope", () => { const page = normalizeAutonomyPage(envelope(), IDENTITY, CONVERSATION, NOW); const draft = {...page.policy.budgets, spendCentsPerRun: 2500, scopeLimit: "conversation"}; const intent = buildPolicyUpdate(page.policy, IDENTITY, "autonomous", page.policy.grants, draft); assert.equal(intent.expectedPolicyId, page.policy.id); assert.equal(intent.guardrails.spendCentsPerRun, 2500); assert.equal("spendCentsPerRun" in intent.budgets, false); assert.deepEqual(Object.keys(intent.grants), CAPABILITIES); const invalidDrafts = [ {...draft, tokensPerRun: -1}, {...draft, toolCallsPerRun: 501}, {...draft, wallClockSeconds: 86401}, {...draft, delegationsPerRun: 33}, {...draft, spendCentsPerRun: 100001}, {...draft, scopeLimit: "internet"}, ]; for (const bad of invalidDrafts) assert.throws(() => buildPolicyUpdate(page.policy, IDENTITY, "safe", page.policy.grants, bad), /bounds/); assert.throws(() => buildPolicyUpdate(page.policy, {...IDENTITY, userRef: "usr_ffffffffffffffff"}, "safe", page.policy.grants, draft), /bounds/); assert.throws(() => buildPolicyUpdate(page.policy, IDENTITY, "turbo", page.policy.grants, draft), /bounds/); assert.throws(() => buildPolicyUpdate(page.policy, IDENTITY, "safe", {...page.policy.grants, shell: "maybe"}, draft), /bounds/); }); test("authorization requires exact live approval whenever policy says ask", () => { const page = normalizeAutonomyPage(envelope(), IDENTITY, CONVERSATION, NOW); const pending = page.approvals[0]; assert.equal(actionAuthorization(null, "read_files", null, IDENTITY, NOW), "deny"); assert.equal(actionAuthorization({...page.policy, owner: "usr_ffffffffffffffff"}, "read_files", null, IDENTITY, NOW), "deny"); assert.equal(actionAuthorization(page.policy, "unknown", null, IDENTITY, NOW), "deny"); assert.equal(actionAuthorization(page.policy, "send_message", null, IDENTITY, NOW), "approval_required"); assert.equal(actionAuthorization(page.policy, "send_message", pending, IDENTITY, NOW), "approval_required"); assert.equal(actionAuthorization(page.policy, "shell", pending, IDENTITY, NOW), "approval_required"); assert.equal(actionAuthorization(page.policy, "send_message", {...pending, status: "denied"}, IDENTITY, NOW), "approval_required"); assert.equal(actionAuthorization(page.policy, "send_message", {...pending, expiresAt: "2026-08-24T10:04:00Z"}, IDENTITY, NOW), "approval_required"); assert.equal(actionAuthorization(page.policy, "send_message", {...pending, identityKey: "cross"}, IDENTITY, NOW), "approval_required"); const allow = {...page.policy, autonomy: "autonomous", grants: {...page.policy.grants, read_files: "allow", send_message: "allow"}}; assert.equal(actionAuthorization(allow, "read_files", null, IDENTITY, NOW), "allow"); assert.equal(actionAuthorization(allow, "send_message", null, IDENTITY, NOW), "allow"); const safe = {...allow, autonomy: "safe"}; assert.equal(actionAuthorization(safe, "read_files", null, IDENTITY, NOW), "allow"); assert.equal(actionAuthorization(safe, "send_message", null, IDENTITY, NOW), "approval_required"); assert.equal(actionAuthorization({...allow, grants: {...allow.grants, deploy: "deny"}}, "deploy", null, IDENTITY, NOW), "deny"); }); test("approval decision proves identity and original timestamp", () => { const item = normalizeAutonomyPage(envelope(), IDENTITY, CONVERSATION, NOW).approvals[0]; for (const choice of ["once", "session", "always", "deny"]) { const intent = buildApprovalDecision(item, choice, IDENTITY, item.requestedAt, NOW); assert.equal(intent.choice, choice); assert.equal(intent.expectedRunId, item.runId); assert.equal(intent.expectedConversationId, CONVERSATION); } assert.throws(() => buildApprovalDecision(item, "once", IDENTITY, "2026-08-24T10:00:01Z", NOW), /denied/); assert.throws(() => buildApprovalDecision(item, "once", {...IDENTITY, surface: "worker"}, item.requestedAt, NOW), /denied/); assert.throws(() => buildApprovalDecision({...item, status: "denied"}, "once", IDENTITY, item.requestedAt, NOW), /denied/); assert.throws(() => buildApprovalDecision({...item, expiresAt: "2026-08-24T10:04:00Z"}, "once", IDENTITY, item.requestedAt, NOW), /denied/); assert.throws(() => buildApprovalDecision(item, "later", IDENTITY, item.requestedAt, NOW), /denied/); }); test("stop intent and receipt require exact ownership and remain bounded", () => { const run = {runId: "run_test1234", conversationId: CONVERSATION, tenantRef: IDENTITY.tenantRef, owner: IDENTITY.userRef}; assert.deepEqual(buildStopIntent(run, IDENTITY, run.runId), { runId: run.runId, conversationId: CONVERSATION, identity: IDENTITY, }); for (const bad of [ {...run, runId: ""}, {...run, runId: "x".repeat(121)}, {...run, conversationId: "bad"}, {...run, owner: "usr_ffffffffffffffff"}, {...run, tenantRef: "tnt_ffffffffffffffff"}, ]) assert.throws(() => buildStopIntent(bad, IDENTITY, run.runId), /ownership/); assert.throws(() => buildStopIntent(run, IDENTITY, "run_stale1234"), /ownership/); const raw = { schema: "hux.cancel_receipt.v1", id: "rcpt_test1234", run_id: run.runId, requested_by: {type: "user", id: IDENTITY.userRef}, requested_at: "2026-08-24T10:05:00Z", outcome: "cancelled", side_effects: Array.from({length: 7}, (_, index) => ({ description: index ? `Effect ${index}` : "token=do-not-show", reverted: index % 2 === 0, })), }; const receipt = normalizeStopReceipt(raw, run, IDENTITY); assert.equal(receipt.effects.length, 4); assert.equal(receipt.omittedEffects, 3); assert.equal(receipt.stopped, 2); assert.equal(receipt.remaining, 2); assert.doesNotMatch(JSON.stringify(receipt), /do-not-show/); assert.equal(normalizeStopReceipt({...raw, run_id: "run_other1234"}, run, IDENTITY), null); assert.equal(normalizeStopReceipt({...raw, requested_by: null}, run, IDENTITY), null); assert.equal(normalizeStopReceipt({...raw, requested_by: {type: "system", id: IDENTITY.userRef}}, run, IDENTITY), null); assert.equal(normalizeStopReceipt({...raw, requested_by: {type: "user", id: "usr_ffffffffffffffff"}}, run, IDENTITY), null); assert.equal(normalizeStopReceipt({...raw, schema: "old"}, run, IDENTITY), null); assert.equal(normalizeStopReceipt(raw, {...run, owner: "usr_ffffffffffffffff"}, IDENTITY), null); assert.equal(normalizeStopReceipt(raw, {...run, tenantRef: "tnt_ffffffffffffffff"}, IDENTITY), null); }); test("endpoint adapter is inert, same-origin, versioned, and release-aware", () => { const enabled = new Set([FOUNDATION_FLAG, ACTIVITY_TIMELINE_FLAG, AUTONOMY_FLAG]); const client = { apiVersion: "hux.v1", identity: IDENTITY, enabled: (flag) => enabled.has(flag), endpoint: (path) => `/hux/v1${path}`, }; const contract = createAutonomyEndpointContract(client); assert.equal(contract.policy.path, "/hux/v1/autonomy/policy"); assert.equal(contract.updatePolicy.responseSchema, "hux.policy.v1"); assert.equal(contract.approvals.path, "/hux/v1/approvals?status=pending&limit=100"); assert.match(contract.decideApproval("apr_test1234").path, /\/approvals\/apr_test1234\/decision$/); assert.match(contract.stop("run_test1234").path, /\/runs\/run_test1234\/stop$/); assert.deepEqual(contract.release, {schema: "hux.release.v1", requiredState: "live_verified", featureFlags: [FOUNDATION_FLAG, ACTIVITY_TIMELINE_FLAG, AUTONOMY_FLAG]}); assert.throws(() => contract.decideApproval("bad"), /Opaque/); assert.throws(() => contract.stop("bad"), /Exact/); for (const flag of enabled) { assert.equal(createAutonomyEndpointContract({...client, enabled: (candidate) => candidate !== flag && enabled.has(candidate)}), null); } assert.equal(createAutonomyEndpointContract({...client, apiVersion: "hux.v0"}), null); assert.throws(() => createAutonomyEndpointContract({...client, identity: {...IDENTITY, userRef: "raw-email"}}), /identity/); });