Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

270 lines
8.0 KiB
TypeScript

/**
* Covers the native options.parameters migration: resolveActionArgs trusts complete planner
* parameters without extractor calls, RESOLVE_REQUEST and CALENDAR consume planner fields
* directly, and CALENDAR is a flat action-valued umbrella. Deterministic, mocked extractor.
*/
import {
type HandlerOptions,
type IAgentRuntime,
listSubactionsFromParameters,
type Memory,
promoteSubactionsToActions,
resolveActionArgs,
} from "@elizaos/core";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { calendarAction } from "../src/actions/calendar.js";
import { resolveRequestAction } from "../src/actions/resolve-request.js";
import {
createOwnerFactStore,
registerOwnerFactStore,
resolveOwnerFactStore,
} from "../src/lifeops/owner/fact-store.js";
const mocks = vi.hoisted(() => ({
hasOwnerAccess: vi.fn(),
queue: {
list: vi.fn(),
reject: vi.fn(),
approve: vi.fn(),
},
}));
vi.mock("@elizaos/agent", () => ({
hasOwnerAccess: mocks.hasOwnerAccess,
}));
vi.mock("../src/lifeops/approval-queue.js", () => ({
createApprovalQueue: vi.fn(() => mocks.queue),
}));
function makeRuntime(): IAgentRuntime {
const cache = new Map<string, unknown>();
return {
agentId: "agent-native-params",
useModel: vi.fn(() => {
throw new Error("legacy extractor should not be called");
}),
getService: vi.fn(() => {
throw new Error("calendar writer should not be called");
}),
async getCache<T>(key: string): Promise<T | null> {
const value = cache.get(key);
return value === undefined ? null : (value as T);
},
async setCache<T>(key: string, value: T): Promise<boolean> {
cache.set(key, value);
return true;
},
async deleteCache(key: string): Promise<boolean> {
return cache.delete(key);
},
} as IAgentRuntime;
}
function makeMessage(text = "reject req-1"): Memory {
return {
entityId: "owner-1",
content: { text },
} as Memory;
}
describe("LifeOps native options.parameters migration", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.hasOwnerAccess.mockResolvedValue(true);
});
it("resolveActionArgs trusts complete planner parameters without extractor calls", async () => {
const runtime = makeRuntime();
const result = await resolveActionArgs<"snooze", Record<string, unknown>>({
runtime,
message: makeMessage("snooze brushing"),
actionName: "LIFE",
subactions: {
snooze: {
description: "Snooze an occurrence.",
descriptionCompressed: "snooze occurrence",
required: ["target"],
optional: ["minutes"],
},
},
options: {
parameters: {
subaction: "snooze",
target: "Brush teeth",
minutes: 30,
},
} as HandlerOptions,
});
expect(result).toMatchObject({
ok: true,
subaction: "snooze",
params: {
subaction: "snooze",
target: "Brush teeth",
minutes: 30,
},
});
expect(runtime.useModel).not.toHaveBeenCalled();
});
it("RESOLVE_REQUEST uses planner requestId/reason without resolution extraction", async () => {
const runtime = makeRuntime();
mocks.queue.list.mockResolvedValue([
{ id: "req-1", action: "send_message", channel: "sms", reason: "one" },
{ id: "req-2", action: "send_email", channel: "gmail", reason: "two" },
]);
mocks.queue.reject.mockResolvedValue({
id: "req-1",
action: "send_message",
state: "rejected",
});
const result = await resolveRequestAction.handler(
runtime,
makeMessage("no, not that one"),
{},
{
parameters: {
subaction: "reject",
requestId: "req-1",
reason: "not now",
},
},
);
expect(result).toMatchObject({
success: true,
data: { requestId: "req-1", state: "rejected" },
});
expect(mocks.queue.reject).toHaveBeenCalledWith("req-1", {
resolvedBy: "owner-1",
resolutionReason: "not now",
});
expect(runtime.useModel).not.toHaveBeenCalled();
});
it("promoted RESOLVE_REQUEST_REJECT resolves the only pending approval without model extraction", async () => {
const runtime = makeRuntime();
const rejectVirtual = promoteSubactionsToActions(resolveRequestAction).find(
(action) => action.name === "RESOLVE_REQUEST_REJECT",
);
if (!rejectVirtual) {
throw new Error("RESOLVE_REQUEST_REJECT virtual was not promoted");
}
mocks.queue.list.mockResolvedValue([
{ id: "req-1", action: "send_message", channel: "sms", reason: "one" },
]);
mocks.queue.reject.mockResolvedValue({
id: "req-1",
action: "send_message",
state: "rejected",
});
const result = await rejectVirtual.handler(
runtime,
makeMessage(
"Wait - which Chris? There are two. Don't send it, reject that for now.",
),
{},
undefined,
);
expect(result).toMatchObject({
success: true,
data: { requestId: "req-1", state: "rejected" },
});
expect(mocks.queue.reject).toHaveBeenCalledWith("req-1", {
resolvedBy: "owner-1",
resolutionReason:
"Wait - which Chris? There are two. Don't send it, reject that for now.",
});
expect(runtime.useModel).not.toHaveBeenCalled();
});
it("CALENDAR exposes concrete contexts and is a flat action-valued umbrella (no nested subActions/subPlanner)", () => {
expect(calendarAction.contexts).toEqual([
"general",
"calendar",
"contacts",
"tasks",
"connectors",
"web",
]);
// The legacy 2-layer `subActions` + `subPlanner` dispatch was removed in
// favor of a flat action enum + `promoteSubactionsToActions` virtuals
// (CALENDAR_FEED, CALENDAR_CREATE_EVENT, CALENDAR_PROPOSE_TIMES, etc.).
expect(calendarAction.subActions).toBeUndefined();
expect(calendarAction.subPlanner).toBeUndefined();
expect(
(calendarAction.parameters ?? []).find((p) => p.name === "subaction"),
).toBeUndefined();
const actionParam = (calendarAction.parameters ?? []).find(
(p) => p.name === "action",
);
expect(actionParam).toBeDefined();
const enumVerbs = listSubactionsFromParameters(calendarAction.parameters);
expect(enumVerbs).toContain("feed");
expect(enumVerbs).toContain("create_event");
expect(enumVerbs).toContain("propose_times");
expect(enumVerbs).toContain("check_availability");
expect(enumVerbs).toContain("update_preferences");
});
it("CALENDAR create_event fails closed inside stored protected sleep without explicit override", async () => {
const runtime = makeRuntime();
registerOwnerFactStore(runtime, createOwnerFactStore(runtime));
await resolveOwnerFactStore(runtime).update(
{
timezone: "UTC",
quietHours: {
startLocal: "05:00",
endLocal: "13:00",
timezone: "UTC",
},
},
{ source: "profile_save", recordedAt: "2026-07-06T00:00:00.000Z" },
);
const callbackTexts: string[] = [];
const result = await calendarAction.handler(
runtime,
makeMessage(
"Can you throw a team sync on my calendar for 10am tomorrow?",
),
{},
{
parameters: {
action: "create_event",
title: "team sync",
details: {
start: "2026-07-07T10:00:00.000Z",
end: "2026-07-07T10:30:00.000Z",
},
},
},
async (content) => {
if (typeof content.text === "string") {
callbackTexts.push(content.text);
}
return [];
},
);
expect(result).toMatchObject({
success: false,
data: {
error: "PROTECTED_SLEEP_CONFLICT",
noop: true,
requestedLocalTime: "10:00",
},
});
expect(String(result.text)).toContain("protected quiet/sleep window");
expect(String(result.text)).toContain("explicit override");
expect(callbackTexts.join("\n")).toContain("after 14:00");
expect(runtime.useModel).not.toHaveBeenCalled();
expect(runtime.getService).not.toHaveBeenCalled();
});
});