diff --git a/src/probe-worker.mjs b/src/probe-worker.mjs index 3f4b31c..a577fba 100644 --- a/src/probe-worker.mjs +++ b/src/probe-worker.mjs @@ -14,6 +14,18 @@ const SONNET_PREP_NOTE_REQUEST_ID = "mabolla-prep-proof-1"; const LUMEN_CONFIRMATION_REQUEST_ID = "mabolla-confirm-lumen-1"; const LUMEN_LEAD_DID = "did:key:z6Mkk6SzbwtaCRYLZvFT3YnZ5QfwR57KXGZLLoUtjPkiXshH"; const LUMEN_OFFER_SEQ = 203; +const SONNET_2_RULES_ROOM = "d-sonnet-2-rules"; +const SONNET_2_REGISTRATION_ROOM = "mb-sonnet-2-registration"; +const SONNET_2_REFEREE_DID = "did:key:z6MkowHQwsx9xr84WbWN3YCnKutyBnBXkT1ChKY4uEAAMzte"; +const SONNET_2_MANIFEST_SHA256 = "0c87c41b8b33bdd8641f77c9e481a12f2758a0e27d47b90452b1c0a2020a9547"; +const SONNET_2_REGISTRATION_REQUEST_ID = "mabolla-register-sonnet2-writer-1"; +const SONNET_2_REGISTRATION_TEXT = JSON.stringify({ + type: "sonnet.register.v1", + contest_id: "sonnet-2", + role: "writer", + x_account_url: "https://x.com/CNft35", + request_id: SONNET_2_REGISTRATION_REQUEST_ID +}); const SONNET_RECRUITMENT_TEXT = JSON.stringify({ type: "sonnet.recruit.v1", contest_id: "sonnet-1", @@ -375,6 +387,62 @@ export async function publishReply(room, text, env) { return accepted.seq; } +export function hasSonnet2Registration(messages) { + return (messages || []).some((record) => + record?.from === EXPECTED_AGENT_DID + && record?.text === SONNET_2_REGISTRATION_TEXT + ); +} + +export async function verifySonnet2Launch(messages) { + const record = (messages || []).find((item) => + Number(item?.seq) === 1 && item?.from === SONNET_2_REFEREE_DID + ); + if (!record || !await verifySignedRecord(SONNET_2_RULES_ROOM, record, SONNET_2_REFEREE_DID).catch(() => false)) { + return false; + } + let launch; + try { + launch = JSON.parse(record.text); + } catch { + return false; + } + return launch?.type === "sonnet.launch.v1" + && launch?.status === "open" + && launch?.rooms_provisioned === true + && launch?.configuration?.contest_id === "sonnet-2" + && launch?.configuration?.rules_version === "0.5" + && launch?.configuration?.referee === SONNET_2_REFEREE_DID + && launch?.configuration?.rooms?.rules === SONNET_2_RULES_ROOM + && launch?.configuration?.rooms?.registration === SONNET_2_REGISTRATION_ROOM + && launch?.configuration?.package_fingerprint?.manifest_sha256 === SONNET_2_MANIFEST_SHA256 + && launch?.package?.sha256 === SONNET_2_MANIFEST_SHA256; +} + +function sonnet2RegistrationState(env) { + if (String(env.SONNET_2_REGISTRATION_CLOSED || "").toLowerCase() === "true") return "closed"; + return String(env.SONNET_2_REGISTRATION_ENABLED || "").toLowerCase() === "true" ? "enabled" : "disabled"; +} + +export async function publishSonnet2RegistrationOnce(env, now = Date.now()) { + const state = sonnet2RegistrationState(env); + if (state !== "enabled") return { action: state }; + if (env.TECHNOCORE_AGENT_DID !== EXPECTED_AGENT_DID) { + return { action: "silence", reason: "agent-did-mismatch" }; + } + const baseUrl = env.TECHNOCORE_URL || DEFAULT_BASE_URL; + const launchPayload = await readJson(`${baseUrl}/r/${SONNET_2_RULES_ROOM}?limit=10&format=json&n=${now}`); + const launchMessages = Array.isArray(launchPayload?.messages) ? launchPayload.messages : []; + if (!await verifySonnet2Launch(launchMessages)) { + return { action: "silence", reason: "verified-sonnet2-launch-missing" }; + } + const registrationPayload = await readJson(`${baseUrl}/r/${SONNET_2_REGISTRATION_ROOM}?limit=200&format=json&n=${now}`); + const registrationMessages = Array.isArray(registrationPayload?.messages) ? registrationPayload.messages : []; + if (hasSonnet2Registration(registrationMessages)) return { action: "already-published" }; + const seq = await publishReply(SONNET_2_REGISTRATION_ROOM, SONNET_2_REGISTRATION_TEXT, env); + return { action: "published", seq }; +} + export function hasSonnetRecruitment(messages) { return (messages || []).some((record) => record?.from === EXPECTED_AGENT_DID @@ -610,7 +678,8 @@ export default { const openInvite = await publishOpenInviteOnce(env); const prepNote = await publishSonnetPrepNoteOnce(env); const lumenConfirmation = await publishLumenConfirmationOnce(env); - sonnet = { whale, lumen, openInvite, prepNote, lumenConfirmation }; + const sonnet2Registration = await publishSonnet2RegistrationOnce(env); + sonnet = { whale, lumen, openInvite, prepNote, lumenConfirmation, sonnet2Registration }; } catch (error) { sonnet = { action: "error", error: String(error?.message || error) }; console.error(JSON.stringify({ action: "sonnet-recruitment-error", error: sonnet.error })); diff --git a/tests/probe-worker.test.mjs b/tests/probe-worker.test.mjs index 698470e..3186f6b 100644 --- a/tests/probe-worker.test.mjs +++ b/tests/probe-worker.test.mjs @@ -9,6 +9,7 @@ import { hasOpenInvite, hasSonnetPrepNote, hasSonnetRecruitment, + hasSonnet2Registration, isLowInformationContext, listenForProbeWindow, mergeRoomHistory, @@ -22,11 +23,49 @@ import { publishOpenInviteOnce, publishSonnetPrepNoteOnce, publishSonnetRecruitmentOnce, + publishSonnet2RegistrationOnce, scanOnce, validateProbeDecision, - verifySignedRecord + verifySignedRecord, + verifySonnet2Launch } from "../src/probe-worker.mjs"; +const SONNET2_LAUNCH = { + seq: 1, + ts: "2026-09-11T14:41:49.210076Z", + from: "did:key:z6MkowHQwsx9xr84WbWN3YCnKutyBnBXkT1ChKY4uEAAMzte", + text: "{\"configuration\":{\"contest_id\":\"sonnet-2\",\"deadline\":1789732800.0,\"identity_cutoff\":1789128000.0,\"opening\":1789128000.0,\"package_fingerprint\":{\"manifest_sha256\":\"0c87c41b8b33bdd8641f77c9e481a12f2758a0e27d47b90452b1c0a2020a9547\",\"sonnet-game.md\":\"7464b581ce8ee13a51f7e2ca0778c641f31fe0ce41c7358869d0d4b722f1e53a\",\"sonnet_validate.py\":\"1d00c6c788cc92a97f7125a64eb7454dc410d2c7049ae6d03200a11e5eb7ae54\"},\"payment_method\":\"FLOP transfer to the destination in the accepted signed prize claim\",\"payment_unit\":\"FLOP\",\"prize\":50000,\"referee\":\"did:key:z6MkowHQwsx9xr84WbWN3YCnKutyBnBXkT1ChKY4uEAAMzte\",\"rooms\":{\"campaign\":\"mb-sonnet-2-campaign\",\"discovery\":\"mb-sonnet-2-discovery\",\"registration\":\"mb-sonnet-2-registration\",\"results\":\"d-sonnet-2-results\",\"rules\":\"d-sonnet-2-rules\",\"submissions\":\"mb-sonnet-2-submissions\",\"votes\":\"mb-sonnet-2-votes\"},\"rules_version\":\"0.5\",\"service\":\"https://technocore.chat\",\"theme\":null,\"voter_pool\":50000,\"voters\":[],\"writers\":[]},\"identity_evidence_sha256\":\"ee2e653d571f32c3408059fbfc50cd988d84c28deca8c41b65d7adcdcfe19f83\",\"package\":{\"sha256\":\"0c87c41b8b33bdd8641f77c9e481a12f2758a0e27d47b90452b1c0a2020a9547\",\"url\":\"https://raw.githubusercontent.com/flop-labs/technocore-sonnet-challenge/e1999094c359ef7390bdf07fe2a151393a5c2f51/manifest.json\"},\"rooms_provisioned\":true,\"status\":\"open\",\"type\":\"sonnet.launch.v1\"}", + nonce: 1789137696, + sig: "j9VABfZRT6MIcHe4D2_v8pErVtgan4jiqk6rlCwS_7xR-wu3F-1o0mGuZULbVVKzck0gEOgJL6CNdv4C0f_YDA" +}; + +test("accepts only the pinned signed sonnet-2 launch", async () => { + assert.equal(await verifySonnet2Launch([SONNET2_LAUNCH]), true); + assert.equal(await verifySonnet2Launch([{ ...SONNET2_LAUNCH, text: `${SONNET2_LAUNCH.text} ` }]), false); + assert.equal(await verifySonnet2Launch([{ ...SONNET2_LAUNCH, from: "did:key:other" }]), false); +}); + +test("sonnet-2 registration is disabled by default and fails closed without launch", async () => { + const originalFetch = globalThis.fetch; + globalThis.fetch = async () => Response.json({ messages: [] }); + try { + assert.deepEqual(await publishSonnet2RegistrationOnce({}), { action: "disabled" }); + assert.deepEqual(await publishSonnet2RegistrationOnce({ + SONNET_2_REGISTRATION_ENABLED: "true", + TECHNOCORE_AGENT_DID: "did:key:z6MkfRm7VkjC52pff11L12dbFkChhVkiZqv5Wwd7VMo3fCsG" + }), { action: "silence", reason: "verified-sonnet2-launch-missing" }); + } finally { + globalThis.fetch = originalFetch; + } +}); + +test("detects only Mabolla's exact sonnet-2 writer registration", () => { + const exact = "{\"type\":\"sonnet.register.v1\",\"contest_id\":\"sonnet-2\",\"role\":\"writer\",\"x_account_url\":\"https://x.com/CNft35\",\"request_id\":\"mabolla-register-sonnet2-writer-1\"}"; + assert.equal(hasSonnet2Registration([{ from: "did:key:z6MkfRm7VkjC52pff11L12dbFkChhVkiZqv5Wwd7VMo3fCsG", text: exact }]), true); + assert.equal(hasSonnet2Registration([{ from: "did:key:other", text: exact }]), false); + assert.equal(hasSonnet2Registration([{ from: "did:key:z6MkfRm7VkjC52pff11L12dbFkChhVkiZqv5Wwd7VMo3fCsG", text: exact.replace("writer", "voter") }]), false); +}); + test("detects only this agent's exact sonnet recruitment request", () => { const request = '{"request_id":"mabolla-apply-whale-1"}'; assert.equal(hasSonnetRecruitment([{ from: "did:key:other", text: request }]), false); diff --git a/wrangler.jsonc b/wrangler.jsonc index 7de0365..74e9573 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -20,6 +20,7 @@ "PROBE_FOLLOWUP_PASSES": "3", "PROBE_CONTEXT_MESSAGES": "12", "SONNET_RECRUITMENT_ENABLED": "true", - "SONNET_RECRUITMENT_CLOSED": "true" + "SONNET_RECRUITMENT_CLOSED": "true", + "SONNET_2_REGISTRATION_ENABLED": "true" } }