Ctrl K
ComunidadGuías…

scripts/verify-snapshot-durability.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node
/**
 * Helper invoked by verify-durability.mjs to prove live Agent config survives
 * a process restart. It remains directly runnable for focused diagnosis.
 *
 * Flow (offline, fake Slack/provider):
 *   1. Boot the built app on DB_A + STATE_A.
 *   2. Patch the assigned profile to instructions A through /admin/api/*.
 *   3. First turn in a Slack thread uses instructions A.
 *   4. Patch the Agent to instructions B through /admin/api/*.
 *   5. SIGKILL, restart on the same DB_A + STATE_A.
 *   6. Follow up in the same thread. The provider request and Admin effective
 *      config must both carry B because Agent edits apply on the next message.
 */
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

import {
  assertNodeVersion,
  buildNodeServer,
  delay,
  getFreePort,
  loadFake,
  postSignedEvent,
  seedOfflineDemoChannelConfig,
  seedOfflineSlackAuthority,
  spawnServer,
  stopChild,
  waitForFinals,
  waitForReady,
} from './lib/offline-harness.mjs';

const WORKSPACE = 'TDEMO123';
const APP_ID = 'ADEMO123';
const BOT_USER_ID = 'UBOT1234';
const ACTOR_USER_ID = 'UALICE01';
const EXEC_CHANNEL = 'CEXEC123';
const ROOT_TS = '1782772000.000100';
const ALPHA = 'SNAPSHOT_SCRIPT_ALPHA: original instructions for this thread.';
const BETA = 'SNAPSHOT_SCRIPT_BETA: edited instructions for the live Agent.';

const results = [];
let personalToken = '';

function log(line) {
  console.log(line);
}

function record(name, passed, detail) {
  results.push({ name, passed });
  log(`${passed ? 'PASS' : 'FAIL'}  ${name}${detail ? ` - ${detail}` : ''}`);
}

function mention({ eventId, ts, threadTs, text }) {
  return {
    token: 'verification-token-not-a-secret',
    team_id: WORKSPACE,
    api_app_id: APP_ID,
    event_id: eventId,
    event_time: 1782772000,
    type: 'event_callback',
    event: {
      type: 'app_mention',
      user: ACTOR_USER_ID,
      text,
      ts,
      channel: EXEC_CHANNEL,
      event_ts: ts,
      ...(threadTs ? { thread_ts: threadTs } : {}),
    },
  };
}

async function startServer({ serverEntry, fakeUrl, dbPath, stateDbPath, keyringPath, netGuardLog, port = undefined }) {
  const resolvedPort = port ?? await getFreePort();
  const server = spawnServer({
    serverEntry,
    port: resolvedPort,
    fakeUrl,
    netGuardLog,
    env: {
      TAG_DB_PATH: dbPath,
      SLACK_STATE_DB_PATH: stateDbPath,
      CHICKPEA_CREDENTIAL_KEYRING_PATH: keyringPath,
      CHICKPEA_AUTH_SECRET: '9d'.repeat(32),
      SLACK_TAG_MODEL: 'local-stub/snapshot-durability',
    },
  });
  try {
    await waitForReady(server.child, server.eventsUrl, server.getOutput);
  } catch (error) {
    await stopChild(server.child);
    throw error;
  }
  return server;
}

async function adminRequest(baseUrl, path, init = {}) {
  const headers = new Headers(init.headers);
  headers.set('authorization', `Bearer ${personalToken}`);
  if (init.body !== undefined && !headers.has('content-type')) {
    headers.set('content-type', 'application/json');
  }
  const response = await fetch(`${baseUrl}${path}`, { ...init, headers });
  const text = await response.text();
  let body;
  try {
    body = text ? JSON.parse(text) : undefined;
  } catch {
    body = text;
  }
  return { status: response.status, body };
}

async function patchInstructions(baseUrl, instructions) {
  const current = await adminRequest(baseUrl, '/admin/api/agents/agent_default');
  if (current.status !== 200 || typeof current.body?.agent?.revision !== 'number') {
    return current;
  }
  return adminRequest(baseUrl, '/admin/api/agents/agent_default', {
    method: 'PATCH',
    body: JSON.stringify({
      expectedRevision: current.body.agent.revision,
      instructions,
    }),
  });
}

async function effectiveConfig(baseUrl) {
  return adminRequest(
    baseUrl,
    `/admin/api/effective-config?workspaceId=${WORKSPACE}&channelId=${EXEC_CHANNEL}`,
  );
}

async function waitForProviderCalls(backend, minCalls, timeoutMs = 15_000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    if (backend.providerCalls().length >= minCalls) {
      return backend.providerCalls();
    }
    await delay(200);
  }
  return backend.providerCalls();
}

function providerBodyText(call) {
  return JSON.stringify(call?.body ?? {});
}

const { FakeSlackBackend } = await loadFake();
const backend = new FakeSlackBackend({
  slack: {
    identity: { appId: APP_ID, teamId: WORKSPACE, botUserId: BOT_USER_ID },
    channels: [{ id: EXEC_CHANNEL, name: 'exec', isMember: true, teamId: WORKSPACE }],
    channelMembers: { [EXEC_CHANNEL]: [ACTOR_USER_ID, BOT_USER_ID] },
    workspaceUsers: [
      { id: ACTOR_USER_ID, teamId: WORKSPACE },
      { id: BOT_USER_ID, teamId: WORKSPACE, isBot: true, isAppUser: true },
    ],
  },
  provider: { mode: 'ok', replyText: 'snapshot durability ok' },
});

const dbDir = mkdtempSync(join(tmpdir(), 'flue-snapshot-db-'));
const dbPath = join(dbDir, 'flue.db');
const stateDbPath = join(dbDir, 'state.db');
const keyringPath = join(dbDir, 'credential-keyring.json');
const netGuardLog = join(mkdtempSync(join(tmpdir(), 'flue-snapshot-guard-')), 'external-hosts.log');
let firstProviderBody = '';
let secondProviderBody = '';
let effectiveBeforeRestart;
let effectiveAfterRestart;

try {
  const fake = await backend.listen();
  const serverEntry = await buildNodeServer();
  log(`built node server: ${serverEntry}`);
  log(`node ${assertNodeVersion()}  DB=${dbPath}  STATE=${stateDbPath}`);
  log(`fake backend listening at ${fake.url}`);
  await seedOfflineDemoChannelConfig(stateDbPath, { workspaceId: WORKSPACE, channelId: EXEC_CHANNEL });
  const initialPort = await getFreePort();
  personalToken = await seedOfflineSlackAuthority({
    stateDbPath,
    keyringPath,
    canonicalOrigin: `http://127.0.0.1:${initialPort}`,
    teamId: WORKSPACE,
    slackUserId: ACTOR_USER_ID,
    appId: APP_ID,
    botUserId: BOT_USER_ID,
  });

  let server = await startServer({
    serverEntry, fakeUrl: fake.url, dbPath, stateDbPath, keyringPath, netGuardLog, port: initialPort,
  });
  const patchA = await patchInstructions(server.baseUrl, ALPHA);
  record('admin patch writes initial instructions', patchA.status === 200, `status=${patchA.status}`);

  await postSignedEvent(
    server.eventsUrl,
    mention({
      eventId: 'Ev_SNAPSHOT_DUR_T1',
      ts: ROOT_TS,
      text: `<@${BOT_USER_ID}> start this snapshot durability thread`,
    }),
  );
  await waitForFinals(backend, 1, 15_000);
  const firstCalls = await waitForProviderCalls(backend, 1);
  firstProviderBody = providerBodyText(firstCalls.at(-1));
  const firstHasAlpha = firstProviderBody.includes(ALPHA);
  const firstHasBeta = firstProviderBody.includes(BETA);
  record(
    'pre-restart provider request carries the initial instructions',
    firstHasAlpha && !firstHasBeta,
    `alpha=${firstHasAlpha} beta=${firstHasBeta}`,
  );

  const patchB = await patchInstructions(server.baseUrl, BETA);
  effectiveBeforeRestart = await effectiveConfig(server.baseUrl);
  const effectiveHasBeta = JSON.stringify(effectiveBeforeRestart.body).includes(BETA);
  record(
    'admin edit changes effective config for future threads',
    patchB.status === 200 && effectiveBeforeRestart.status === 200 && effectiveHasBeta,
    `patchStatus=${patchB.status} effectiveStatus=${effectiveBeforeRestart.status} beta=${effectiveHasBeta}`,
  );

  await stopChild(server.child);
  log('server SIGKILLed after the first turn and Agent edit');

  backend.reset();
  server = await startServer({ serverEntry, fakeUrl: fake.url, dbPath, stateDbPath, keyringPath, netGuardLog });
  effectiveAfterRestart = await effectiveConfig(server.baseUrl);
  await postSignedEvent(
    server.eventsUrl,
    mention({
      eventId: 'Ev_SNAPSHOT_DUR_T2',
      ts: '1782772001.000100',
      threadTs: ROOT_TS,
      text: `<@${BOT_USER_ID}> continue after restart`,
    }),
  );
  await waitForFinals(backend, 1, 15_000);
  const secondCalls = await waitForProviderCalls(backend, 1);
  secondProviderBody = providerBodyText(secondCalls.at(-1));
  const secondHasAlpha = secondProviderBody.includes(ALPHA);
  const secondHasBeta = secondProviderBody.includes(BETA);
  const restartedEffectiveHasBeta = JSON.stringify(effectiveAfterRestart.body).includes(BETA);
  await stopChild(server.child);

  record(
    'post-restart same-thread provider request carries current Agent instructions',
    !secondHasAlpha && secondHasBeta,
    `alpha=${secondHasAlpha} beta=${secondHasBeta}`,
  );
  record(
    'post-restart admin effective config still carries edited instructions',
    effectiveAfterRestart.status === 200 && restartedEffectiveHasBeta,
    `status=${effectiveAfterRestart.status} beta=${restartedEffectiveHasBeta}`,
  );
  log(
    `matching pre/post-restart instruction marker: ` +
      `${firstHasAlpha ? 'ALPHA' : 'missing'} -> ${secondHasBeta ? 'BETA' : 'missing'}`,
  );

  const attempted = existsSync(netGuardLog) ? readFileSync(netGuardLog, 'utf8').trim() : '';
  record('NET_GUARD_LOG empty -> zero external traffic', attempted === '', attempted || 'none');
} catch (error) {
  record('live Agent durability harness', false, error instanceof Error ? error.stack : String(error));
} finally {
  await backend.close();
  rmSync(dbDir, { recursive: true, force: true });
}

const failed = results.filter((result) => !result.passed);
log(`\n${results.length - failed.length}/${results.length} checks passed`);
process.exit(failed.length === 0 ? 0 : 1);