214 lines
6.0 KiB
TypeScript
214 lines
6.0 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import {
|
|
claimWork,
|
|
detectStaleClaims,
|
|
handoffWork,
|
|
markClaimStealable,
|
|
releaseClaim,
|
|
type BossWorkClaim,
|
|
} from "@/lib/boss-work-claims";
|
|
import {
|
|
canUseCapabilityGroup,
|
|
DEFAULT_CAPABILITY_GROUPS,
|
|
explainCapabilityGroupDecision,
|
|
} from "@/lib/boss-capability-groups";
|
|
import {
|
|
assertDeviceTrustEnvelope,
|
|
evaluateDeviceTrust,
|
|
verifyDeviceTrustEnvelope,
|
|
type BossDeviceTrustProfile,
|
|
} from "@/lib/boss-device-trust";
|
|
|
|
test("work claims block conflicting actors and emit deterministic events", () => {
|
|
const first = claimWork({
|
|
claims: [],
|
|
resourceId: "project:alpha",
|
|
actorId: "user:kris",
|
|
actorKind: "user",
|
|
now: "2026-05-10T10:00:00.000Z",
|
|
ttlMs: 60_000,
|
|
});
|
|
|
|
assert.equal(first.ok, true);
|
|
assert.equal(first.claim?.resourceId, "project:alpha");
|
|
assert.equal(first.events[0]?.type, "claim_acquired");
|
|
|
|
const second = claimWork({
|
|
claims: first.claim ? [first.claim] : [],
|
|
resourceId: "project:alpha",
|
|
actorId: "device:mac-studio",
|
|
actorKind: "device",
|
|
now: "2026-05-10T10:00:10.000Z",
|
|
ttlMs: 60_000,
|
|
});
|
|
|
|
assert.equal(second.ok, false);
|
|
assert.equal(second.reason, "claim_conflict");
|
|
assert.equal(second.events[0]?.type, "claim_conflict");
|
|
assert.equal(second.conflictingClaim?.actorId, "user:kris");
|
|
});
|
|
|
|
test("work claims support release, handoff, stealable marking, and stale detection", () => {
|
|
const original: BossWorkClaim = {
|
|
claimId: "claim-1",
|
|
resourceId: "project:alpha",
|
|
actorId: "main-agent",
|
|
actorKind: "agent",
|
|
acquiredAt: "2026-05-10T10:00:00.000Z",
|
|
expiresAt: "2026-05-10T10:05:00.000Z",
|
|
status: "active",
|
|
};
|
|
|
|
const marked = markClaimStealable({
|
|
claim: original,
|
|
actorId: "main-agent",
|
|
now: "2026-05-10T10:01:00.000Z",
|
|
reason: "waiting for device",
|
|
});
|
|
assert.equal(marked.ok, true);
|
|
assert.equal(marked.claim?.status, "stealable");
|
|
assert.equal(marked.events[0]?.type, "claim_marked_stealable");
|
|
|
|
const handed = handoffWork({
|
|
claim: marked.claim!,
|
|
fromActorId: "main-agent",
|
|
toActorId: "device:mac-studio",
|
|
toActorKind: "device",
|
|
now: "2026-05-10T10:02:00.000Z",
|
|
ttlMs: 120_000,
|
|
});
|
|
assert.equal(handed.ok, true);
|
|
assert.equal(handed.claim?.actorId, "device:mac-studio");
|
|
assert.equal(handed.events.map((event) => event.type).join(","), "claim_handoff_released,claim_handoff_acquired");
|
|
|
|
const stale = detectStaleClaims({
|
|
claims: [handed.claim!],
|
|
now: "2026-05-10T10:05:01.000Z",
|
|
});
|
|
assert.equal(stale.staleClaims.length, 1);
|
|
assert.equal(stale.events[0]?.type, "claim_stale_detected");
|
|
|
|
const released = releaseClaim({
|
|
claim: handed.claim!,
|
|
actorId: "device:mac-studio",
|
|
now: "2026-05-10T10:03:00.000Z",
|
|
});
|
|
assert.equal(released.ok, true);
|
|
assert.equal(released.claim?.status, "released");
|
|
assert.equal(released.events[0]?.type, "claim_released");
|
|
});
|
|
|
|
test("capability groups enforce account permissions, skill scope, and device scope", () => {
|
|
assert.deepEqual(
|
|
DEFAULT_CAPABILITY_GROUPS.map((group) => group.id),
|
|
["computer_control", "codex_development", "browser_automation", "skill_operations", "admin_ops"],
|
|
);
|
|
|
|
const allowed = canUseCapabilityGroup({
|
|
groupId: "browser_automation",
|
|
accountPermissions: ["computer.control"],
|
|
skillIds: ["browser-use:browser"],
|
|
deviceScopes: ["browserAutomation"],
|
|
requestedSkillId: "browser-use:browser",
|
|
requestedDeviceScope: "browserAutomation",
|
|
});
|
|
assert.equal(allowed.allowed, true);
|
|
|
|
const denied = canUseCapabilityGroup({
|
|
groupId: "skill_operations",
|
|
accountPermissions: ["skill.use"],
|
|
skillIds: ["browser-use:browser"],
|
|
deviceScopes: ["skillLifecycle"],
|
|
requestedSkillId: "skill-installer",
|
|
requestedDeviceScope: "skillLifecycle",
|
|
});
|
|
assert.equal(denied.allowed, false);
|
|
assert.equal(denied.reason, "skill_scope_missing");
|
|
|
|
assert.match(explainCapabilityGroupDecision(denied), /skill_scope_missing/);
|
|
});
|
|
|
|
test("device trust rejects over-budget, over-hop, low-trust, and invalid signed envelopes", () => {
|
|
const device: BossDeviceTrustProfile = {
|
|
deviceId: "mac-studio",
|
|
trustTier: "managed",
|
|
publicKeyFingerprint: "sha256:abc",
|
|
maxMessageBudget: 3,
|
|
maxHopCount: 2,
|
|
allowedCapabilities: ["computer_control", "browser_automation"],
|
|
};
|
|
|
|
assert.equal(
|
|
evaluateDeviceTrust({
|
|
device,
|
|
requiredTrustTier: "verified",
|
|
capabilityGroupId: "browser_automation",
|
|
messageBudgetUsed: 2,
|
|
hopCount: 2,
|
|
}).allowed,
|
|
true,
|
|
);
|
|
assert.equal(
|
|
evaluateDeviceTrust({
|
|
device,
|
|
requiredTrustTier: "federated",
|
|
capabilityGroupId: "browser_automation",
|
|
messageBudgetUsed: 2,
|
|
hopCount: 2,
|
|
}).reason,
|
|
"trust_tier_too_low",
|
|
);
|
|
assert.equal(
|
|
evaluateDeviceTrust({
|
|
device,
|
|
requiredTrustTier: "verified",
|
|
capabilityGroupId: "browser_automation",
|
|
messageBudgetUsed: 4,
|
|
hopCount: 2,
|
|
}).reason,
|
|
"message_budget_exceeded",
|
|
);
|
|
assert.equal(
|
|
evaluateDeviceTrust({
|
|
device,
|
|
requiredTrustTier: "verified",
|
|
capabilityGroupId: "browser_automation",
|
|
messageBudgetUsed: 2,
|
|
hopCount: 3,
|
|
}).reason,
|
|
"hop_limit_exceeded",
|
|
);
|
|
|
|
const envelope = {
|
|
deviceId: "mac-studio",
|
|
payload: { taskId: "task-1" },
|
|
signature: "sig-1",
|
|
keyFingerprint: "sha256:abc",
|
|
timestamp: "2026-05-10T10:00:00.000Z",
|
|
nonce: "nonce-1",
|
|
};
|
|
|
|
assert.equal(
|
|
verifyDeviceTrustEnvelope(envelope, {
|
|
device,
|
|
now: "2026-05-10T10:01:00.000Z",
|
|
maxClockSkewMs: 120_000,
|
|
verifySignature: ({ canonicalPayload, signature }) =>
|
|
signature === "sig-1" && canonicalPayload.includes("\"taskId\":\"task-1\""),
|
|
}).valid,
|
|
true,
|
|
);
|
|
assert.throws(() =>
|
|
assertDeviceTrustEnvelope(
|
|
{ ...envelope, keyFingerprint: "sha256:other" },
|
|
{
|
|
device,
|
|
now: "2026-05-10T10:01:00.000Z",
|
|
maxClockSkewMs: 120_000,
|
|
verifySignature: () => true,
|
|
},
|
|
),
|
|
);
|
|
});
|