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

177 lines
6.5 KiB
TypeScript

// @journey-14
/**
* LifeOps Journey #14 — Flight Conflict Detection And Rebooking
*
* Calendar conflict detected → agent proposes alternative flights → rebook
* executed after approval → calendar updated.
*
* PRD §Suite D — Travel And Event Operations
* (`ea.travel.flight-conflict-rebooking`).
*
* Setup: a flight on Wed 8 AM and a calendar event Wed 9 AM that the agent
* detects as overlapping with a layover.
*
* Gate: ELIZA_LIVE_TEST=1 + provider key.
*/
import crypto from "node:crypto";
import { ChannelType, createMessageMemory, type UUID } from "@elizaos/core";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { selectLiveProvider } from "../../../packages/test/helpers/live-provider.ts";
import { withTimeout } from "../../../packages/test/helpers/test-utils.ts";
import type { MockedTestRuntime } from "../../../packages/test/mocks/helpers/mock-runtime.ts";
import { createMockedTestRuntime } from "../../../packages/test/mocks/helpers/mock-runtime.ts";
import { createApprovalQueue } from "../src/lifeops/approval-queue.js";
import { judgeTextWithLlm } from "./helpers/lifeops-live-judge.ts";
const LIVE_ENABLED = process.env.ELIZA_LIVE_TEST === "1";
const provider = LIVE_ENABLED ? selectLiveProvider() : null;
if (!LIVE_ENABLED || !provider) {
console.info(
"[flight-rebook-e2e] skipped: set ELIZA_LIVE_TEST=1 and provide a provider API key",
);
}
describe.skipIf(!LIVE_ENABLED || !provider)(
"LifeOps Journey #14 — Flight Conflict Detection And Rebooking",
() => {
let mocked: MockedTestRuntime;
let ownerId: UUID;
let roomId: UUID;
beforeAll(async () => {
mocked = await createMockedTestRuntime({
seedLifeOpsSimulator: true,
withLLM: true,
preferredProvider: provider?.name,
});
ownerId = crypto.randomUUID() as UUID;
roomId = crypto.randomUUID() as UUID;
// Seed the conflicting flight and calendar event via Google mock
const calendarBase = mocked.mocks.baseUrls.google;
// Flight arrives Wed 8 AM (could be tight for a 9 AM meeting after baggage)
await fetch(`${calendarBase}/calendar/v3/calendars/primary/events`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer mock-token",
"X-Eliza-Test-Run": "journey-14",
},
body: JSON.stringify({
summary: "Flight SFO → JFK — arrival 8:00 AM",
start: {
dateTime: "2026-05-20T08:00:00-04:00",
timeZone: "America/New_York",
},
end: {
dateTime: "2026-05-20T08:30:00-04:00",
timeZone: "America/New_York",
},
description:
"Flight arrives 8 AM; tight connection to 9 AM board meeting",
}),
});
// Board meeting 9 AM same day
await fetch(`${calendarBase}/calendar/v3/calendars/primary/events`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer mock-token",
"X-Eliza-Test-Run": "journey-14",
},
body: JSON.stringify({
summary: "Board Meeting — NYC office",
start: {
dateTime: "2026-05-20T09:00:00-04:00",
timeZone: "America/New_York",
},
end: {
dateTime: "2026-05-20T11:00:00-04:00",
timeZone: "America/New_York",
},
}),
});
mocked.mocks.clearRequestLedger();
}, 120_000);
afterAll(async () => {
await mocked?.cleanup();
});
it("detects the flight/meeting conflict and proposes alternative earlier flights", async () => {
const message = createMessageMemory({
id: crypto.randomUUID() as UUID,
entityId: ownerId,
roomId,
metadata: { type: "user_message", entityName: "shaw" },
content: {
text: "Can I make my Wednesday, May 20 board meeting given my morning flight to JFK that lands at 8 AM?",
source: "telegram",
channelType: ChannelType.DM,
},
});
let responseText = "";
const result = await withTimeout(
Promise.resolve(
mocked.runtime.messageService?.handleMessage(
mocked.runtime,
message,
async (content: { text?: string }) => {
if (content.text) responseText += content.text;
return [];
},
),
),
90_000,
"handleMessage",
);
const reply =
String(result?.responseContent?.text ?? "").trim() || responseText;
expect(reply).not.toMatch(/something (?:went wrong|flaked)|try again/i);
// The agent must do real work: either (a) enqueue a `book_travel`
// approval for an earlier flight, or (b) actually surface alternative
// flights / a rebooking plan in the reply. We do NOT pre-populate the
// approval queue ourselves — if the agent does nothing, this test
// fails (which is the correct outcome).
const approvalQueue = createApprovalQueue(mocked.runtime, {
agentId: mocked.runtime.agentId,
});
const pending = await approvalQueue.list({
subjectUserId: String(ownerId),
state: "pending",
action: null,
limit: 10,
});
const enqueuedRebook = pending.some((request) => {
const payload = JSON.stringify(request.payload).toLowerCase();
return (
request.action === "book_travel" &&
(payload.includes("flight") ||
payload.includes("sfo") ||
payload.includes("jfk"))
);
});
const judgement = await judgeTextWithLlm({
label: "flight-rebook.detected-conflict-and-proposed",
rubric:
"The reply must (1) acknowledge the timing conflict between the 8 AM JFK arrival and the 9 AM board meeting AND (2) either propose at least one specific alternative (e.g. an earlier flight, a calendar move, a remote-attend option) or describe a concrete rebooking plan. A reply that only restates the question, only says 'I'll check', or asks unrelated questions fails. The reply does NOT need to actually book anything — just propose.",
text: reply,
minimumScore: 0.7,
});
expect(
enqueuedRebook || judgement.passed,
`Agent must either enqueue a book_travel approval or surface a rebooking proposal in the reply. Approvals=${pending.length}, judge=${JSON.stringify(judgement)}`,
).toBe(true);
}, 120_000);
},
);