Consume Epiphany operator deliveries safely
This commit is contained in:
parent
7493642d05
commit
a21d173ec0
11 changed files with 480 additions and 4 deletions
|
|
@ -24,6 +24,7 @@ import {
|
|||
loadFaceIdentityRegistry,
|
||||
queueRepoFaceMention,
|
||||
exportPersonaFeedbackObservation,
|
||||
registerEpiphanyOperatorInteraction,
|
||||
submitEpiphanyOperatorRequest,
|
||||
type RepoDiscordIdentity,
|
||||
stripRepoIdentityTextAddress,
|
||||
|
|
@ -66,6 +67,7 @@ import {
|
|||
} from "./discord-bot-handlers";
|
||||
import { maybeMirrorOwnerAquariumMessageAsMetameVoice } from "./metame-owner-voice-bridge";
|
||||
import { startRepoFaceVoicePlayback } from "./repo-face-voice-playback";
|
||||
import { startEpiphanyOperatorDeliveryConsumer } from "./epiphany-operator-delivery-consumer";
|
||||
import {
|
||||
buildActorFromInteraction,
|
||||
buildActorFromMessage,
|
||||
|
|
@ -342,6 +344,15 @@ export async function startBot(): Promise<void> {
|
|||
console.log(`VoidBot connected as ${readyClient.user.tag}.`);
|
||||
await maybeRegisterCommands(config.botToken!, config.applicationId, config.developmentGuildId);
|
||||
startRepoFaceVoicePlayback(readyClient, config.repoFaceDiscordVoice);
|
||||
startEpiphanyOperatorDeliveryConsumer({
|
||||
applicationId: readyClient.application.id,
|
||||
requestStorePath: config.bifrostEpiphanyOperatorRequestStorePath,
|
||||
deliveryStorePath: config.bifrostEpiphanyOperatorDeliveryStorePath,
|
||||
checkpointStorePath: config.epiphanyOperatorDeliveryCheckpointStorePath,
|
||||
bifrostRoot: config.bifrostRoot,
|
||||
cultlibRoot: process.env.VOIDBOT_CULTLIB_ROOT,
|
||||
pollIntervalMs: config.epiphanyOperatorDeliveryPollIntervalMs,
|
||||
});
|
||||
});
|
||||
|
||||
client.on(Events.MessageCreate, async (message) => {
|
||||
|
|
@ -582,6 +593,26 @@ export async function startBot(): Promise<void> {
|
|||
);
|
||||
break;
|
||||
}
|
||||
const issuedAt = new Date().toISOString();
|
||||
await registerEpiphanyOperatorInteraction(
|
||||
{
|
||||
requestId: interaction.id,
|
||||
applicationId: interaction.applicationId,
|
||||
interactionToken: interaction.token,
|
||||
guildId: interaction.guildId,
|
||||
channelId: interaction.channelId,
|
||||
registeredAt: issuedAt,
|
||||
},
|
||||
{
|
||||
checkpointStorePath: config.epiphanyOperatorDeliveryCheckpointStorePath,
|
||||
bifrostRoot: config.bifrostRoot,
|
||||
cultlibRoot: process.env.VOIDBOT_CULTLIB_ROOT,
|
||||
},
|
||||
);
|
||||
await replyEphemeral(
|
||||
interaction,
|
||||
`Epiphany operator request \`${interaction.id}\` is pending Bifrost admission. This request grants no Mind, Hands, release, deployment, or local execution authority.`,
|
||||
);
|
||||
const request = await submitEpiphanyOperatorRequest(
|
||||
{
|
||||
interactionId: interaction.id,
|
||||
|
|
@ -589,6 +620,7 @@ export async function startBot(): Promise<void> {
|
|||
guildId: interaction.guildId,
|
||||
channelId: interaction.channelId,
|
||||
command: epiphanyOperatorCommandFromInteraction(interaction),
|
||||
issuedAt,
|
||||
},
|
||||
{
|
||||
storePath: config.bifrostEpiphanyOperatorRequestStorePath,
|
||||
|
|
@ -597,10 +629,7 @@ export async function startBot(): Promise<void> {
|
|||
producerRuntimeId: "voidbot-yggdrasil",
|
||||
},
|
||||
);
|
||||
await replyEphemeral(
|
||||
interaction,
|
||||
`Epiphany operator request \`${request.requestId}\` is pending Bifrost admission. This request grants no Mind, Hands, release, deployment, or local execution authority.`,
|
||||
);
|
||||
if (request.requestId !== interaction.id) throw new Error("Epiphany request identity changed after interaction registration.");
|
||||
break;
|
||||
}
|
||||
case "ask":
|
||||
|
|
|
|||
48
apps/bot/src/epiphany-operator-delivery-consumer.ts
Normal file
48
apps/bot/src/epiphany-operator-delivery-consumer.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { consumeEpiphanyOperatorDeliveries } from "@voidbot/core";
|
||||
|
||||
export interface EpiphanyOperatorDeliveryConsumerConfig {
|
||||
applicationId: string;
|
||||
requestStorePath: string;
|
||||
deliveryStorePath: string;
|
||||
checkpointStorePath: string;
|
||||
bifrostRoot: string;
|
||||
cultlibRoot?: string;
|
||||
pollIntervalMs: number;
|
||||
}
|
||||
|
||||
export function startEpiphanyOperatorDeliveryConsumer(config: EpiphanyOperatorDeliveryConsumerConfig): void {
|
||||
let running = false;
|
||||
const pulse = async (): Promise<void> => {
|
||||
if (running) return;
|
||||
running = true;
|
||||
try {
|
||||
const result = await consumeEpiphanyOperatorDeliveries(
|
||||
{
|
||||
requestStorePath: config.requestStorePath,
|
||||
deliveryStorePath: config.deliveryStorePath,
|
||||
checkpointStorePath: config.checkpointStorePath,
|
||||
bifrostRoot: config.bifrostRoot,
|
||||
cultlibRoot: config.cultlibRoot,
|
||||
},
|
||||
async (applicationId, interactionToken, content) => {
|
||||
if (applicationId !== config.applicationId) throw new Error("interaction application binding mismatch");
|
||||
const response = await fetch(
|
||||
`https://discord.com/api/v10/webhooks/${encodeURIComponent(applicationId)}/${encodeURIComponent(interactionToken)}/messages/@original`,
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ content }),
|
||||
},
|
||||
);
|
||||
if (!response.ok) throw new Error(`Discord interaction edit failed with HTTP ${response.status}`);
|
||||
},
|
||||
);
|
||||
if (result.failed > 0) console.error("Epiphany operator delivery pulse retained failures for retry:", result);
|
||||
} catch (error) {
|
||||
console.error("Epiphany operator delivery pulse failed without acknowledging deliveries:", error);
|
||||
} finally { running = false; }
|
||||
};
|
||||
void pulse();
|
||||
const timer = setInterval(() => void pulse(), config.pollIntervalMs);
|
||||
timer.unref?.();
|
||||
}
|
||||
|
|
@ -292,3 +292,7 @@ When context pressure is rising:
|
|||
4. add distilled evidence only when the lesson changes future belief
|
||||
|
||||
The point is to bank coals, not leave the next waking thing a pile of ash and a browser tab graveyard.
|
||||
|
||||
# Epiphany Operator Result Leg
|
||||
|
||||
The `/epiphany` result leg consumes Bifrost's frozen `bifrost.discord.epiphany_operator_delivery.v0` outbox contract. It persists private Discord interaction correlation before request submission, validates the exact immutable request digest and all Discord/runtime bindings, and idempotently edits the original ephemeral reply. Restart replay, same-key collision, private-state rejection, sealed-field shape, bounded operator fields, and transient Discord retry are covered by `scripts/smoke-epiphany-operator-delivery.ts`. Deployment remains Idunn-owned and was not performed in this pass.
|
||||
|
|
|
|||
|
|
@ -326,4 +326,17 @@ Within the Postgres path, the implementation is split on purpose now too:
|
|||
|
||||
The explicit `/epiphany` slash surface is a separate operator-request fork. It accepts only `status`, `sleep(reason)`, `wake`, and `direct(objective)`, requires exact equality with `DISCORD_OWNER_ID`, and writes one immutable, sixty-second `voidbot.discord.epiphany_operator_request.v0` document to `BIFROST_EPIPHANY_OPERATOR_REQUEST_STORE`. The Discord interaction id is the request id, command id, nonce, source event, and slash-message identity. The target is fixed to `epiphany-yggdrasil`. VoidBot does not authenticate the crossing, execute locally, open Epiphany state, or grant Mind, Hands, release, or deployment authority; Bifrost must independently authenticate and admit the request. Ordinary `MessageCreate` content, including the word `wake`, stays on the feedback-only path and cannot call the operator client.
|
||||
|
||||
The matching result leg consumes only immutable `bifrost.discord.epiphany_operator_delivery.v0` documents from `BIFROST_EPIPHANY_OPERATOR_DISCORD_DELIVERY_STORE`. Bifrost owns terminal execution truth; VoidBot recomputes the exact MessagePack request digest from its own request inbox, checks request/command/guild/channel/interaction/runtime bindings and `privateStateExposed=false`, then lowers only bounded completed/refused status fields into the original ephemeral Discord interaction. The interaction token lives only in VoidBot's private typed checkpoint store. PATCHing `@original` is the shared idempotent response primitive. The checkpoint records the first delivery hash before Discord I/O and records completion afterward, so restart replay edits one original response, changed same-key content collides closed, and transient Discord failure remains visible and retryable without acknowledging or mutating Bifrost's outbox.
|
||||
|
||||
Result-leg authority map:
|
||||
|
||||
- Owner: Bifrost owns immutable terminal delivery; VoidBot owns only exact correlation and Discord presentation.
|
||||
- Inputs: VoidBot's exact immutable request, Bifrost's exact delivery, and VoidBot's private interaction checkpoint.
|
||||
- Outputs: one edit to the original ephemeral interaction plus a private responded checkpoint.
|
||||
- Derived state: rendered operator text and attempt/error counters are presentation and transport witnesses, never Epiphany state.
|
||||
- Forbidden writers: Discord chat, renderers, checkpoint retries, and VoidBot inference cannot create execution truth, change a delivery, retry a consequence, or inspect sealed result/signature payloads.
|
||||
- Shared path: first response, process restart, and transient Discord retry all use the same webhook PATCH and checkpoint transition.
|
||||
- Cut line: no raw signature/key/private state, no Epiphany store reader, no chat inference, no local executor, and no outbox acknowledgment/deletion exists in this organ.
|
||||
- Verification layer: hostile schema/binding tests plus a real temporary CultCache/CultMesh request-delivery-checkpoint replay smoke.
|
||||
|
||||
Configured remote Persona feedback is an observation fork beside the existing mention scheduler. `discord-bot.ts` commits the local Persona mention obligation first and independently calls `exportPersonaFeedbackObservation` only when `remotePersonaFeedbackTarget` is present on the addressed Face. The exported immutable event carries exact Discord provenance, addressing mode, target binding, content hash, and stable `voidbot` producer identity. It writes only to `BIFROST_PERSONA_FEEDBACK_OBSERVATION_STORE_PATH`, never the outbound Discord command provider store or Bifrost-private binding/receipt state. It carries `feedback_only`; Bifrost—not VoidBot—classifies actor links, admits targets, and produces downstream cognition pressure.
|
||||
|
|
|
|||
1
package-lock.json
generated
1
package-lock.json
generated
|
|
@ -2814,6 +2814,7 @@
|
|||
"name": "@voidbot/core",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@msgpack/msgpack": "^3.1.3",
|
||||
"@voidbot/shared": "0.1.0",
|
||||
"cultcache-ts": "file:../../vendor/cultcache-ts",
|
||||
"pg": "^8.20.0",
|
||||
|
|
|
|||
|
|
@ -137,6 +137,9 @@ const envSchema = z.object({
|
|||
BIFROST_CULTMESH_COMMAND_PUMP_ENABLED: booleanFromEnv.default(true),
|
||||
BIFROST_PERSONA_FEEDBACK_OBSERVATION_STORE_PATH: z.string().min(1).default("E:/Projects/Bifrost/.bifrost/persona-feedback-observations.cc"),
|
||||
BIFROST_EPIPHANY_OPERATOR_REQUEST_STORE: z.string().min(1).default("E:/Projects/Bifrost/.bifrost/epiphany-operator-requests.cc"),
|
||||
BIFROST_EPIPHANY_OPERATOR_DISCORD_DELIVERY_STORE: z.string().min(1).default("E:/Projects/Bifrost/.bifrost/epiphany-operator-discord-deliveries.cc"),
|
||||
EPIPHANY_OPERATOR_DELIVERY_CHECKPOINT_STORE_PATH: z.string().min(1).default(".voidbot/private/epiphany-operator-delivery-checkpoints.cc"),
|
||||
EPIPHANY_OPERATOR_DELIVERY_POLL_INTERVAL_MS: z.coerce.number().int().positive().default(2000),
|
||||
REPO_FACE_HEARTBEATS_ENABLED: booleanFromEnv.default(false),
|
||||
VOID_MODERATION_DAEMON_ENABLED: booleanFromEnv.default(false),
|
||||
VOID_MODERATION_DAEMON_INTERVAL_MINUTES: z.coerce.number().int().min(5).default(15),
|
||||
|
|
@ -259,6 +262,9 @@ export interface AppConfig {
|
|||
};
|
||||
bifrostPersonaFeedbackObservationStorePath: string;
|
||||
bifrostEpiphanyOperatorRequestStorePath: string;
|
||||
bifrostEpiphanyOperatorDeliveryStorePath: string;
|
||||
epiphanyOperatorDeliveryCheckpointStorePath: string;
|
||||
epiphanyOperatorDeliveryPollIntervalMs: number;
|
||||
voidModerationDaemon: { enabled: boolean; intervalMinutes: number; enforcementMode: string };
|
||||
voidCandidateDelivery: { enabled: boolean; personaName: string; personaAvatarUrl?: string };
|
||||
voidPhysiology: { intervalMinutes: number };
|
||||
|
|
@ -554,6 +560,9 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig {
|
|||
},
|
||||
bifrostPersonaFeedbackObservationStorePath: resolve(parsed.BIFROST_PERSONA_FEEDBACK_OBSERVATION_STORE_PATH),
|
||||
bifrostEpiphanyOperatorRequestStorePath: resolve(parsed.BIFROST_EPIPHANY_OPERATOR_REQUEST_STORE),
|
||||
bifrostEpiphanyOperatorDeliveryStorePath: resolve(parsed.BIFROST_EPIPHANY_OPERATOR_DISCORD_DELIVERY_STORE),
|
||||
epiphanyOperatorDeliveryCheckpointStorePath: resolve(parsed.EPIPHANY_OPERATOR_DELIVERY_CHECKPOINT_STORE_PATH),
|
||||
epiphanyOperatorDeliveryPollIntervalMs: parsed.EPIPHANY_OPERATOR_DELIVERY_POLL_INTERVAL_MS,
|
||||
voidModerationDaemon: {
|
||||
enabled: parsed.VOID_MODERATION_DAEMON_ENABLED,
|
||||
intervalMinutes: parsed.VOID_MODERATION_DAEMON_INTERVAL_MINUTES,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"dependencies": {
|
||||
"@msgpack/msgpack": "^3.1.3",
|
||||
"@voidbot/shared": "0.1.0",
|
||||
"cultcache-ts": "file:../../vendor/cultcache-ts",
|
||||
"pg": "^8.20.0",
|
||||
|
|
|
|||
241
packages/core/src/epiphany-operator-delivery.ts
Normal file
241
packages/core/src/epiphany-operator-delivery.ts
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
import { createHash } from "node:crypto";
|
||||
import { createRequire } from "node:module";
|
||||
import { resolve } from "node:path";
|
||||
import { encode } from "@msgpack/msgpack";
|
||||
|
||||
import {
|
||||
EPIPHANY_OPERATOR_REQUEST_SCHEMA,
|
||||
EPIPHANY_OPERATOR_REQUEST_TYPE,
|
||||
EPIPHANY_OPERATOR_RUNTIME_ID,
|
||||
type EpiphanyOperatorRequestDocument,
|
||||
} from "./epiphany-operator-request";
|
||||
|
||||
export const EPIPHANY_OPERATOR_DELIVERY_TYPE = "bifrost.discord.epiphany_operator_delivery";
|
||||
export const EPIPHANY_OPERATOR_DELIVERY_SCHEMA = "bifrost.discord.epiphany_operator_delivery.v0";
|
||||
export const EPIPHANY_OPERATOR_CHECKPOINT_TYPE = "voidbot.private.epiphany_operator_delivery_checkpoint";
|
||||
export const EPIPHANY_OPERATOR_CHECKPOINT_SCHEMA = "voidbot.private.epiphany_operator_delivery_checkpoint.v0";
|
||||
|
||||
const DELIVERY_KEYS = [
|
||||
"schemaVersion", "deliveryId", "requestId", "requestPayloadSha256", "commandId",
|
||||
"discordGuildId", "discordChannelId", "discordInteractionId", "targetRuntimeId",
|
||||
"status", "disposition", "failureCode", "detail", "sealedResultPayloadSha256",
|
||||
"operatorStatus", "stateStatus", "coordinatorAction", "brakeStatus", "executorSignatureSha256",
|
||||
"resultProviderIdentityId", "recordedAt", "privateStateExposed",
|
||||
] as const;
|
||||
const SHA256 = /^sha256-[0-9a-f]{64}$/;
|
||||
|
||||
export interface EpiphanyOperatorDeliveryDocument {
|
||||
schemaVersion: typeof EPIPHANY_OPERATOR_DELIVERY_SCHEMA;
|
||||
deliveryId: string;
|
||||
requestId: string;
|
||||
requestPayloadSha256: string;
|
||||
commandId: string;
|
||||
discordGuildId: string;
|
||||
discordChannelId: string;
|
||||
discordInteractionId: string;
|
||||
targetRuntimeId: typeof EPIPHANY_OPERATOR_RUNTIME_ID;
|
||||
status: "completed" | "refused";
|
||||
disposition: string;
|
||||
failureCode: string;
|
||||
detail: string;
|
||||
operatorStatus: string;
|
||||
stateStatus: string;
|
||||
coordinatorAction: string;
|
||||
brakeStatus: string;
|
||||
sealedResultPayloadSha256: string;
|
||||
executorSignatureSha256: string;
|
||||
resultProviderIdentityId: string;
|
||||
recordedAt: string;
|
||||
privateStateExposed: false;
|
||||
}
|
||||
|
||||
export interface EpiphanyOperatorDeliveryCheckpoint {
|
||||
schemaVersion: typeof EPIPHANY_OPERATOR_CHECKPOINT_SCHEMA;
|
||||
requestId: string;
|
||||
applicationId: string;
|
||||
interactionToken: string;
|
||||
discordGuildId: string;
|
||||
discordChannelId: string;
|
||||
discordInteractionId: string;
|
||||
targetRuntimeId: typeof EPIPHANY_OPERATOR_RUNTIME_ID;
|
||||
state: "pending" | "responded";
|
||||
observedDeliverySha256: string;
|
||||
respondedDeliverySha256: string;
|
||||
attemptCount: number;
|
||||
lastError: string;
|
||||
registeredAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface MeshNode {
|
||||
put: (definition: unknown, key: string, value: unknown) => Promise<void>;
|
||||
get: (definition: unknown, key: string) => unknown;
|
||||
flush?: () => Promise<void>;
|
||||
cache: { pullAllBackingStores?: () => Promise<void>; getAll: (definition: unknown) => unknown[] };
|
||||
}
|
||||
|
||||
export interface EpiphanyOperatorDeliveryStoresConfig {
|
||||
requestStorePath: string;
|
||||
deliveryStorePath: string;
|
||||
checkpointStorePath: string;
|
||||
bifrostRoot: string;
|
||||
cultlibRoot?: string;
|
||||
}
|
||||
|
||||
export interface RegisterEpiphanyOperatorInteractionInput {
|
||||
requestId: string;
|
||||
applicationId: string;
|
||||
interactionToken: string;
|
||||
guildId: string;
|
||||
channelId: string;
|
||||
registeredAt?: string;
|
||||
}
|
||||
|
||||
export async function registerEpiphanyOperatorInteraction(
|
||||
input: RegisterEpiphanyOperatorInteractionInput,
|
||||
config: Pick<EpiphanyOperatorDeliveryStoresConfig, "checkpointStorePath" | "bifrostRoot" | "cultlibRoot">,
|
||||
): Promise<void> {
|
||||
const runtime = loadRuntime(config);
|
||||
const definition = checkpointDefinition(runtime.defineDocumentType);
|
||||
const node = await runtime.CultMesh.createNode(config.checkpointStorePath, { documents: [definition] }) as MeshNode;
|
||||
await node.cache?.pullAllBackingStores?.();
|
||||
const now = input.registeredAt ?? new Date().toISOString();
|
||||
const checkpoint: EpiphanyOperatorDeliveryCheckpoint = {
|
||||
schemaVersion: EPIPHANY_OPERATOR_CHECKPOINT_SCHEMA,
|
||||
requestId: required(input.requestId, "requestId"),
|
||||
applicationId: required(input.applicationId, "applicationId"),
|
||||
interactionToken: required(input.interactionToken, "interactionToken"),
|
||||
discordGuildId: required(input.guildId, "guildId"),
|
||||
discordChannelId: required(input.channelId, "channelId"),
|
||||
discordInteractionId: required(input.requestId, "requestId"),
|
||||
targetRuntimeId: EPIPHANY_OPERATOR_RUNTIME_ID,
|
||||
state: "pending", observedDeliverySha256: "", respondedDeliverySha256: "",
|
||||
attemptCount: 0, lastError: "", registeredAt: now, updatedAt: now,
|
||||
};
|
||||
const existing = unwrap(node.get(definition, checkpoint.requestId));
|
||||
if (existing && stable(existing) !== stable(checkpoint)) {
|
||||
throw new Error(`Epiphany interaction checkpoint ${checkpoint.requestId} already has different immutable registration.`);
|
||||
}
|
||||
if (!existing) await node.put(definition, checkpoint.requestId, checkpoint);
|
||||
await node.flush?.();
|
||||
}
|
||||
|
||||
export async function consumeEpiphanyOperatorDeliveries(
|
||||
config: EpiphanyOperatorDeliveryStoresConfig,
|
||||
editOriginal: (applicationId: string, interactionToken: string, content: string) => Promise<void>,
|
||||
): Promise<{ observed: number; responded: number; failed: number }> {
|
||||
const runtime = loadRuntime(config);
|
||||
const requestDef = requestDefinition(runtime.defineDocumentType);
|
||||
const deliveryDef = deliveryDefinition(runtime.defineDocumentType);
|
||||
const checkpointDef = checkpointDefinition(runtime.defineDocumentType);
|
||||
const [requests, deliveries, checkpoints] = await Promise.all([
|
||||
runtime.CultMesh.createNode(config.requestStorePath, { documents: [requestDef] }),
|
||||
runtime.CultMesh.createNode(config.deliveryStorePath, { documents: [deliveryDef] }),
|
||||
runtime.CultMesh.createNode(config.checkpointStorePath, { documents: [checkpointDef] }),
|
||||
]) as MeshNode[];
|
||||
await Promise.all([requests, deliveries, checkpoints].map((node) => node.cache?.pullAllBackingStores?.()));
|
||||
let observed = 0; let responded = 0; let failed = 0;
|
||||
for (const raw of deliveries.cache.getAll(deliveryDef)) {
|
||||
observed += 1;
|
||||
const candidate = unwrap(raw);
|
||||
const requestId = isRecord(candidate) && typeof candidate.requestId === "string" ? candidate.requestId : "";
|
||||
const checkpoint = unwrap(checkpoints.get(checkpointDef, requestId)) as EpiphanyOperatorDeliveryCheckpoint | undefined;
|
||||
if (!checkpoint || checkpoint.state === "responded") continue;
|
||||
const request = unwrap(requests.get(requestDef, requestId)) as EpiphanyOperatorRequestDocument | undefined;
|
||||
const outcome = await processEpiphanyOperatorDelivery(candidate, request, checkpoint, editOriginal, async (next) => {
|
||||
await checkpoints.put(checkpointDef, checkpoint.requestId, next);
|
||||
await checkpoints.flush?.();
|
||||
});
|
||||
if (outcome === "responded") responded += 1; else if (outcome === "failed") failed += 1;
|
||||
}
|
||||
return { observed, responded, failed };
|
||||
}
|
||||
|
||||
export async function processEpiphanyOperatorDelivery(
|
||||
candidate: unknown,
|
||||
request: EpiphanyOperatorRequestDocument | undefined,
|
||||
checkpoint: EpiphanyOperatorDeliveryCheckpoint,
|
||||
editOriginal: (applicationId: string, interactionToken: string, content: string) => Promise<void>,
|
||||
saveCheckpoint: (checkpoint: EpiphanyOperatorDeliveryCheckpoint) => Promise<void>,
|
||||
): Promise<"responded" | "failed" | "replayed"> {
|
||||
if (checkpoint.state === "responded") return "replayed";
|
||||
const deliveryHash = hashStable(candidate);
|
||||
try {
|
||||
if (checkpoint.observedDeliverySha256 && checkpoint.observedDeliverySha256 !== deliveryHash) throw new Error("immutable delivery collision");
|
||||
const delivery = validateDelivery(candidate);
|
||||
validateBindings(delivery, request, checkpoint);
|
||||
const observed = { ...checkpoint, observedDeliverySha256: deliveryHash, updatedAt: new Date().toISOString() };
|
||||
await saveCheckpoint(observed);
|
||||
await editOriginal(checkpoint.applicationId, checkpoint.interactionToken, renderDelivery(delivery, request!));
|
||||
await saveCheckpoint({ ...observed, state: "responded", respondedDeliverySha256: deliveryHash,
|
||||
attemptCount: checkpoint.attemptCount + 1, lastError: "", updatedAt: new Date().toISOString() });
|
||||
return "responded";
|
||||
} catch (error) {
|
||||
await saveCheckpoint({ ...checkpoint, observedDeliverySha256: checkpoint.observedDeliverySha256 || deliveryHash,
|
||||
attemptCount: checkpoint.attemptCount + 1, lastError: boundedError(error), updatedAt: new Date().toISOString() });
|
||||
return "failed";
|
||||
}
|
||||
}
|
||||
|
||||
export function epiphanyOperatorRequestPayloadSha256(request: EpiphanyOperatorRequestDocument): string {
|
||||
const compact = request.command.kind === "status" || request.command.kind === "wake"
|
||||
? request.command.kind
|
||||
: request.command.kind === "sleep" ? ["sleep", request.command.reason] : ["directive", request.command.objective];
|
||||
const tuple = [request.schemaName, request.schemaVersion, request.requestId, request.commandId, request.nonce,
|
||||
request.sourceEventId, request.sourceActorDiscordId, request.discordGuildId, request.discordChannelId,
|
||||
request.discordMessageId, request.targetRuntimeId, request.issuedAt, request.expiresAt, request.producerId,
|
||||
request.producerRuntimeId, request.authorityClass, request.status, compact];
|
||||
return `sha256-${createHash("sha256").update(encode(tuple)).digest("hex")}`;
|
||||
}
|
||||
|
||||
export function validateDelivery(value: unknown): EpiphanyOperatorDeliveryDocument {
|
||||
if (!isRecord(value) || Object.keys(value).sort().join("\0") !== [...DELIVERY_KEYS].sort().join("\0")) throw new Error("delivery has an inexact field set");
|
||||
for (const key of DELIVERY_KEYS) if (key !== "privateStateExposed" && typeof value[key] !== "string") throw new Error(`delivery ${key} must be a string`);
|
||||
if (value.schemaVersion !== EPIPHANY_OPERATOR_DELIVERY_SCHEMA) throw new Error("delivery schema mismatch");
|
||||
if (!required(value.deliveryId as string, "deliveryId") || value.deliveryId !== value.requestId) throw new Error("delivery identity mismatch");
|
||||
if (!SHA256.test(value.requestPayloadSha256 as string)) throw new Error("request payload digest malformed");
|
||||
if (value.targetRuntimeId !== EPIPHANY_OPERATOR_RUNTIME_ID) throw new Error("delivery runtime mismatch");
|
||||
if (value.privateStateExposed !== false) throw new Error("delivery exposes private state");
|
||||
if (typeof value.detail !== "string" || value.detail.length > 512) throw new Error("delivery detail exceeds 512 characters");
|
||||
for (const key of ["operatorStatus", "stateStatus", "coordinatorAction", "brakeStatus"] as const) {
|
||||
if (typeof value[key] !== "string" || value[key].length > 512) throw new Error(`delivery ${key} exceeds 512 characters`);
|
||||
}
|
||||
if (!Number.isFinite(Date.parse(value.recordedAt as string))) throw new Error("delivery recordedAt is invalid");
|
||||
if (value.status === "completed") {
|
||||
if (!required(value.disposition as string, "disposition") || !SHA256.test(value.sealedResultPayloadSha256 as string) || !SHA256.test(value.executorSignatureSha256 as string) || !required(value.resultProviderIdentityId as string, "resultProviderIdentityId")) throw new Error("completed delivery lacks sealed bindings");
|
||||
if (value.failureCode !== "") throw new Error("completed delivery has a failure code");
|
||||
} else if (value.status === "refused") {
|
||||
if (!required(value.failureCode as string, "failureCode")) throw new Error("refused delivery lacks failure code");
|
||||
if (value.disposition !== "" || value.operatorStatus !== "" || value.stateStatus !== "" || value.coordinatorAction !== "" || value.brakeStatus !== "" || value.sealedResultPayloadSha256 !== "" || value.executorSignatureSha256 !== "" || value.resultProviderIdentityId !== "") throw new Error("refused delivery carries completed-result bindings");
|
||||
} else throw new Error("delivery status is not terminal");
|
||||
return value as unknown as EpiphanyOperatorDeliveryDocument;
|
||||
}
|
||||
|
||||
function validateBindings(delivery: EpiphanyOperatorDeliveryDocument, request: EpiphanyOperatorRequestDocument | undefined, checkpoint: EpiphanyOperatorDeliveryCheckpoint): void {
|
||||
if (!request || request.schemaName !== EPIPHANY_OPERATOR_REQUEST_TYPE || request.schemaVersion !== EPIPHANY_OPERATOR_REQUEST_SCHEMA) throw new Error("exact operator request is unavailable");
|
||||
if (delivery.requestId !== request.requestId || delivery.commandId !== request.commandId || delivery.discordGuildId !== request.discordGuildId || delivery.discordChannelId !== request.discordChannelId || delivery.discordInteractionId !== request.sourceEventId || delivery.targetRuntimeId !== request.targetRuntimeId) throw new Error("delivery does not bind the exact request");
|
||||
if (delivery.requestPayloadSha256 !== epiphanyOperatorRequestPayloadSha256(request)) throw new Error("delivery request digest mismatch");
|
||||
if (checkpoint.requestId !== request.requestId || checkpoint.discordGuildId !== request.discordGuildId || checkpoint.discordChannelId !== request.discordChannelId || checkpoint.discordInteractionId !== request.sourceEventId || checkpoint.targetRuntimeId !== request.targetRuntimeId) throw new Error("private interaction binding mismatch");
|
||||
}
|
||||
|
||||
function renderDelivery(delivery: EpiphanyOperatorDeliveryDocument, request: EpiphanyOperatorRequestDocument): string {
|
||||
const command = request.command.kind === "directive" ? "direct" : request.command.kind;
|
||||
const headline = delivery.status === "completed" ? `Completed (${delivery.disposition})` : `Refused (${delivery.failureCode})`;
|
||||
const detail = delivery.detail ? `\n${delivery.detail}` : "";
|
||||
const statuses = delivery.status === "completed"
|
||||
? [["Operator", delivery.operatorStatus], ["State", delivery.stateStatus], ["Coordinator", delivery.coordinatorAction], ["Brakes", delivery.brakeStatus]]
|
||||
.filter((entry) => entry[1]).map(([label, value]) => `${label}: ${value}`).join("\n")
|
||||
: "";
|
||||
return `Epiphany ${command}: ${headline}.${detail}${statuses ? `\n${statuses}` : ""}\nBifrost reported this terminal result; VoidBot did not execute or inspect Epiphany state.`;
|
||||
}
|
||||
|
||||
function requestDefinition(define: any): unknown { return define({ type: EPIPHANY_OPERATOR_REQUEST_TYPE, schemaName: EPIPHANY_OPERATOR_REQUEST_TYPE, schemaId: EPIPHANY_OPERATOR_REQUEST_SCHEMA, schemaVersion: EPIPHANY_OPERATOR_REQUEST_SCHEMA, contentHash: EPIPHANY_OPERATOR_REQUEST_SCHEMA, global: false, name: "requestId", schema: { parse: (v: unknown) => v } }); }
|
||||
function deliveryDefinition(define: any): unknown { return define({ type: EPIPHANY_OPERATOR_DELIVERY_TYPE, schemaName: EPIPHANY_OPERATOR_DELIVERY_TYPE, schemaId: EPIPHANY_OPERATOR_DELIVERY_SCHEMA, schemaVersion: EPIPHANY_OPERATOR_DELIVERY_SCHEMA, contentHash: EPIPHANY_OPERATOR_DELIVERY_SCHEMA, global: false, name: "deliveryId", schema: { parse: (v: unknown) => v } }); }
|
||||
function checkpointDefinition(define: any): unknown { return define({ type: EPIPHANY_OPERATOR_CHECKPOINT_TYPE, schemaName: EPIPHANY_OPERATOR_CHECKPOINT_TYPE, schemaId: EPIPHANY_OPERATOR_CHECKPOINT_SCHEMA, schemaVersion: EPIPHANY_OPERATOR_CHECKPOINT_SCHEMA, contentHash: EPIPHANY_OPERATOR_CHECKPOINT_SCHEMA, global: false, name: "requestId", schema: { parse: (v: unknown) => v } }); }
|
||||
function loadRuntime(config: { bifrostRoot: string; cultlibRoot?: string }): any { const root = config.cultlibRoot ?? resolve(config.bifrostRoot, "..", "CultLib"); const mesh = resolve(root, "packages", "cultmesh-ts", "package.json"); const cache = resolve(root, "packages", "cultcache-ts", "package.json"); return { CultMesh: createRequire(mesh)("cultmesh-ts").CultMesh, defineDocumentType: createRequire(cache)("cultcache-ts").defineDocumentType }; }
|
||||
function unwrap(value: unknown): any { const record = value as { value?: unknown } | undefined; return Array.isArray(value) ? value[0] : record?.value ?? value; }
|
||||
function stable(value: unknown): string { return JSON.stringify(value, (_k, v) => v instanceof Uint8Array ? Buffer.from(v).toString("hex") : v); }
|
||||
function hashStable(value: unknown): string { return `sha256-${createHash("sha256").update(stable(value)).digest("hex")}`; }
|
||||
function isRecord(value: unknown): value is Record<string, any> { return !!value && typeof value === "object" && !Array.isArray(value); }
|
||||
function required(value: string, label: string): string { const text = value?.trim(); if (!text) throw new Error(`${label} is required`); return text; }
|
||||
function boundedError(error: unknown): string { return (error instanceof Error ? error.message : String(error)).slice(0, 512); }
|
||||
|
|
@ -29,3 +29,4 @@ export * from "./persona-state-service";
|
|||
export * from "./bifrost-discord-command";
|
||||
export * from "./persona-feedback-observation";
|
||||
export * from "./epiphany-operator-request";
|
||||
export * from "./epiphany-operator-delivery";
|
||||
|
|
|
|||
128
scripts/smoke-epiphany-operator-delivery.ts
Normal file
128
scripts/smoke-epiphany-operator-delivery.ts
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { createRequire } from "node:module";
|
||||
import { join, resolve } from "node:path";
|
||||
|
||||
import {
|
||||
buildEpiphanyOperatorRequest,
|
||||
epiphanyOperatorRequestPayloadSha256,
|
||||
processEpiphanyOperatorDelivery,
|
||||
consumeEpiphanyOperatorDeliveries,
|
||||
registerEpiphanyOperatorInteraction,
|
||||
submitEpiphanyOperatorRequest,
|
||||
validateDelivery,
|
||||
type EpiphanyOperatorDeliveryDocument,
|
||||
type EpiphanyOperatorDeliveryCheckpoint,
|
||||
} from "../packages/core/src/index";
|
||||
|
||||
async function main(): Promise<void> {
|
||||
|
||||
const request = buildEpiphanyOperatorRequest({
|
||||
interactionId: "interaction-1", actorDiscordId: "owner-1", guildId: "guild-1",
|
||||
channelId: "channel-1", command: { kind: "sleep", reason: "bank the fire" },
|
||||
issuedAt: "2026-07-19T12:00:00.000Z",
|
||||
}, "voidbot-yggdrasil");
|
||||
const digest = epiphanyOperatorRequestPayloadSha256(request);
|
||||
assert.match(digest, /^sha256-[0-9a-f]{64}$/);
|
||||
assert.notEqual(digest, epiphanyOperatorRequestPayloadSha256({ ...request, discordGuildId: "other" }));
|
||||
assert.notEqual(digest, epiphanyOperatorRequestPayloadSha256({ ...request, command: { kind: "wake" } }));
|
||||
|
||||
const completed: EpiphanyOperatorDeliveryDocument = {
|
||||
schemaVersion: "bifrost.discord.epiphany_operator_delivery.v0",
|
||||
deliveryId: request.requestId, requestId: request.requestId, requestPayloadSha256: digest,
|
||||
commandId: request.commandId, discordGuildId: request.discordGuildId,
|
||||
discordChannelId: request.discordChannelId, discordInteractionId: request.sourceEventId,
|
||||
targetRuntimeId: "epiphany-yggdrasil", status: "completed", disposition: "observed",
|
||||
failureCode: "", detail: "sleep accepted", operatorStatus: "sleeping", stateStatus: "banked",
|
||||
coordinatorAction: "none", brakeStatus: "engaged",
|
||||
sealedResultPayloadSha256: `sha256-${"a".repeat(64)}`,
|
||||
executorSignatureSha256: `sha256-${"b".repeat(64)}`, resultProviderIdentityId: "bifrost-yggdrasil",
|
||||
recordedAt: "2026-07-19T12:00:01.000Z", privateStateExposed: false,
|
||||
};
|
||||
assert.equal(validateDelivery(completed).status, "completed");
|
||||
|
||||
const refused: EpiphanyOperatorDeliveryDocument = {
|
||||
...completed, status: "refused", disposition: "", failureCode: "brakes_engaged", detail: "refused",
|
||||
operatorStatus: "", stateStatus: "", coordinatorAction: "", brakeStatus: "",
|
||||
sealedResultPayloadSha256: "", executorSignatureSha256: "", resultProviderIdentityId: "",
|
||||
};
|
||||
assert.equal(validateDelivery(refused).status, "refused");
|
||||
|
||||
function rejects(change: Record<string, unknown>, pattern: RegExp): void {
|
||||
assert.throws(() => validateDelivery({ ...completed, ...change }), pattern);
|
||||
}
|
||||
rejects({ privateStateExposed: true }, /private state/);
|
||||
rejects({ status: "pending" }, /terminal/);
|
||||
rejects({ detail: "x".repeat(513) }, /detail/);
|
||||
rejects({ operatorStatus: "x".repeat(513) }, /operatorStatus/);
|
||||
rejects({ requestPayloadSha256: "raw-payload" }, /digest/);
|
||||
rejects({ targetRuntimeId: "epiphany-nightwing" }, /runtime/);
|
||||
rejects({ deliveryId: "collision" }, /identity/);
|
||||
rejects({ executorSignatureSha256: "signature-bytes" }, /sealed bindings/);
|
||||
rejects({ resultProviderIdentityId: "" }, /resultProviderIdentityId|sealed bindings/);
|
||||
rejects({ surprisePrivatePayload: "secret" }, /field set/);
|
||||
assert.throws(() => validateDelivery({ ...refused, brakeStatus: "not empty" }), /completed-result bindings/);
|
||||
assert.throws(() => validateDelivery({ ...refused, failureCode: "" }), /failureCode|failure code/);
|
||||
|
||||
const baseCheckpoint: EpiphanyOperatorDeliveryCheckpoint = {
|
||||
schemaVersion: "voidbot.private.epiphany_operator_delivery_checkpoint.v0", requestId: request.requestId,
|
||||
applicationId: "app-1", interactionToken: "private-token", discordGuildId: request.discordGuildId,
|
||||
discordChannelId: request.discordChannelId, discordInteractionId: request.sourceEventId,
|
||||
targetRuntimeId: "epiphany-yggdrasil", state: "pending", observedDeliverySha256: "",
|
||||
respondedDeliverySha256: "", attemptCount: 0, lastError: "",
|
||||
registeredAt: "2026-07-19T12:00:00.000Z", updatedAt: "2026-07-19T12:00:00.000Z",
|
||||
};
|
||||
let current = baseCheckpoint;
|
||||
let editCount = 0;
|
||||
let failEdit = true;
|
||||
const edit = async (_app: string, token: string, content: string): Promise<void> => {
|
||||
assert.equal(token, "private-token");
|
||||
assert.doesNotMatch(content, /signature|private-token|sha256-/i);
|
||||
editCount += 1;
|
||||
if (failEdit) throw new Error("temporary Discord failure");
|
||||
};
|
||||
const save = async (next: EpiphanyOperatorDeliveryCheckpoint): Promise<void> => { current = next; };
|
||||
assert.equal(await processEpiphanyOperatorDelivery(completed, request, current, edit, save), "failed");
|
||||
assert.equal(current.state, "pending");
|
||||
assert.match(current.lastError, /temporary Discord failure/);
|
||||
assert.ok(current.observedDeliverySha256);
|
||||
failEdit = false;
|
||||
assert.equal(await processEpiphanyOperatorDelivery(completed, request, current, edit, save), "responded");
|
||||
assert.equal(current.state, "responded");
|
||||
assert.equal(await processEpiphanyOperatorDelivery(completed, request, current, edit, save), "replayed");
|
||||
assert.equal(editCount, 2, "a restart replay must not create another response");
|
||||
|
||||
for (const [field, value] of [["commandId", "wrong"], ["discordGuildId", "wrong"], ["discordChannelId", "wrong"], ["discordInteractionId", "wrong"], ["targetRuntimeId", "epiphany-nightwing"], ["requestPayloadSha256", `sha256-${"c".repeat(64)}`]] as const) {
|
||||
let rejectedCheckpoint = { ...baseCheckpoint };
|
||||
assert.equal(await processEpiphanyOperatorDelivery({ ...completed, [field]: value }, request, rejectedCheckpoint, async () => { throw new Error("must not edit"); }, async (next) => { rejectedCheckpoint = next; }), "failed");
|
||||
assert.match(rejectedCheckpoint.lastError, /request|runtime|digest/);
|
||||
}
|
||||
let collisionCheckpoint = { ...baseCheckpoint, observedDeliverySha256: `sha256-${"d".repeat(64)}` };
|
||||
assert.equal(await processEpiphanyOperatorDelivery(completed, request, collisionCheckpoint, async () => { throw new Error("must not edit"); }, async (next) => { collisionCheckpoint = next; }), "failed");
|
||||
assert.match(collisionCheckpoint.lastError, /collision/);
|
||||
|
||||
const temp = await mkdtemp(join(tmpdir(), "voidbot-epiphany-delivery-"));
|
||||
try {
|
||||
const bifrostRoot = resolve("../Bifrost");
|
||||
const cultlibRoot = resolve("../CultLib");
|
||||
const stores = { requestStorePath: join(temp, "requests.cc"), deliveryStorePath: join(temp, "deliveries.cc"), checkpointStorePath: join(temp, "checkpoints.cc"), bifrostRoot, cultlibRoot };
|
||||
await registerEpiphanyOperatorInteraction({ requestId: request.requestId, applicationId: "app-1", interactionToken: "private-token", guildId: request.discordGuildId, channelId: request.discordChannelId, registeredAt: request.issuedAt }, stores);
|
||||
await submitEpiphanyOperatorRequest({ interactionId: request.requestId, actorDiscordId: request.sourceActorDiscordId, guildId: request.discordGuildId, channelId: request.discordChannelId, command: request.command, issuedAt: request.issuedAt }, { storePath: stores.requestStorePath, bifrostRoot, cultlibRoot, producerRuntimeId: request.producerRuntimeId });
|
||||
const cachePackage = resolve(cultlibRoot, "packages/cultcache-ts/package.json");
|
||||
const meshPackage = resolve(cultlibRoot, "packages/cultmesh-ts/package.json");
|
||||
const define = createRequire(cachePackage)("cultcache-ts").defineDocumentType;
|
||||
const CultMesh = createRequire(meshPackage)("cultmesh-ts").CultMesh;
|
||||
const deliveryDef = define({ type: "bifrost.discord.epiphany_operator_delivery", schemaName: "bifrost.discord.epiphany_operator_delivery", schemaId: completed.schemaVersion, schemaVersion: completed.schemaVersion, contentHash: completed.schemaVersion, global: false, name: "deliveryId", schema: { parse: (value: unknown) => value } });
|
||||
const deliveryNode = await CultMesh.createNode(stores.deliveryStorePath, { documents: [deliveryDef] });
|
||||
await deliveryNode.put(deliveryDef, completed.deliveryId, completed);
|
||||
let integrationEdits = 0;
|
||||
assert.deepEqual(await consumeEpiphanyOperatorDeliveries(stores, async () => { integrationEdits += 1; }), { observed: 1, responded: 1, failed: 0 });
|
||||
assert.deepEqual(await consumeEpiphanyOperatorDeliveries(stores, async () => { integrationEdits += 1; }), { observed: 1, responded: 0, failed: 0 });
|
||||
assert.equal(integrationEdits, 1);
|
||||
} finally { await rm(temp, { recursive: true, force: true }); }
|
||||
|
||||
console.log("Epiphany operator delivery smoke passed.");
|
||||
}
|
||||
|
||||
void main().catch((error) => { console.error(error); process.exitCode = 1; });
|
||||
|
|
@ -33,6 +33,7 @@ constraints:
|
|||
|
||||
invariants:
|
||||
- The `/epiphany` Discord edge is an exact-owner request producer only. Its closed status/sleep/wake/direct command family writes short-lived immutable documents to a dedicated Bifrost inbox; ordinary conversation remains feedback-only, and VoidBot gains no Epiphany state, Mind, Hands, release, deployment, argv, or local-execution authority.
|
||||
- The `/epiphany` result edge consumes only Bifrost's frozen immutable terminal delivery schema, validates it against VoidBot's exact request and private Discord interaction checkpoint, and may only edit the original ephemeral response. It cannot inspect Epiphany state, synthesize execution truth, expose signatures/private state, mutate the Bifrost outbox, or retry consequences.
|
||||
- Deployment follows one path: push the intended upstream commit, then let Idunn deploy VoidBot on Yggdrasil. Do not create, restore, or search for a local VoidBot deployment path.
|
||||
- The map is canonical and slow-changing.
|
||||
- Scratch is temporary and disposable.
|
||||
|
|
|
|||
Loading…
Reference in a new issue